From 2b5d3f51cb7e8501c1b5481ce45d139b7e49171e Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Fri, 12 Feb 2016 16:10:36 -0800 Subject: [PATCH 1/8] Allow user to specify custom network aliases Signed-off-by: Joffrey F --- compose/config/config.py | 3 +++ compose/config/service_schema_v2.0.json | 13 ++++++++++--- compose/config/validation.py | 13 +++++++++++++ compose/network.py | 11 ++++++++--- compose/project.py | 4 ++-- compose/service.py | 12 ++++++------ 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/compose/config/config.py b/compose/config/config.py index dbc6b6b22..1e077adca 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -31,6 +31,7 @@ from .types import ServiceLink from .types import VolumeFromSpec from .types import VolumeSpec from .validation import match_named_volumes +from .validation import match_network_aliases from .validation import validate_against_fields_schema from .validation import validate_against_service_schema from .validation import validate_depends_on @@ -546,6 +547,8 @@ def validate_service(service_config, service_names, version): validate_network_mode(service_config, service_names) validate_depends_on(service_config, service_names) + match_network_aliases(service_config.config) + if not service_dict.get('image') and has_uppercase(service_name): raise ConfigurationError( "Service '{name}' contains uppercase characters which are not valid " diff --git a/compose/config/service_schema_v2.0.json b/compose/config/service_schema_v2.0.json index 3196ca89e..1c8022d31 100644 --- a/compose/config/service_schema_v2.0.json +++ b/compose/config/service_schema_v2.0.json @@ -120,9 +120,16 @@ "network_mode": {"type": "string"}, "networks": { - "type": "array", - "items": {"type": "string"}, - "uniqueItems": true + "$ref": "#/definitions/list_of_strings" + }, + "network_aliases": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/list_of_strings" + } + }, + "additionalProperties": false }, "pid": {"type": ["string", "null"]}, diff --git a/compose/config/validation.py b/compose/config/validation.py index 35727e2cc..539291509 100644 --- a/compose/config/validation.py +++ b/compose/config/validation.py @@ -91,6 +91,19 @@ def match_named_volumes(service_dict, project_volumes): ) +def match_network_aliases(service_dict): + networks = service_dict.get('networks', []) + aliased_networks = service_dict.get('network_aliases', {}).keys() + for n in aliased_networks: + if n not in networks: + raise ConfigurationError( + 'Network "{0}" is referenced in network_aliases, but is not' + 'declared in the networks list for service "{1}"'.format( + n, service_dict.get('name') + ) + ) + + def validate_top_level_service_objects(filename, service_dicts): """Perform some high level validation of the service name and value. diff --git a/compose/network.py b/compose/network.py index 82a78f3b5..99c04649b 100644 --- a/compose/network.py +++ b/compose/network.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) class Network(object): def __init__(self, client, project, name, driver=None, driver_opts=None, - ipam=None, external_name=None): + ipam=None, external_name=None, aliases=None): self.client = client self.project = project self.name = name @@ -23,6 +23,7 @@ class Network(object): self.driver_opts = driver_opts self.ipam = create_ipam_config_from_dict(ipam) self.external_name = external_name + self.aliases = aliases or [] def ensure(self): if self.external_name: @@ -166,14 +167,18 @@ def get_network_names_for_service(service_dict): def get_networks(service_dict, network_definitions): - networks = [] + networks = {} + aliases = service_dict.get('network_aliases', {}) for name in get_network_names_for_service(service_dict): + log.debug(name) network = network_definitions.get(name) if network: - networks.append(network.full_name) + log.debug(aliases) + networks[network.full_name] = aliases.get(name, []) else: raise ConfigurationError( 'Service "{}" uses an undefined network "{}"' .format(service_dict['name'], name)) + log.debug(networks) return networks diff --git a/compose/project.py b/compose/project.py index 62e1d2cd3..0394fa15a 100644 --- a/compose/project.py +++ b/compose/project.py @@ -69,11 +69,11 @@ class Project(object): if use_networking: service_networks = get_networks(service_dict, networks) else: - service_networks = [] + service_networks = {} service_dict.pop('networks', None) links = project.get_links(service_dict) - network_mode = project.get_network_mode(service_dict, service_networks) + network_mode = project.get_network_mode(service_dict, service_networks.keys()) volumes_from = get_volumes_from(project, service_dict) if config_data.version != V1: diff --git a/compose/service.py b/compose/service.py index 78eed4c46..c597abd08 100644 --- a/compose/service.py +++ b/compose/service.py @@ -124,7 +124,7 @@ class Service(object): self.links = links or [] self.volumes_from = volumes_from or [] self.network_mode = network_mode or NetworkMode(None) - self.networks = networks or [] + self.networks = networks or {} self.options = options def containers(self, stopped=False, one_off=False, filters={}): @@ -432,14 +432,14 @@ class Service(object): def connect_container_to_networks(self, container): connected_networks = container.get('NetworkSettings.Networks') - for network in self.networks: + for network, aliases in self.networks.items(): if network in connected_networks: self.client.disconnect_container_from_network( container.id, network) self.client.connect_container_to_network( container.id, network, - aliases=self._get_aliases(container), + aliases=list(self._get_aliases(container).union(aliases)), links=self._get_links(False), ) @@ -473,7 +473,7 @@ class Service(object): 'image_id': self.image()['Id'], 'links': self.get_link_names(), 'net': self.network_mode.id, - 'networks': self.networks, + 'networks': self.networks.keys(), 'volumes_from': [ (v.source.name, v.mode) for v in self.volumes_from if isinstance(v.source, Service) @@ -514,9 +514,9 @@ class Service(object): def _get_aliases(self, container): if container.labels.get(LABEL_ONE_OFF) == "True": - return [] + return set() - return [self.name, container.short_id] + return set([self.name, container.short_id]) def _get_links(self, link_to_self): links = {} From 633e349ab97af63849fc739eb9596be6f6f24362 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Fri, 12 Feb 2016 16:58:29 -0800 Subject: [PATCH 2/8] Test network_aliases feature Signed-off-by: Joffrey F --- compose/config/validation.py | 2 +- compose/project.py | 4 ++- tests/acceptance/cli_test.py | 27 +++++++++++++++++++++ tests/fixtures/networks/network-aliases.yml | 18 ++++++++++++++ tests/unit/config/config_test.py | 21 ++++++++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/networks/network-aliases.yml diff --git a/compose/config/validation.py b/compose/config/validation.py index 539291509..59ce9f54e 100644 --- a/compose/config/validation.py +++ b/compose/config/validation.py @@ -97,7 +97,7 @@ def match_network_aliases(service_dict): for n in aliased_networks: if n not in networks: raise ConfigurationError( - 'Network "{0}" is referenced in network_aliases, but is not' + 'Network "{0}" is referenced in network_aliases, but is not ' 'declared in the networks list for service "{1}"'.format( n, service_dict.get('name') ) diff --git a/compose/project.py b/compose/project.py index 0394fa15a..cfb11aa05 100644 --- a/compose/project.py +++ b/compose/project.py @@ -73,7 +73,9 @@ class Project(object): service_dict.pop('networks', None) links = project.get_links(service_dict) - network_mode = project.get_network_mode(service_dict, service_networks.keys()) + network_mode = project.get_network_mode( + service_dict, list(service_networks.keys()) + ) volumes_from = get_volumes_from(project, service_dict) if config_data.version != V1: diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index ea3d132a5..49048fb79 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -445,6 +445,33 @@ class CLITestCase(DockerClientTestCase): assert networks[0]['Options']['com.docker.network.bridge.enable_icc'] == 'false' + def test_up_with_network_aliases(self): + filename = 'network-aliases.yml' + self.base_dir = 'tests/fixtures/networks' + self.dispatch(['-f', filename, 'up', '-d'], None) + back_name = '{}_back'.format(self.project.name) + front_name = '{}_front'.format(self.project.name) + + networks = [ + n for n in self.client.networks() + if n['Name'].startswith('{}_'.format(self.project.name)) + ] + + # Two networks were created: back and front + assert sorted(n['Name'] for n in networks) == [back_name, front_name] + web_container = self.project.get_service('web').containers()[0] + + back_aliases = web_container.get( + 'NetworkSettings.Networks.{}.Aliases'.format(back_name) + ) + assert 'web' in back_aliases + front_aliases = web_container.get( + 'NetworkSettings.Networks.{}.Aliases'.format(front_name) + ) + assert 'web' in front_aliases + assert 'forward_facing' in front_aliases + assert 'ahead' in front_aliases + @v2_only() def test_up_with_networks(self): self.base_dir = 'tests/fixtures/networks' diff --git a/tests/fixtures/networks/network-aliases.yml b/tests/fixtures/networks/network-aliases.yml new file mode 100644 index 000000000..987b0809a --- /dev/null +++ b/tests/fixtures/networks/network-aliases.yml @@ -0,0 +1,18 @@ +version: "2" + +services: + web: + image: busybox + command: top + networks: + - front + - back + + network_aliases: + front: + - forward_facing + - ahead + +networks: + front: {} + back: {} diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 1d6f1cbb0..88d46a143 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -556,6 +556,27 @@ class ConfigTest(unittest.TestCase): assert services[1]['name'] == 'db' assert services[2]['name'] == 'web' + def test_invalid_network_alias(self): + config_details = build_config_details({ + 'version': '2', + 'services': { + 'web': { + 'image': 'busybox', + 'networks': ['hello'], + 'network_aliases': { + 'world': ['planet', 'universe'] + } + } + }, + 'networks': { + 'hello': {}, + 'world': {} + } + }) + with pytest.raises(ConfigurationError) as exc: + config.load(config_details) + assert 'not declared in the networks list' in exc.exconly() + def test_config_build_configuration(self): service = config.load( build_config_details( From 7801cfc5d1ac9f481ebc991ef8bd6fab7a94575c Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Fri, 12 Feb 2016 17:17:31 -0800 Subject: [PATCH 3/8] Document network_aliases config Signed-off-by: Joffrey F --- docs/compose-file.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/compose-file.md b/docs/compose-file.md index 04733916f..2e9632c40 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -451,9 +451,27 @@ id. net: "none" net: "container:[service name or container name/id]" +### network_aliases + +> [Version 2 file format](#version-2) only. + +Alias names for this service on each joined network. All networks referenced +here must also appear under the `networks` key. + + networks: + - some-network + - other-network + network_aliases: + some-network: + - alias1 + - alias3 + other-network: + - alias2 + - alias4 + ### network_mode -> [Version 2 file format](#version-1) only. In version 1, use [net](#net). +> [Version 2 file format](#version-2) only. In version 1, use [net](#net). Network mode. Use the same values as the docker client `--net` parameter, plus the special form `service:[service name]`. From 41e399be9880583954c6de6559886465e3f8db85 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Fri, 12 Feb 2016 17:20:18 -0800 Subject: [PATCH 4/8] Fix network list serialization in py3 Signed-off-by: Joffrey F --- compose/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/service.py b/compose/service.py index c597abd08..5f40a4577 100644 --- a/compose/service.py +++ b/compose/service.py @@ -473,7 +473,7 @@ class Service(object): 'image_id': self.image()['Id'], 'links': self.get_link_names(), 'net': self.network_mode.id, - 'networks': self.networks.keys(), + 'networks': list(self.networks.keys()), 'volumes_from': [ (v.source.name, v.mode) for v in self.volumes_from if isinstance(v.source, Service) From 4b99b32ffb346d0cb4b488a69565ea991acbb38f Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Fri, 12 Feb 2016 17:48:42 -0800 Subject: [PATCH 5/8] Add v2_only decorator to network aliases test Signed-off-by: Joffrey F --- tests/acceptance/cli_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 49048fb79..4ba48d45a 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -445,6 +445,7 @@ class CLITestCase(DockerClientTestCase): assert networks[0]['Options']['com.docker.network.bridge.enable_icc'] == 'false' + @v2_only() def test_up_with_network_aliases(self): filename = 'network-aliases.yml' self.base_dir = 'tests/fixtures/networks' From 825a0941f04523a6c68e47bb3392a720744f7b7a Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 18 Feb 2016 12:04:07 -0800 Subject: [PATCH 6/8] Network aliases are now part of the network dictionary Signed-off-by: Joffrey F --- compose/config/config.py | 3 -- compose/config/service_schema_v2.0.json | 30 +++++++++++++------- compose/config/validation.py | 13 --------- compose/network.py | 28 +++++++++++-------- docs/compose-file.md | 31 +++++++++------------ tests/fixtures/networks/network-aliases.yml | 10 +++---- tests/unit/config/config_test.py | 21 -------------- 7 files changed, 54 insertions(+), 82 deletions(-) diff --git a/compose/config/config.py b/compose/config/config.py index 1e077adca..dbc6b6b22 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -31,7 +31,6 @@ from .types import ServiceLink from .types import VolumeFromSpec from .types import VolumeSpec from .validation import match_named_volumes -from .validation import match_network_aliases from .validation import validate_against_fields_schema from .validation import validate_against_service_schema from .validation import validate_depends_on @@ -547,8 +546,6 @@ def validate_service(service_config, service_names, version): validate_network_mode(service_config, service_names) validate_depends_on(service_config, service_names) - match_network_aliases(service_config.config) - if not service_dict.get('image') and has_uppercase(service_name): raise ConfigurationError( "Service '{name}' contains uppercase characters which are not valid " diff --git a/compose/config/service_schema_v2.0.json b/compose/config/service_schema_v2.0.json index 1c8022d31..edccedc66 100644 --- a/compose/config/service_schema_v2.0.json +++ b/compose/config/service_schema_v2.0.json @@ -120,18 +120,28 @@ "network_mode": {"type": "string"}, "networks": { - "$ref": "#/definitions/list_of_strings" - }, - "network_aliases": { - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9._-]+$": { - "$ref": "#/definitions/list_of_strings" + "oneOf": [ + {"$ref": "#/definitions/list_of_strings"}, + { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "oneOf": [ + { + "type": "object", + "properties": { + "aliases": {"$ref": "#/definitions/list_of_strings"} + }, + "additionalProperties": false + }, + {"type": "null"} + ] + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + ] }, - "pid": {"type": ["string", "null"]}, "ports": { diff --git a/compose/config/validation.py b/compose/config/validation.py index 59ce9f54e..35727e2cc 100644 --- a/compose/config/validation.py +++ b/compose/config/validation.py @@ -91,19 +91,6 @@ def match_named_volumes(service_dict, project_volumes): ) -def match_network_aliases(service_dict): - networks = service_dict.get('networks', []) - aliased_networks = service_dict.get('network_aliases', {}).keys() - for n in aliased_networks: - if n not in networks: - raise ConfigurationError( - 'Network "{0}" is referenced in network_aliases, but is not ' - 'declared in the networks list for service "{1}"'.format( - n, service_dict.get('name') - ) - ) - - def validate_top_level_service_objects(filename, service_dicts): """Perform some high level validation of the service name and value. diff --git a/compose/network.py b/compose/network.py index 99c04649b..d17ed0805 100644 --- a/compose/network.py +++ b/compose/network.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) class Network(object): def __init__(self, client, project, name, driver=None, driver_opts=None, - ipam=None, external_name=None, aliases=None): + ipam=None, external_name=None): self.client = client self.project = project self.name = name @@ -23,7 +23,6 @@ class Network(object): self.driver_opts = driver_opts self.ipam = create_ipam_config_from_dict(ipam) self.external_name = external_name - self.aliases = aliases or [] def ensure(self): if self.external_name: @@ -160,25 +159,32 @@ class ProjectNetworks(object): network.ensure() -def get_network_names_for_service(service_dict): +def get_network_aliases_for_service(service_dict): if 'network_mode' in service_dict: - return [] - return service_dict.get('networks', ['default']) + return {} + networks = service_dict.get('networks', ['default']) + if isinstance(networks, list): + return dict((net, []) for net in networks) + + return dict( + (net, (config or {}).get('aliases', [])) + for net, config in networks.items() + ) + + +def get_network_names_for_service(service_dict): + return get_network_aliases_for_service(service_dict).keys() def get_networks(service_dict, network_definitions): networks = {} - aliases = service_dict.get('network_aliases', {}) - for name in get_network_names_for_service(service_dict): - log.debug(name) + for name, aliases in get_network_aliases_for_service(service_dict).items(): network = network_definitions.get(name) if network: - log.debug(aliases) - networks[network.full_name] = aliases.get(name, []) + networks[network.full_name] = aliases else: raise ConfigurationError( 'Service "{}" uses an undefined network "{}"' .format(service_dict['name'], name)) - log.debug(networks) return networks diff --git a/docs/compose-file.md b/docs/compose-file.md index 2e9632c40..45e1ac098 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -451,24 +451,6 @@ id. net: "none" net: "container:[service name or container name/id]" -### network_aliases - -> [Version 2 file format](#version-2) only. - -Alias names for this service on each joined network. All networks referenced -here must also appear under the `networks` key. - - networks: - - some-network - - other-network - network_aliases: - some-network: - - alias1 - - alias3 - other-network: - - alias2 - - alias4 - ### network_mode > [Version 2 file format](#version-2) only. In version 1, use [net](#net). @@ -493,6 +475,19 @@ Networks to join, referencing entries under the - some-network - other-network +#### aliases + +Alias names for this service on the specified network. + + networks: + some-network: + aliases: + - alias1 + - alias3 + other-network: + aliases: + - alias2 + ### pid pid: "host" diff --git a/tests/fixtures/networks/network-aliases.yml b/tests/fixtures/networks/network-aliases.yml index 987b0809a..8cf7d5af9 100644 --- a/tests/fixtures/networks/network-aliases.yml +++ b/tests/fixtures/networks/network-aliases.yml @@ -5,13 +5,11 @@ services: image: busybox command: top networks: - - front - - back - - network_aliases: front: - - forward_facing - - ahead + aliases: + - forward_facing + - ahead + back: networks: front: {} diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 88d46a143..1d6f1cbb0 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -556,27 +556,6 @@ class ConfigTest(unittest.TestCase): assert services[1]['name'] == 'db' assert services[2]['name'] == 'web' - def test_invalid_network_alias(self): - config_details = build_config_details({ - 'version': '2', - 'services': { - 'web': { - 'image': 'busybox', - 'networks': ['hello'], - 'network_aliases': { - 'world': ['planet', 'universe'] - } - } - }, - 'networks': { - 'hello': {}, - 'world': {} - } - }) - with pytest.raises(ConfigurationError) as exc: - config.load(config_details) - assert 'not declared in the networks list' in exc.exconly() - def test_config_build_configuration(self): service = config.load( build_config_details( From 7152f7ea7662633415de0750b6cb6b3f6742c847 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 18 Feb 2016 14:52:52 -0800 Subject: [PATCH 7/8] Handle mismatched network formats in config files Signed-off-by: Joffrey F --- compose/config/config.py | 6 +++++- compose/network.py | 5 +---- docs/compose-file.md | 10 ++++++++- tests/acceptance/cli_test.py | 2 +- tests/integration/project_test.py | 4 ++-- tests/unit/config/config_test.py | 36 +++++++++++++++++++++++++++++++ tests/unit/project_test.py | 2 +- 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/compose/config/config.py b/compose/config/config.py index dbc6b6b22..d0024e9cd 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -612,6 +612,9 @@ def finalize_service(service_config, service_names, version): else: service_dict['network_mode'] = network_mode + if 'networks' in service_dict: + service_dict['networks'] = parse_networks(service_dict['networks']) + if 'restart' in service_dict: service_dict['restart'] = parse_restart_spec(service_dict['restart']) @@ -701,6 +704,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('networks', parse_networks) md.merge_sequence('links', ServiceLink.parse) for field in ['volumes', 'devices']: @@ -710,7 +714,6 @@ def merge_service_dicts(base, override, version): 'depends_on', 'expose', 'external_links', - 'networks', 'ports', 'volumes_from', ]: @@ -798,6 +801,7 @@ def parse_dict_or_list(split_func, type_name, arguments): parse_build_arguments = functools.partial(parse_dict_or_list, split_env, 'build arguments') parse_environment = functools.partial(parse_dict_or_list, split_env, 'environment') parse_labels = functools.partial(parse_dict_or_list, split_label, 'labels') +parse_networks = functools.partial(parse_dict_or_list, lambda k: (k, None), 'networks') def parse_ulimits(ulimits): diff --git a/compose/network.py b/compose/network.py index d17ed0805..135502cc0 100644 --- a/compose/network.py +++ b/compose/network.py @@ -162,10 +162,7 @@ class ProjectNetworks(object): def get_network_aliases_for_service(service_dict): if 'network_mode' in service_dict: return {} - networks = service_dict.get('networks', ['default']) - if isinstance(networks, list): - return dict((net, []) for net in networks) - + networks = service_dict.get('networks', {'default': None}) return dict( (net, (config or {}).get('aliases', [])) for net, config in networks.items() diff --git a/docs/compose-file.md b/docs/compose-file.md index 45e1ac098..6441297fe 100644 --- a/docs/compose-file.md +++ b/docs/compose-file.md @@ -477,7 +477,15 @@ Networks to join, referencing entries under the #### aliases -Alias names for this service on the specified network. +Aliases (alternative hostnames) for this service on the network. Other servers +on the network can use either the service name or this alias to connect to +this service. Since `alias` is network-scoped: + + * the same service can have different aliases when connected to another + network. + * it is allowable to configure the same alias name to multiple containers + (services) on the same network. + networks: some-network: diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 4ba48d45a..318ab3d3f 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -185,7 +185,7 @@ class CLITestCase(DockerClientTestCase): 'build': { 'context': os.path.abspath(self.base_dir), }, - 'networks': ['front', 'default'], + 'networks': {'front': None, 'default': None}, 'volumes_from': ['service:other:rw'], }, 'other': { diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index 6bb076a3f..6542fa18e 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -565,7 +565,7 @@ class ProjectTest(DockerClientTestCase): 'name': 'web', 'image': 'busybox:latest', 'command': 'top', - 'networks': ['foo', 'bar', 'baz'], + 'networks': {'foo': None, 'bar': None, 'baz': None}, }], volumes={}, networks={ @@ -598,7 +598,7 @@ class ProjectTest(DockerClientTestCase): services=[{ 'name': 'web', 'image': 'busybox:latest', - 'networks': ['front'], + 'networks': {'front': None}, }], volumes={}, networks={ diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 1d6f1cbb0..204003bce 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -649,6 +649,42 @@ class ConfigTest(unittest.TestCase): assert service['build']['args']['opt1'] == '42' assert service['build']['args']['opt2'] == 'foobar' + def test_load_with_multiple_files_mismatched_networks_format(self): + base_file = config.ConfigFile( + 'base.yaml', + { + 'version': '2', + 'services': { + 'web': { + 'image': 'example/web', + 'networks': { + 'foobar': {'aliases': ['foo', 'bar']} + } + } + }, + 'networks': {'foobar': {}, 'baz': {}} + } + ) + + override_file = config.ConfigFile( + 'override.yaml', + { + 'version': '2', + 'services': { + 'web': { + 'networks': ['baz'] + } + } + } + ) + + details = config.ConfigDetails('.', [base_file, override_file]) + web_service = config.load(details).services[0] + assert web_service['networks'] == { + 'foobar': {'aliases': ['foo', 'bar']}, + 'baz': None + } + def test_load_with_multiple_files_v2(self): base_file = config.ConfigFile( 'base.yaml', diff --git a/tests/unit/project_test.py b/tests/unit/project_test.py index bec238de6..c28c21523 100644 --- a/tests/unit/project_test.py +++ b/tests/unit/project_test.py @@ -438,7 +438,7 @@ class ProjectTest(unittest.TestCase): { 'name': 'foo', 'image': 'busybox:latest', - 'networks': ['custom'] + 'networks': {'custom': None} }, ], networks={'custom': {}}, From 0cb8ba37757d8e075be9630d96b08475aff8986a Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 18 Feb 2016 15:28:12 -0800 Subject: [PATCH 8/8] Use modern set notation in _get_aliases Signed-off-by: Joffrey F --- compose/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/service.py b/compose/service.py index 5f40a4577..8b22b7d75 100644 --- a/compose/service.py +++ b/compose/service.py @@ -516,7 +516,7 @@ class Service(object): if container.labels.get(LABEL_ONE_OFF) == "True": return set() - return set([self.name, container.short_id]) + return {self.name, container.short_id} def _get_links(self, link_to_self): links = {}