Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions compose/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,11 @@ def load_mapping(config_files, get_func, entity_type, working_dir=None):

external = config.get('external')
if external:
name_field = 'name' if entity_type == 'Volume' else 'external_name'
validate_external(entity_type, name, config, config_file.version)
if isinstance(external, dict):
config[name_field] = external.get('name')
config['name'] = external.get('name')
elif not config.get('name'):
config[name_field] = name
config['name'] = name

if 'driver_opts' in config:
config['driver_opts'] = build_string_dict(
Expand Down
3 changes: 2 additions & 1 deletion compose/config/config_schema_v2.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@
},
"internal": {"type": "boolean"},
"enable_ipv6": {"type": "boolean"},
"labels": {"$ref": "#/definitions/list_or_dict"}
"labels": {"$ref": "#/definitions/list_or_dict"},
"name": {"type": "string"}
},
"additionalProperties": false
},
Expand Down
3 changes: 2 additions & 1 deletion compose/config/config_schema_v2.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,8 @@
},
"internal": {"type": "boolean"},
"enable_ipv6": {"type": "boolean"},
"labels": {"$ref": "#/definitions/list_or_dict"}
"labels": {"$ref": "#/definitions/list_or_dict"},
"name": {"type": "string"}
},
"additionalProperties": false
},
Expand Down
3 changes: 2 additions & 1 deletion compose/config/config_schema_v2.3.json
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@
},
"internal": {"type": "boolean"},
"enable_ipv6": {"type": "boolean"},
"labels": {"$ref": "#/definitions/list_or_dict"}
"labels": {"$ref": "#/definitions/list_or_dict"},
"name": {"type": "string"}
},
"additionalProperties": false
},
Expand Down
58 changes: 44 additions & 14 deletions compose/config/config_schema_v3.5.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
}
},

"patternProperties": {"^x-": {}},
"additionalProperties": false,

