diff --git a/compose/config/config.py b/compose/config/config.py index 55e8c2757..aa8dd068b 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -20,6 +20,7 @@ from ..utils import json_hash from ..utils import parse_bytes from ..utils import parse_nanoseconds_int from ..utils import splitdrive +from ..version import ComposeVersion from .environment import env_vars_from_file from .environment import Environment from .environment import split_env @@ -184,6 +185,13 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')): def from_filename(cls, filename): return cls(filename, load_yaml(filename)) + @cached_property + def config_version(self): + version = self.config.get('version', None) + if isinstance(version, dict): + return V1 + return ComposeVersion(version) if version else self.version + @cached_property def version(self): version = self.config.get('version', None) @@ -222,15 +230,13 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')): 'Version "{}" in "{}" is invalid.' .format(version, self.filename)) - if version.startswith("1"): - version = V1 - - if version == V1: + if version.startswith("1"): raise ConfigurationError( 'Version in "{}" is invalid. {}' .format(self.filename, VERSION_EXPLANATION) ) - return version + + return VERSION def get_service(self, name): return self.get_service_dicts()[name] @@ -253,8 +259,10 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')): return {} if self.version == V1 else self.config.get('configs', {}) -class Config(namedtuple('_Config', 'version services volumes networks secrets configs')): +class Config(namedtuple('_Config', 'config_version version services volumes networks secrets configs')): """ + :param config_version: configuration file version + :type config_version: int :param version: configuration version :type version: int :param services: List of service description dictionaries @@ -401,9 +409,8 @@ def load(config_details, interpolate=True): for service_dict in service_dicts: match_named_volumes(service_dict, volumes) - version = main_file.version - - return Config(version, service_dicts, volumes, networks, secrets, configs) + return Config(main_file.config_version, main_file.version, + service_dicts, volumes, networks, secrets, configs) def load_mapping(config_files, get_func, entity_type, working_dir=None): diff --git a/compose/config/serialize.py b/compose/config/serialize.py index 2d9493a03..e3295df78 100644 --- a/compose/config/serialize.py +++ b/compose/config/serialize.py @@ -44,7 +44,7 @@ yaml.SafeDumper.add_representer(types.ServicePort, serialize_dict_type) def denormalize_config(config, image_digests=None): - result = {'version': str(config.version)} + result = {'version': str(config.config_version)} denormalized_services = [ denormalize_service_dict( service_dict, diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 3d9a31c2d..d678130c1 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -359,7 +359,7 @@ services: 'web': { 'command': 'true', 'image': 'alpine:latest', - 'ports': ['5643/tcp', '9999/tcp'] + 'ports': [{'target': 5643}, {'target': 9999}] } } } @@ -374,7 +374,7 @@ services: 'web': { 'command': 'false', 'image': 'alpine:latest', - 'ports': ['5644/tcp', '9998/tcp'] + 'ports': [{'target': 5644}, {'target': 9998}] } } } @@ -389,7 +389,7 @@ services: 'web': { 'command': 'echo uwu', 'image': 'alpine:3.10.1', - 'ports': ['3341/tcp', '4449/tcp'] + 'ports': [{'target': 3341}, {'target': 4449}] } } } diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index 96929f209..c4210291f 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -37,6 +37,7 @@ from tests.integration.testcases import no_cluster def build_config(**kwargs): return config.Config( + config_version=kwargs.get('version', VERSION), version=kwargs.get('version', VERSION), services=kwargs.get('services'), volumes=kwargs.get('volumes'), diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index b1586ae1f..b67ecdaf4 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -168,12 +168,14 @@ class ConfigTest(unittest.TestCase): } }) ) + assert cfg.config_version == VERSION assert cfg.version == VERSION for version in ['2', '2.0', '2.1', '2.2', '2.3', '3', '3.0', '3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7', '3.8']: cfg = config.load(build_config_details({'version': version})) - assert cfg.version == version + assert cfg.config_version == version + assert cfg.version == VERSION def test_v1_file_version(self): cfg = config.load(build_config_details({'web': {'image': 'busybox'}})) @@ -5369,7 +5371,7 @@ class SerializeTest(unittest.TestCase): assert serialized_config['secrets']['two'] == {'external': True, 'name': 'two'} def test_serialize_ports(self): - config_dict = config.Config(version=VERSION, services=[ + config_dict = config.Config(config_version=VERSION, version=VERSION, services=[ { 'ports': [types.ServicePort('80', '8080', None, None, None)], 'image': 'alpine', @@ -5380,8 +5382,20 @@ class SerializeTest(unittest.TestCase): serialized_config = yaml.safe_load(serialize_config(config_dict)) assert [{'published': 8080, 'target': 80}] == serialized_config['services']['web']['ports'] + def test_serialize_ports_v1(self): + config_dict = config.Config(config_version=V1, version=V1, services=[ + { + 'ports': [types.ServicePort('80', '8080', None, None, None)], + 'image': 'alpine', + 'name': 'web' + } + ], volumes={}, networks={}, secrets={}, configs={}) + + serialized_config = yaml.safe_load(serialize_config(config_dict)) + assert ['8080:80/tcp'] == serialized_config['services']['web']['ports'] + def test_serialize_ports_with_ext_ip(self): - config_dict = config.Config(version=VERSION, services=[ + config_dict = config.Config(config_version=VERSION, version=VERSION, services=[ { 'ports': [types.ServicePort('80', '8080', None, None, '127.0.0.1')], 'image': 'alpine', diff --git a/tests/unit/project_test.py b/tests/unit/project_test.py index 01f0b11ef..a3ffdb67d 100644 --- a/tests/unit/project_test.py +++ b/tests/unit/project_test.py @@ -28,6 +28,7 @@ from compose.service import Service def build_config(**kwargs): return Config( + config_version=kwargs.get('config_version', VERSION), version=kwargs.get('version', VERSION), services=kwargs.get('services'), volumes=kwargs.get('volumes'),