From ae0f3c74a0a986f0b5f1c0b546d6fb88e617aa76 Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Wed, 16 Jan 2019 13:47:23 +0100 Subject: [PATCH] Support for credential_spec Signed-off-by: Djordje Lukic --- compose/cli/main.py | 4 ++-- compose/config/config.py | 23 ++++++++++++++++++++++- compose/config/validation.py | 12 ++++++++++++ contrib/completion/zsh/_docker-compose | 2 +- tests/unit/config/config_test.py | 6 +++++- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/compose/cli/main.py b/compose/cli/main.py index 950e5055d..789601792 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -206,8 +206,8 @@ class TopLevelCommand(object): name specified in the client certificate --project-directory PATH Specify an alternate working directory (default: the path of the Compose file) - --compatibility If set, Compose will attempt to convert deploy - keys in v3 files to their non-Swarm equivalent + --compatibility If set, Compose will attempt to convert keys + in v3 files to their non-Swarm equivalent Commands: build Build or rebuild services diff --git a/compose/config/config.py b/compose/config/config.py index 3df211f73..e2ed29a47 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -51,6 +51,7 @@ from .validation import match_named_volumes from .validation import validate_against_config_schema from .validation import validate_config_section from .validation import validate_cpu +from .validation import validate_credential_spec from .validation import validate_depends_on from .validation import validate_extends_file_path from .validation import validate_healthcheck @@ -369,7 +370,6 @@ def check_swarm_only_config(service_dicts, compatibility=False): ) if not compatibility: check_swarm_only_key(service_dicts, 'deploy') - check_swarm_only_key(service_dicts, 'credential_spec') check_swarm_only_key(service_dicts, 'configs') @@ -706,6 +706,7 @@ def validate_service(service_config, service_names, config_file): validate_depends_on(service_config, service_names) validate_links(service_config, service_names) validate_healthcheck(service_config) + validate_credential_spec(service_config) if not service_dict.get('image') and has_uppercase(service_name): raise ConfigurationError( @@ -894,6 +895,7 @@ def finalize_service(service_config, service_names, version, environment, compat normalize_build(service_dict, service_config.working_dir, environment) if compatibility: + service_dict = translate_credential_spec_to_security_opt(service_dict) service_dict, ignored_keys = translate_deploy_keys_to_container_config( service_dict ) @@ -930,6 +932,25 @@ def convert_restart_policy(name): raise ConfigurationError('Invalid restart policy "{}"'.format(name)) +def convert_credential_spec_to_security_opt(credential_spec): + if 'file' in credential_spec: + return 'file://{file}'.format(file=credential_spec['file']) + return 'registry://{registry}'.format(registry=credential_spec['registry']) + + +def translate_credential_spec_to_security_opt(service_dict): + result = [] + + if 'credential_spec' in service_dict: + spec = convert_credential_spec_to_security_opt(service_dict['credential_spec']) + result.append('credentialspec={spec}'.format(spec=spec)) + + if result: + service_dict['security_opt'] = result + + return service_dict + + def translate_deploy_keys_to_container_config(service_dict): if 'credential_spec' in service_dict: del service_dict['credential_spec'] diff --git a/compose/config/validation.py b/compose/config/validation.py index 039569551..1cceb71f0 100644 --- a/compose/config/validation.py +++ b/compose/config/validation.py @@ -240,6 +240,18 @@ def validate_depends_on(service_config, service_names): ) +def validate_credential_spec(service_config): + credential_spec = service_config.config.get('credential_spec') + if not credential_spec: + return + + if 'registry' not in credential_spec and 'file' not in credential_spec: + raise ConfigurationError( + "Service '{s.name}' is missing 'credential_spec.file' or " + "credential_spec.registry'".format(s=service_config) + ) + + def get_unsupported_config_msg(path, error_key): msg = "Unsupported config option for {}: '{}'".format(path_string(path), error_key) if error_key in DOCKER_CONFIG_HINTS: diff --git a/contrib/completion/zsh/_docker-compose b/contrib/completion/zsh/_docker-compose index e55c91964..5fd418a69 100755 --- a/contrib/completion/zsh/_docker-compose +++ b/contrib/completion/zsh/_docker-compose @@ -339,7 +339,7 @@ _docker-compose() { '(- :)'{-h,--help}'[Get help]' \ '*'{-f,--file}"[${file_description}]:file:_files -g '*.yml'" \ '(-p --project-name)'{-p,--project-name}'[Specify an alternate project name (default: directory name)]:project name:' \ - "--compatibility[If set, Compose will attempt to convert deploy keys in v3 files to their non-Swarm equivalent]" \ + "--compatibility[If set, Compose will attempt to convert keys in v3 files to their non-Swarm equivalent]" \ '(- :)'{-v,--version}'[Print version and exit]' \ '--verbose[Show more output]' \ '--log-level=[Set log level]:level:(DEBUG INFO WARNING ERROR CRITICAL)' \ diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 8baf8e4ee..c2e6b7b07 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -3593,6 +3593,9 @@ class InterpolationTest(unittest.TestCase): 'reservations': {'memory': '100M'}, }, }, + 'credential_spec': { + 'file': 'spec.json' + }, }, }, }) @@ -3610,7 +3613,8 @@ class InterpolationTest(unittest.TestCase): 'mem_limit': '300M', 'mem_reservation': '100M', 'cpus': 0.7, - 'name': 'foo' + 'name': 'foo', + 'security_opt': ['credentialspec=file://spec.json'], } @mock.patch.dict(os.environ)