From 2acf286ed611651fda1a87360d04de2ddca6e649 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Mon, 13 Mar 2017 15:57:13 -0400 Subject: [PATCH] Support V3.2 Signed-off-by: Daniel Nephin --- compose/config/config.py | 37 +++++++------------ compose/config/serialize.py | 8 ++-- compose/config/validation.py | 18 ++++++--- compose/const.py | 4 ++ .../ports-composefile/expanded-notation.yml | 2 +- tests/integration/project_test.py | 6 +-- tests/integration/testcases.py | 8 ++-- tests/unit/config/config_test.py | 10 ++--- tests/unit/config/types_test.py | 4 +- 9 files changed, 49 insertions(+), 48 deletions(-) diff --git a/compose/config/config.py b/compose/config/config.py index 3d7cbe8d6..5a0a3c847 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -13,11 +13,8 @@ import yaml from cached_property import cached_property from . import types +from .. import const from ..const import COMPOSEFILE_V1 as V1 -from ..const import COMPOSEFILE_V2_0 as V2_0 -from ..const import COMPOSEFILE_V2_1 as V2_1 -from ..const import COMPOSEFILE_V3_0 as V3_0 -from ..const import COMPOSEFILE_V3_1 as V3_1 from ..utils import build_string_dict from ..utils import parse_nanoseconds_int from ..utils import splitdrive @@ -185,10 +182,10 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')): .format(self.filename, VERSION_EXPLANATION)) if version == '2': - version = V2_0 + version = const.COMPOSEFILE_V2_0 if version == '3': - version = V3_0 + version = const.COMPOSEFILE_V3_0 return version @@ -205,7 +202,7 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')): return {} if self.version == V1 else self.config.get('networks', {}) def get_secrets(self): - return {} if self.version < V3_1 else self.config.get('secrets', {}) + return {} if self.version < const.COMPOSEFILE_V3_1 else self.config.get('secrets', {}) class Config(namedtuple('_Config', 'version services volumes networks secrets')): @@ -427,7 +424,7 @@ def load_services(config_details, config_file): service_dict = process_service(resolver.run()) service_config = service_config._replace(config=service_dict) - validate_service(service_config, service_names, config_file.version) + validate_service(service_config, service_names, config_file) service_dict = finalize_service( service_config, service_names, @@ -480,7 +477,7 @@ def process_config_file(config_file, environment, service_name=None): 'service', environment) - if config_file.version in (V2_0, V2_1, V3_0, V3_1): + if config_file.version != V1: processed_config = dict(config_file.config) processed_config['services'] = services processed_config['volumes'] = interpolate_config_section( @@ -493,19 +490,13 @@ def process_config_file(config_file, environment, service_name=None): config_file.get_networks(), 'network', environment) - if config_file.version in (V3_1,): - processed_config['secrets'] = interpolate_config_section( - config_file, - config_file.get_secrets(), - 'secrets', - environment - ) - elif config_file.version == V1: - processed_config = services + processed_config['secrets'] = interpolate_config_section( + config_file, + config_file.get_secrets(), + 'secrets', + environment) else: - raise ConfigurationError( - 'Version in "{}" is unsupported. {}' - .format(config_file.filename, VERSION_EXPLANATION)) + processed_config = services config_file = config_file._replace(config=processed_config) validate_against_config_schema(config_file) @@ -642,9 +633,9 @@ def validate_extended_service_dict(service_dict, filename, service): "%s services with 'depends_on' cannot be extended" % error_prefix) -def validate_service(service_config, service_names, version): +def validate_service(service_config, service_names, config_file): service_dict, service_name = service_config.config, service_config.name - validate_service_constraints(service_dict, service_name, version) + validate_service_constraints(service_dict, service_name, config_file) validate_paths(service_dict) validate_ulimits(service_config) diff --git a/compose/config/serialize.py b/compose/config/serialize.py index 6e2ad5906..46e1d9f44 100644 --- a/compose/config/serialize.py +++ b/compose/config/serialize.py @@ -4,10 +4,10 @@ from __future__ import unicode_literals import six import yaml +from compose import const from compose.config import types -from compose.config.config import V1 -from compose.config.config import V2_1 -from compose.config.config import V3_1 +from compose.const import COMPOSEFILE_V1 as V1 +from compose.const import COMPOSEFILE_V2_1 as V2_1 def serialize_config_type(dumper, data): @@ -103,7 +103,7 @@ def denormalize_service_dict(service_dict, version): service_dict['healthcheck']['timeout'] ) - if 'ports' in service_dict and version != V3_1: + if 'ports' in service_dict and version < const.COMPOSEFILE_V3_2: service_dict['ports'] = map( lambda p: p.legacy_repr() if isinstance(p, types.ServicePort) else p, service_dict['ports'] diff --git a/compose/config/validation.py b/compose/config/validation.py index d4d29565f..1df6dd6b7 100644 --- a/compose/config/validation.py +++ b/compose/config/validation.py @@ -365,7 +365,7 @@ def process_config_schema_errors(error): def validate_against_config_schema(config_file): - schema = load_jsonschema(config_file.version) + schema = load_jsonschema(config_file) format_checker = FormatChecker(["ports", "expose"]) validator = Draft4Validator( schema, @@ -377,11 +377,12 @@ def validate_against_config_schema(config_file): config_file.filename) -def validate_service_constraints(config, service_name, version): +def validate_service_constraints(config, service_name, config_file): def handler(errors): - return process_service_constraint_errors(errors, service_name, version) + return process_service_constraint_errors( + errors, service_name, config_file.version) - schema = load_jsonschema(version) + schema = load_jsonschema(config_file) validator = Draft4Validator(schema['definitions']['constraints']['service']) handle_errors(validator.iter_errors(config), handler, None) @@ -390,10 +391,15 @@ def get_schema_path(): return os.path.dirname(os.path.abspath(__file__)) -def load_jsonschema(version): +def load_jsonschema(config_file): filename = os.path.join( get_schema_path(), - "config_schema_v{0}.json".format(version)) + "config_schema_v{0}.json".format(config_file.version)) + + if not os.path.exists(filename): + raise ConfigurationError( + 'Version in "{}" is unsupported. {}' + .format(config_file.filename, VERSION_EXPLANATION)) with open(filename, "r") as fh: return json.load(fh) diff --git a/compose/const.py b/compose/const.py index e694dbdae..8de693445 100644 --- a/compose/const.py +++ b/compose/const.py @@ -21,8 +21,10 @@ SECRETS_PATH = '/run/secrets' COMPOSEFILE_V1 = '1' COMPOSEFILE_V2_0 = '2.0' COMPOSEFILE_V2_1 = '2.1' + COMPOSEFILE_V3_0 = '3.0' COMPOSEFILE_V3_1 = '3.1' +COMPOSEFILE_V3_2 = '3.2' API_VERSIONS = { COMPOSEFILE_V1: '1.21', @@ -30,6 +32,7 @@ API_VERSIONS = { COMPOSEFILE_V2_1: '1.24', COMPOSEFILE_V3_0: '1.25', COMPOSEFILE_V3_1: '1.25', + COMPOSEFILE_V3_2: '1.25', } API_VERSION_TO_ENGINE_VERSION = { @@ -38,4 +41,5 @@ API_VERSION_TO_ENGINE_VERSION = { API_VERSIONS[COMPOSEFILE_V2_1]: '1.12.0', API_VERSIONS[COMPOSEFILE_V3_0]: '1.13.0', API_VERSIONS[COMPOSEFILE_V3_1]: '1.13.0', + API_VERSIONS[COMPOSEFILE_V3_2]: '1.13.0', } diff --git a/tests/fixtures/ports-composefile/expanded-notation.yml b/tests/fixtures/ports-composefile/expanded-notation.yml index 46d587363..6fbe59176 100644 --- a/tests/fixtures/ports-composefile/expanded-notation.yml +++ b/tests/fixtures/ports-composefile/expanded-notation.yml @@ -1,4 +1,4 @@ -version: '3.1' +version: '3.2' services: simple: image: busybox:latest diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index f0d21456b..e8dbe8fbf 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -15,11 +15,11 @@ from .testcases import DockerClientTestCase from compose.config import config from compose.config import ConfigurationError from compose.config import types -from compose.config.config import V2_0 -from compose.config.config import V2_1 -from compose.config.config import V3_1 from compose.config.types import VolumeFromSpec from compose.config.types import VolumeSpec +from compose.const import COMPOSEFILE_V2_0 as V2_0 +from compose.const import COMPOSEFILE_V2_1 as V2_1 +from compose.const import COMPOSEFILE_V3_1 as V3_1 from compose.const import LABEL_PROJECT from compose.const import LABEL_SERVICE from compose.container import Container diff --git a/tests/integration/testcases.py b/tests/integration/testcases.py index efc1551b4..38fdcc660 100644 --- a/tests/integration/testcases.py +++ b/tests/integration/testcases.py @@ -10,12 +10,12 @@ from pytest import skip from .. import unittest from compose.cli.docker_client import docker_client from compose.config.config import resolve_environment -from compose.config.config import V1 -from compose.config.config import V2_0 -from compose.config.config import V2_1 -from compose.config.config import V3_0 from compose.config.environment import Environment from compose.const import API_VERSIONS +from compose.const import COMPOSEFILE_V1 as V1 +from compose.const import COMPOSEFILE_V2_0 as V2_0 +from compose.const import COMPOSEFILE_V2_0 as V2_1 +from compose.const import COMPOSEFILE_V3_0 as V3_0 from compose.const import LABEL_PROJECT from compose.progress_stream import stream_output from compose.service import Service diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 93bae9726..c86485d7b 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -17,11 +17,6 @@ from compose.config import config from compose.config import types from compose.config.config import resolve_build_args from compose.config.config import resolve_environment -from compose.config.config import V1 -from compose.config.config import V2_0 -from compose.config.config import V2_1 -from compose.config.config import V3_0 -from compose.config.config import V3_1 from compose.config.environment import Environment from compose.config.errors import ConfigurationError from compose.config.errors import VERSION_EXPLANATION @@ -29,6 +24,11 @@ from compose.config.serialize import denormalize_service_dict from compose.config.serialize import serialize_config from compose.config.serialize import serialize_ns_time_value from compose.config.types import VolumeSpec +from compose.const import COMPOSEFILE_V1 as V1 +from compose.const import COMPOSEFILE_V2_0 as V2_0 +from compose.const import COMPOSEFILE_V2_1 as V2_1 +from compose.const import COMPOSEFILE_V3_0 as V3_0 +from compose.const import COMPOSEFILE_V3_1 as V3_1 from compose.const import IS_WINDOWS_PLATFORM from compose.utils import nanoseconds_from_time_seconds from tests import mock diff --git a/tests/unit/config/types_test.py b/tests/unit/config/types_test.py index 22d7aa88a..66588d629 100644 --- a/tests/unit/config/types_test.py +++ b/tests/unit/config/types_test.py @@ -3,13 +3,13 @@ from __future__ import unicode_literals import pytest -from compose.config.config import V1 -from compose.config.config import V2_0 from compose.config.errors import ConfigurationError from compose.config.types import parse_extra_hosts from compose.config.types import ServicePort from compose.config.types import VolumeFromSpec from compose.config.types import VolumeSpec +from compose.const import COMPOSEFILE_V1 as V1 +from compose.const import COMPOSEFILE_V2_0 as V2_0 def test_parse_extra_hosts_list():