Limit hostnames to 31 characters (#2531)

This commit is contained in:
Oxan van Leeuwen 2021-10-22 12:09:47 +02:00 committed by GitHub
parent 9220d9fc52
commit f7b3f52731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 73 deletions

View File

@ -903,21 +903,9 @@ def validate_bytes(value):
def hostname(value):
value = string(value)
warned_underscore = False
if len(value) > 63:
raise Invalid("Hostnames can only be 63 characters long")
for c in value:
if not (c.isalnum() or c in "-_"):
raise Invalid("Hostname can only have alphanumeric characters and -")
if c in "_" and not warned_underscore:
_LOGGER.warning(
"'%s': Using the '_' (underscore) character in the hostname is discouraged "
"as it can cause problems with some DHCP and local name services. "
"For more information, see https://esphome.io/guides/faq.html#why-shouldn-t-i-use-underscores-in-my-device-name",
value,
)
warned_underscore = True
return value
if re.match(r"^[a-z0-9-]{1,63}$", value, re.IGNORECASE) is not None:
return value
raise Invalid(f"Invalid hostname: {value}")
def domain(value):

View File

@ -55,6 +55,24 @@ CONF_NAME_ADD_MAC_SUFFIX = "name_add_mac_suffix"
VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"}
def validate_hostname(config):
max_length = 31
if config[CONF_NAME_ADD_MAC_SUFFIX]:
max_length -= 7 # "-AABBCC" is appended when add mac suffix option is used
if len(config[CONF_NAME]) > max_length:
raise cv.Invalid(
f"Hostnames can only be {max_length} characters long", path=[CONF_NAME]
)
if "_" in config[CONF_NAME]:
_LOGGER.warning(
"'%s': Using the '_' (underscore) character in the hostname is discouraged "
"as it can cause problems with some DHCP and local name services. "
"For more information, see https://esphome.io/guides/faq.html#why-shouldn-t-i-use-underscores-in-my-device-name",
config[CONF_NAME],
)
return config
def valid_include(value):
try:
return cv.directory(value)
@ -79,42 +97,47 @@ def valid_project_name(value: str):
CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash"
CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_NAME): cv.hostname,
cv.Optional(CONF_COMMENT): cv.string,
cv.Required(CONF_BUILD_PATH): cv.string,
cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema(
{
cv.string_strict: cv.Any([cv.string], cv.string),
}
),
cv.Optional(CONF_ON_BOOT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger),
cv.Optional(CONF_PRIORITY, default=600.0): cv.float_,
}
),
cv.Optional(CONF_ON_SHUTDOWN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ShutdownTrigger),
}
),
cv.Optional(CONF_ON_LOOP): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoopTrigger),
}
),
cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include),
cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict),
cv.Optional(CONF_NAME_ADD_MAC_SUFFIX, default=False): cv.boolean,
cv.Optional(CONF_PROJECT): cv.Schema(
{
cv.Required(CONF_NAME): cv.All(cv.string_strict, valid_project_name),
cv.Required(CONF_VERSION): cv.string_strict,
}
),
}
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.Required(CONF_NAME): cv.valid_name,
cv.Optional(CONF_COMMENT): cv.string,
cv.Required(CONF_BUILD_PATH): cv.string,
cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema(
{
cv.string_strict: cv.Any([cv.string], cv.string),
}
),
cv.Optional(CONF_ON_BOOT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger),
cv.Optional(CONF_PRIORITY, default=600.0): cv.float_,
}
),
cv.Optional(CONF_ON_SHUTDOWN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ShutdownTrigger),
}
),
cv.Optional(CONF_ON_LOOP): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoopTrigger),
}
),
cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include),
cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict),
cv.Optional(CONF_NAME_ADD_MAC_SUFFIX, default=False): cv.boolean,
cv.Optional(CONF_PROJECT): cv.Schema(
{
cv.Required(CONF_NAME): cv.All(
cv.string_strict, valid_project_name
),
cv.Required(CONF_VERSION): cv.string_strict,
}
),
}
),
validate_hostname,
)
PRELOAD_CONFIG_SCHEMA = cv.Schema(

View File

@ -40,28 +40,6 @@ def test_valid_name__invalid(value):
config_validation.valid_name(value)
@pytest.mark.parametrize("value", ("foo", "bar123", "foo-bar"))
def test_hostname__valid(value):
actual = config_validation.hostname(value)
assert actual == value
@pytest.mark.parametrize("value", ("foo bar", "foobar ", "foo#bar"))
def test_hostname__invalid(value):
with pytest.raises(Invalid):
config_validation.hostname(value)
def test_hostname__warning(caplog):
actual = config_validation.hostname("foo_bar")
assert actual == "foo_bar"
assert (
"Using the '_' (underscore) character in the hostname is discouraged"
in caplog.text
)
@given(one_of(integers(), text()))
def test_string__valid(value):
actual = config_validation.string(value)