Make config validation error messages more consistent.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2016-02-19 12:35:05 -05:00
parent 4b2a666231
commit 0d218c34c7
4 changed files with 30 additions and 28 deletions

View File

@ -111,30 +111,29 @@ def validate_config_section(filename, config, section):
""" """
if not isinstance(config, dict): if not isinstance(config, dict):
raise ConfigurationError( raise ConfigurationError(
"In file '{filename}' {section} must be a mapping, not " "In file '{filename}', {section} must be a mapping, not "
"'{type}'.".format( "{type}.".format(
filename=filename, filename=filename,
section=section, section=section,
type=python_type_to_yaml_type(config))) type=anglicize_json_type(python_type_to_yaml_type(config))))
for key, value in config.items(): for key, value in config.items():
if not isinstance(key, six.string_types): if not isinstance(key, six.string_types):
raise ConfigurationError( raise ConfigurationError(
"In file '{filename}' {section} name {name} needs to be a " "In file '{filename}', the {section} name {name} must be a "
"string, eg '{name}'".format( "quoted string, i.e. '{name}'.".format(
filename=filename, filename=filename,
section=section, section=section,
name=key)) name=key))
if not isinstance(value, (dict, type(None))): if not isinstance(value, (dict, type(None))):
raise ConfigurationError( raise ConfigurationError(
"In file '{filename}' {section} '{name}' is the wrong type. " "In file '{filename}', {section} '{name}' must be a mapping not "
"It should be a mapping of configuration options, it is a " "{type}.".format(
"'{type}'.".format(
filename=filename, filename=filename,
section=section, section=section,
name=key, name=key,
type=python_type_to_yaml_type(value))) type=anglicize_json_type(python_type_to_yaml_type(value))))
def validate_top_level_object(config_file): def validate_top_level_object(config_file):
@ -203,10 +202,10 @@ def get_unsupported_config_msg(path, error_key):
return msg return msg
def anglicize_validator(validator): def anglicize_json_type(json_type):
if validator in ["array", "object"]: if json_type.startswith(('a', 'e', 'i', 'o', 'u')):
return 'an ' + validator return 'an ' + json_type
return 'a ' + validator return 'a ' + json_type
def is_service_dict_schema(schema_id): def is_service_dict_schema(schema_id):
@ -314,14 +313,14 @@ def _parse_valid_types_from_validator(validator):
a valid type. Parse the valid types and prefix with the correct article. a valid type. Parse the valid types and prefix with the correct article.
""" """
if not isinstance(validator, list): if not isinstance(validator, list):
return anglicize_validator(validator) return anglicize_json_type(validator)
if len(validator) == 1: if len(validator) == 1:
return anglicize_validator(validator[0]) return anglicize_json_type(validator[0])
return "{}, or {}".format( return "{}, or {}".format(
", ".join([anglicize_validator(validator[0])] + validator[1:-1]), ", ".join([anglicize_json_type(validator[0])] + validator[1:-1]),
anglicize_validator(validator[-1])) anglicize_json_type(validator[-1]))
def _parse_oneof_validator(error): def _parse_oneof_validator(error):

View File

@ -159,7 +159,7 @@ class CLITestCase(DockerClientTestCase):
'-f', 'tests/fixtures/invalid-composefile/invalid.yml', '-f', 'tests/fixtures/invalid-composefile/invalid.yml',
'config', '-q' 'config', '-q'
], returncode=1) ], returncode=1)
assert "'notaservice' is the wrong type" in result.stderr assert "'notaservice' must be a mapping" in result.stderr
# TODO: this shouldn't be v2-dependent # TODO: this shouldn't be v2-dependent
@v2_only() @v2_only()

View File

@ -268,7 +268,7 @@ class ConfigTest(unittest.TestCase):
}) })
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
config.load(config_details) config.load(config_details)
assert "volume must be a mapping, not 'array'" in exc.exconly() assert "volume must be a mapping, not an array" in exc.exconly()
def test_networks_invalid_type_list(self): def test_networks_invalid_type_list(self):
config_details = build_config_details({ config_details = build_config_details({
@ -280,7 +280,7 @@ class ConfigTest(unittest.TestCase):
}) })
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
config.load(config_details) config.load(config_details)
assert "network must be a mapping, not 'array'" in exc.exconly() assert "network must be a mapping, not an array" in exc.exconly()
def test_load_service_with_name_version(self): def test_load_service_with_name_version(self):
with mock.patch('compose.config.config.log') as mock_logging: with mock.patch('compose.config.config.log') as mock_logging:
@ -392,8 +392,7 @@ class ConfigTest(unittest.TestCase):
'filename.yml') 'filename.yml')
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
config.load(config_details) config.load(config_details)
error_msg = "service 'web' is the wrong type" assert "service 'web' must be a mapping not a string." in exc.exconly()
assert error_msg in exc.exconly()
def test_config_integer_service_name_raise_validation_error(self): def test_config_integer_service_name_raise_validation_error(self):
with pytest.raises(ConfigurationError) as excinfo: with pytest.raises(ConfigurationError) as excinfo:
@ -405,8 +404,10 @@ class ConfigTest(unittest.TestCase):
) )
) )
assert "In file 'filename.yml' service name 1 needs to be a string, eg '1'" \ assert (
in excinfo.exconly() "In file 'filename.yml', the service name 1 must be a quoted string, i.e. '1'" in
excinfo.exconly()
)
def test_config_integer_service_name_raise_validation_error_v2(self): def test_config_integer_service_name_raise_validation_error_v2(self):
with pytest.raises(ConfigurationError) as excinfo: with pytest.raises(ConfigurationError) as excinfo:
@ -421,8 +422,10 @@ class ConfigTest(unittest.TestCase):
) )
) )
assert "In file 'filename.yml' service name 1 needs to be a string, eg '1'" \ assert (
in excinfo.exconly() "In file 'filename.yml', the service name 1 must be a quoted string, i.e. '1'." in
excinfo.exconly()
)
def test_load_with_multiple_files_v1(self): def test_load_with_multiple_files_v1(self):
base_file = config.ConfigFile( base_file = config.ConfigFile(
@ -556,7 +559,7 @@ class ConfigTest(unittest.TestCase):
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
config.load(details) config.load(details)
assert "service 'bogus' is the wrong type" in exc.exconly() assert "service 'bogus' must be a mapping not a string." in exc.exconly()
assert "In file 'override.yaml'" in exc.exconly() assert "In file 'override.yaml'" in exc.exconly()
def test_load_sorts_in_dependency_order(self): def test_load_sorts_in_dependency_order(self):