"definitions": {
Expand Down Expand Up @@ -154,6 +155,7 @@
"hostname": {"type": "string"},
"image": {"type": "string"},
"ipc": {"type": "string"},
"isolation": {"type": "string"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},

Expand Down Expand Up @@ -281,7 +283,6 @@
{
"type": "object",
"required": ["type"],
"additionalProperties": false,
"properties": {
"type": {"type": "string"},
"source": {"type": "string"},
Expand All @@ -300,7 +301,8 @@
"nocopy": {"type": "boolean"}
}
}
}
},
"additionalProperties": false
}
],
"uniqueItems": true
Expand All @@ -317,15 +319,16 @@
"additionalProperties": false,
"properties": {
"disable": {"type": "boolean"},
"interval": {"type": "string"},
"interval": {"type": "string", "format": "duration"},
"retries": {"type": "number"},
"test": {
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
]
},
"timeout": {"type": "string"}
"timeout": {"type": "string", "format": "duration"},
"start_period": {"type": "string", "format": "duration"}
}
},
"deployment": {
Expand Down Expand Up @@ -353,8 +356,23 @@
"resources": {
"type": "object",
"properties": {
"limits": {"$ref": "#/definitions/resource"},
"reservations": {"$ref": "#/definitions/resource"}
"limits": {
"type": "object",
"properties": {
"cpus": {"type": "string"},
"memory": {"type": "string"}
},
"additionalProperties": false
},
"reservations": {
"type": "object",
"properties": {
"cpus": {"type": "string"},
"memory": {"type": "string"},
"generic_resources": {"$ref": "#/definitions/generic_resources"}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
Expand Down Expand Up @@ -389,20 +407,30 @@
"additionalProperties": false
},

"resource": {
"id": "#/definitions/resource",
"type": "object",
"properties": {
"cpus": {"type": "string"},
"memory": {"type": "string"}
},
"additionalProperties": false
"generic_resources": {
"id": "#/definitions/generic_resources",
"type": "array",
"items": {
"type": "object",
"properties": {
"discrete_resource_spec": {
"type": "object",
"properties": {
"kind": {"type": "string"},
"value": {"type": "number"}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},

"network": {
"id": "#/definitions/network",
"type": ["object", "null"],
"properties": {
"name": {"type": "string"},
"driver": {"type": "string"},
"driver_opts": {
"type": "object",
Expand Down Expand Up @@ -469,6 +497,7 @@
"id": "#/definitions/secret",
"type": "object",
"properties": {
"name": {"type": "string"},
"file": {"type": "string"},
"external": {
"type": ["boolean", "object"],
Expand All @@ -485,6 +514,7 @@
"id": "#/definitions/config",
"type": "object",
"properties": {
"name": {"type": "string"},
"file": {"type": "string"},
"external": {
"type": ["boolean", "object"],
Expand Down
11 changes: 10 additions & 1 deletion compose/config/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from compose.const import COMPOSEFILE_V3_0 as V3_0
from compose.const import COMPOSEFILE_V3_2 as V3_2
from compose.const import COMPOSEFILE_V3_4 as V3_4
from compose.const import COMPOSEFILE_V3_5 as V3_5


def serialize_config_type(dumper, data):
Expand Down Expand Up @@ -58,6 +59,7 @@ def denormalize_config(config, image_digests=None):
service_dict.pop('name'): service_dict
for service_dict in denormalized_services
}

for key in ('networks', 'volumes', 'secrets', 'configs'):
config_dict = getattr(config, key)
if not config_dict:
Expand All @@ -68,14 +70,21 @@ def denormalize_config(config, image_digests=None):
del conf['external_name']

if 'name' in conf:
if config.version < V2_1 or (config.version >= V3_0 and config.version < V3_4):
if config.version < V2_1 or (
config.version >= V3_0 and config.version < v3_introduced_name_key(key)):
del conf['name']
elif 'external' in conf:
conf['external'] = True

return result


def v3_introduced_name_key(key):
if key == 'volumes':
return V3_4
return V3_5


def serialize_config(config, image_digests=None):
return yaml.safe_dump(
denormalize_config(config, image_digests),
Expand Down
5 changes: 3 additions & 2 deletions compose/config/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,17 +293,18 @@ def merge_field(self):
return self.alias


class ServiceConfigBase(namedtuple('_ServiceConfigBase', 'source target uid gid mode')):
class ServiceConfigBase(namedtuple('_ServiceConfigBase', 'source target uid gid mode name')):
@classmethod
def parse(cls, spec):
if isinstance(spec, six.string_types):
return cls(spec, None, None, None, None)
return cls(spec, None, None, None, None, None)
return cls(
spec.get('source'),
spec.get('target'),
spec.get('uid'),
spec.get('gid'),
spec.get('mode'),
spec.get('name')
)

@property
Expand Down
23 changes: 13 additions & 10 deletions compose/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@

class Network(object):
def __init__(self, client, project, name, driver=None, driver_opts=None,
ipam=None, external_name=None, internal=False, enable_ipv6=False,
labels=None):
ipam=None, external=False, internal=False, enable_ipv6=False,
labels=None, custom_name=False):
self.client = client
self.project = project
self.name = name
self.driver = driver
self.driver_opts = driver_opts
self.ipam = create_ipam_config_from_dict(ipam)
self.external_name = external_name
self.external = external
self.internal = internal
self.enable_ipv6 = enable_ipv6
self.labels = labels
self.custom_name = custom_name

def ensure(self):
if self.external_name:
if self.external:
try:
self.inspect()
log.debug(
Expand All @@ -51,7 +52,7 @@ def ensure(self):
'Network {name} declared as external, but could'
' not be found. Please create the network manually'
' using `{command} {name}` and try again.'.format(
name=self.external_name,
name=self.full_name,
command='docker network create'
)
)
Expand Down Expand Up @@ -83,7 +84,7 @@ def ensure(self):
)

def remove(self):
if self.external_name:
if self.external:
log.info("Network %s is external, skipping", self.full_name)
return

Expand All @@ -95,8 +96,8 @@ def inspect(self):

@property
def full_name(self):
if self.external_name:
return self.external_name
if self.custom_name:
return self.name
return '{0}_{1}'.format(self.project, self.name)

@property
Expand Down Expand Up @@ -203,14 +204,16 @@ def build_networks(name, config_data, client):
network_config = config_data.networks or {}
networks = {
network_name: Network(
client=client, project=name, name=network_name,
client=client, project=name,
name=data.get('name', network_name),
driver=data.get('driver'),
driver_opts=data.get('driver_opts'),
ipam=data.get('ipam'),
external_name=data.get('external_name'),
external=bool(data.get('external', False)),
internal=data.get('internal'),
enable_ipv6=data.get('enable_ipv6'),
labels=data.get('labels'),
custom_name=data.get('name') is not None,
)
for network_name, data in network_config.items()
}
Expand Down
2 changes: 1 addition & 1 deletion compose/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ def get_secrets(service, service_secrets, secret_defs):
"Service \"{service}\" uses an undefined secret \"{secret}\" "
.format(service=service, secret=secret.source))

if secret_def.get('external_name'):
if secret_def.get('external'):
log.warn("Service \"{service}\" uses secret \"{secret}\" which is external. "
"External secrets are not available to containers created by "
"docker-compose.".format(service=service, secret=secret.source))
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.spec
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ exe = EXE(pyz,
'compose/config/config_schema_v3.4.json',
'DATA'
),
(
'compose/config/config_schema_v3.5.json',
'compose/config/config_schema_v3.5.json',
'DATA'
),
(
'compose/GITSHA',
'compose/GITSHA',
Expand Down
16 changes: 16 additions & 0 deletions tests/acceptance/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,22 @@ def test_config_external_volume_v3_4(self):
}
}

def test_config_external_network_v3_5(self):
self.base_dir = 'tests/fixtures/networks'
result = self.dispatch(['-f', 'external-networks-v3-5.yml', 'config'])
json_result = yaml.load(result.stdout)
assert 'networks' in json_result
assert json_result['networks'] == {
'foo': {
'external': True,
'name': 'some_foo',
},
'bar': {
'external': True,
'name': 'some_bar',
},
}

def test_config_v1(self):
self.base_dir = 'tests/fixtures/v1-config'
result = self.dispatch(['config'])
Expand Down
17 changes: 17 additions & 0 deletions tests/fixtures/networks/external-networks-v3-5.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: "3.5"

services:
web:
image: busybox
command: top
networks:
- foo
- bar

networks:
foo:
external: true
name: some_foo
bar:
external:
name: some_bar
Loading