diff --git a/compose/config/config.py b/compose/config/config.py index 2a81b93da..fa373bb10 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -108,6 +108,7 @@ DOCKER_CONFIG_KEYS = [ ALLOWED_KEYS = DOCKER_CONFIG_KEYS + [ 'build', 'container_name', + 'credential_spec', 'dockerfile', 'log_driver', 'log_opt', @@ -320,6 +321,27 @@ def find_candidates_in_parent_dirs(filenames, path): return (candidates, path) +def check_swarm_only_config(service_dicts): + warning_template = ( + "Some services ({services}) use the '{key}' key, which will be ignored. " + "Compose does not support '{key}' configuration - use " + "`docker stack deploy` to deploy to a swarm." + ) + + def check_swarm_only_key(service_dicts, key): + services = [s for s in service_dicts if s.get(key)] + if services: + log.warn( + warning_template.format( + services=", ".join(sorted(s['name'] for s in services)), + key=key + ) + ) + + check_swarm_only_key(service_dicts, 'deploy') + check_swarm_only_key(service_dicts, 'credential_spec') + + def load(config_details): """Load the configuration from a working directory and a list of configuration files. Files are loaded in order, and merged on top @@ -349,13 +371,7 @@ def load(config_details): for service_dict in service_dicts: match_named_volumes(service_dict, volumes) - services_using_deploy = [s for s in service_dicts if s.get('deploy')] - if services_using_deploy: - log.warn( - "Some services ({}) use the 'deploy' key, which will be ignored. " - "Compose does not support deploy configuration - use " - "`docker stack deploy` to deploy to a swarm." - .format(", ".join(sorted(s['name'] for s in services_using_deploy)))) + check_swarm_only_config(service_dicts) return Config(main_file.version, service_dicts, volumes, networks, secrets) @@ -884,7 +900,7 @@ def merge_service_dicts(base, override, version): md.merge_mapping('environment', parse_environment) md.merge_mapping('labels', parse_labels) - md.merge_mapping('ulimits', parse_ulimits) + md.merge_mapping('ulimits', parse_flat_dict) md.merge_mapping('networks', parse_networks) md.merge_mapping('sysctls', parse_sysctls) md.merge_mapping('depends_on', parse_depends_on) @@ -1018,12 +1034,14 @@ parse_depends_on = functools.partial( parse_deploy = functools.partial(parse_dict_or_list, split_kv, 'deploy') -def parse_ulimits(ulimits): - if not ulimits: +def parse_flat_dict(d): + if not d: return {} - if isinstance(ulimits, dict): - return dict(ulimits) + if isinstance(d, dict): + return dict(d) + + raise ConfigurationError("Invalid type: expected mapping") def resolve_env_var(key, val, environment): diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 357244c2c..9dbce18d2 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -2033,6 +2033,23 @@ class ConfigTest(unittest.TestCase): } } + def test_merge_credential_spec(self): + base = { + 'image': 'bb', + 'credential_spec': { + 'file': '/hello-world', + } + } + + override = { + 'credential_spec': { + 'registry': 'revolution.com', + } + } + + actual = config.merge_service_dicts(base, override, V3_3) + assert actual['credential_spec'] == override['credential_spec'] + def test_external_volume_config(self): config_details = build_config_details({ 'version': '2',