mirror of
https://github.com/docker/compose.git
synced 2025-07-21 12:44:54 +02:00
Merge pull request #3653 from shin-/3637-link-local-ips
Add support for link-local IPs in service.networks definition
This commit is contained in:
commit
f65f89ad8c
@ -14,6 +14,7 @@ from cached_property import cached_property
|
|||||||
|
|
||||||
from ..const import COMPOSEFILE_V1 as V1
|
from ..const import COMPOSEFILE_V1 as V1
|
||||||
from ..const import COMPOSEFILE_V2_0 as V2_0
|
from ..const import COMPOSEFILE_V2_0 as V2_0
|
||||||
|
from ..const import COMPOSEFILE_V2_1 as V2_1
|
||||||
from ..utils import build_string_dict
|
from ..utils import build_string_dict
|
||||||
from ..utils import splitdrive
|
from ..utils import splitdrive
|
||||||
from .environment import env_vars_from_file
|
from .environment import env_vars_from_file
|
||||||
@ -174,7 +175,7 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
|
|||||||
if version == '2':
|
if version == '2':
|
||||||
version = V2_0
|
version = V2_0
|
||||||
|
|
||||||
if version != V2_0:
|
if version not in (V2_0, V2_1):
|
||||||
raise ConfigurationError(
|
raise ConfigurationError(
|
||||||
'Version in "{}" is unsupported. {}'
|
'Version in "{}" is unsupported. {}'
|
||||||
.format(self.filename, VERSION_EXPLANATION))
|
.format(self.filename, VERSION_EXPLANATION))
|
||||||
@ -424,7 +425,7 @@ def process_config_file(config_file, environment, service_name=None):
|
|||||||
'service',
|
'service',
|
||||||
environment,)
|
environment,)
|
||||||
|
|
||||||
if config_file.version == V2_0:
|
if config_file.version in (V2_0, V2_1):
|
||||||
processed_config = dict(config_file.config)
|
processed_config = dict(config_file.config)
|
||||||
processed_config['services'] = services
|
processed_config['services'] = services
|
||||||
processed_config['volumes'] = interpolate_config_section(
|
processed_config['volumes'] = interpolate_config_section(
|
||||||
|
319
compose/config/config_schema_v2.1.json
Normal file
319
compose/config/config_schema_v2.1.json
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"id": "config_schema_v2.1.json",
|
||||||
|
"type": "object",
|
||||||
|
|
||||||
|
"properties": {
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
|
||||||
|
"services": {
|
||||||
|
"id": "#/properties/services",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9._-]+$": {
|
||||||
|
"$ref": "#/definitions/service"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"networks": {
|
||||||
|
"id": "#/properties/networks",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9._-]+$": {
|
||||||
|
"$ref": "#/definitions/network"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"volumes": {
|
||||||
|
"id": "#/properties/volumes",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9._-]+$": {
|
||||||
|
"$ref": "#/definitions/volume"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"additionalProperties": false,
|
||||||
|
|
||||||
|
"definitions": {
|
||||||
|
|
||||||
|
"service": {
|
||||||
|
"id": "#/definitions/service",
|
||||||
|
"type": "object",
|
||||||
|
|
||||||
|
"properties": {
|
||||||
|
"build": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"context": {"type": "string"},
|
||||||
|
"dockerfile": {"type": "string"},
|
||||||
|
"args": {"$ref": "#/definitions/list_or_dict"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"cgroup_parent": {"type": "string"},
|
||||||
|
"command": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"type": "array", "items": {"type": "string"}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"container_name": {"type": "string"},
|
||||||
|
"cpu_shares": {"type": ["number", "string"]},
|
||||||
|
"cpu_quota": {"type": ["number", "string"]},
|
||||||
|
"cpuset": {"type": "string"},
|
||||||
|
"depends_on": {"$ref": "#/definitions/list_of_strings"},
|
||||||
|
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"dns": {"$ref": "#/definitions/string_or_list"},
|
||||||
|
"dns_search": {"$ref": "#/definitions/string_or_list"},
|
||||||
|
"domainname": {"type": "string"},
|
||||||
|
"entrypoint": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"type": "array", "items": {"type": "string"}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"env_file": {"$ref": "#/definitions/string_or_list"},
|
||||||
|
"environment": {"$ref": "#/definitions/list_or_dict"},
|
||||||
|
|
||||||
|
"expose": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": ["string", "number"],
|
||||||
|
"format": "expose"
|
||||||
|
},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"extends": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
|
||||||
|
"properties": {
|
||||||
|
"service": {"type": "string"},
|
||||||
|
"file": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["service"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||||
|
"hostname": {"type": "string"},
|
||||||
|
"image": {"type": "string"},
|
||||||
|
"ipc": {"type": "string"},
|
||||||
|
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||||
|
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
|
||||||
|
"logging": {
|
||||||
|
"type": "object",
|
||||||
|
|
||||||
|
"properties": {
|
||||||
|
"driver": {"type": "string"},
|
||||||
|
"options": {"type": "object"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"mac_address": {"type": "string"},
|
||||||
|
"mem_limit": {"type": ["number", "string"]},
|
||||||
|
"memswap_limit": {"type": ["number", "string"]},
|
||||||
|
"network_mode": {"type": "string"},
|
||||||
|
|
||||||
|
"networks": {
|
||||||
|
"oneOf": [
|
||||||
|
{"$ref": "#/definitions/list_of_strings"},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9._-]+$": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"aliases": {"$ref": "#/definitions/list_of_strings"},
|
||||||
|
"ipv4_address": {"type": "string"},
|
||||||
|
"ipv6_address": {"type": "string"},
|
||||||
|
"link_local_ips": {"$ref": "#/definitions/list_of_strings"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{"type": "null"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pid": {"type": ["string", "null"]},
|
||||||
|
|
||||||
|
"ports": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": ["string", "number"],
|
||||||
|
"format": "ports"
|
||||||
|
},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"privileged": {"type": "boolean"},
|
||||||
|
"read_only": {"type": "boolean"},
|
||||||
|
"restart": {"type": "string"},
|
||||||
|
"security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"shm_size": {"type": ["number", "string"]},
|
||||||
|
"stdin_open": {"type": "boolean"},
|
||||||
|
"stop_signal": {"type": "string"},
|
||||||
|
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
||||||
|
"tty": {"type": "boolean"},
|
||||||
|
"ulimits": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z]+$": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "integer"},
|
||||||
|
{
|
||||||
|
"type":"object",
|
||||||
|
"properties": {
|
||||||
|
"hard": {"type": "integer"},
|
||||||
|
"soft": {"type": "integer"}
|
||||||
|
},
|
||||||
|
"required": ["soft", "hard"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {"type": "string"},
|
||||||
|
"volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"volume_driver": {"type": "string"},
|
||||||
|
"volumes_from": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||||
|
"working_dir": {"type": "string"}
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"memswap_limit": ["mem_limit"]
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"network": {
|
||||||
|
"id": "#/definitions/network",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"driver": {"type": "string"},
|
||||||
|
"driver_opts": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^.+$": {"type": ["string", "number"]}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ipam": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"driver": {"type": "string"},
|
||||||
|
"config": {
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"external": {
|
||||||
|
"type": ["boolean", "object"],
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"volume": {
|
||||||
|
"id": "#/definitions/volume",
|
||||||
|
"type": ["object", "null"],
|
||||||
|
"properties": {
|
||||||
|
"driver": {"type": "string"},
|
||||||
|
"driver_opts": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^.+$": {"type": ["string", "number"]}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"external": {
|
||||||
|
"type": ["boolean", "object"],
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"string_or_list": {
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"$ref": "#/definitions/list_of_strings"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"list_of_strings": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
"uniqueItems": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"list_or_dict": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".+": {
|
||||||
|
"type": ["string", "number", "null"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"constraints": {
|
||||||
|
"service": {
|
||||||
|
"id": "#/definitions/constraints/service",
|
||||||
|
"anyOf": [
|
||||||
|
{"required": ["build"]},
|
||||||
|
{"required": ["image"]}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"build": {
|
||||||
|
"required": ["context"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import yaml
|
|||||||
from compose.config import types
|
from compose.config import types
|
||||||
from compose.config.config import V1
|
from compose.config.config import V1
|
||||||
from compose.config.config import V2_0
|
from compose.config.config import V2_0
|
||||||
|
from compose.config.config import V2_1
|
||||||
|
|
||||||
|
|
||||||
def serialize_config_type(dumper, data):
|
def serialize_config_type(dumper, data):
|
||||||
@ -32,8 +33,12 @@ def denormalize_config(config):
|
|||||||
if 'external_name' in net_conf:
|
if 'external_name' in net_conf:
|
||||||
del net_conf['external_name']
|
del net_conf['external_name']
|
||||||
|
|
||||||
|
version = config.version
|
||||||
|
if version not in (V2_0, V2_1):
|
||||||
|
version = V2_1
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': V2_0,
|
'version': version,
|
||||||
'services': services,
|
'services': services,
|
||||||
'networks': networks,
|
'networks': networks,
|
||||||
'volumes': config.volumes,
|
'volumes': config.volumes,
|
||||||
|
@ -16,13 +16,16 @@ LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
|
|||||||
|
|
||||||
COMPOSEFILE_V1 = '1'
|
COMPOSEFILE_V1 = '1'
|
||||||
COMPOSEFILE_V2_0 = '2.0'
|
COMPOSEFILE_V2_0 = '2.0'
|
||||||
|
COMPOSEFILE_V2_1 = '2.1'
|
||||||
|
|
||||||
API_VERSIONS = {
|
API_VERSIONS = {
|
||||||
COMPOSEFILE_V1: '1.21',
|
COMPOSEFILE_V1: '1.21',
|
||||||
COMPOSEFILE_V2_0: '1.22',
|
COMPOSEFILE_V2_0: '1.22',
|
||||||
|
COMPOSEFILE_V2_1: '1.24',
|
||||||
}
|
}
|
||||||
|
|
||||||
API_VERSION_TO_ENGINE_VERSION = {
|
API_VERSION_TO_ENGINE_VERSION = {
|
||||||
API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
|
API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
|
||||||
API_VERSIONS[COMPOSEFILE_V2_0]: '1.10.0'
|
API_VERSIONS[COMPOSEFILE_V2_0]: '1.10.0',
|
||||||
|
API_VERSIONS[COMPOSEFILE_V2_1]: '1.12.0',
|
||||||
}
|
}
|
||||||
|
@ -478,7 +478,9 @@ class Service(object):
|
|||||||
aliases=self._get_aliases(netdefs, container),
|
aliases=self._get_aliases(netdefs, container),
|
||||||
ipv4_address=netdefs.get('ipv4_address', None),
|
ipv4_address=netdefs.get('ipv4_address', None),
|
||||||
ipv6_address=netdefs.get('ipv6_address', None),
|
ipv6_address=netdefs.get('ipv6_address', None),
|
||||||
links=self._get_links(False))
|
links=self._get_links(False),
|
||||||
|
link_local_ips=netdefs.get('link_local_ips', None),
|
||||||
|
)
|
||||||
|
|
||||||
def remove_duplicate_containers(self, timeout=DEFAULT_TIMEOUT):
|
def remove_duplicate_containers(self, timeout=DEFAULT_TIMEOUT):
|
||||||
for c in self.duplicate_containers():
|
for c in self.duplicate_containers():
|
||||||
|
@ -621,6 +621,31 @@ An example:
|
|||||||
- subnet: 2001:3984:3989::/64
|
- subnet: 2001:3984:3989::/64
|
||||||
gateway: 2001:3984:3989::1
|
gateway: 2001:3984:3989::1
|
||||||
|
|
||||||
|
#### link_local_ips
|
||||||
|
|
||||||
|
> [Added in version 2.1 file format](#version-21).
|
||||||
|
|
||||||
|
Specify a list of link-local IPs. Link-local IPs are special IPs which belong
|
||||||
|
to a well known subnet and are purely managed by the operator, usually
|
||||||
|
dependent on the architecture where they are deployed. Therefore they are not
|
||||||
|
managed by docker (IPAM driver).
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
version: '2.1'
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: busybox
|
||||||
|
command: top
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
link_local_ips:
|
||||||
|
- 57.123.22.11
|
||||||
|
- 57.123.22.13
|
||||||
|
networks:
|
||||||
|
app_net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
### pid
|
### pid
|
||||||
|
|
||||||
pid: "host"
|
pid: "host"
|
||||||
@ -1054,6 +1079,15 @@ A more extended example, defining volumes and networks:
|
|||||||
back-tier:
|
back-tier:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
|
### Version 2.1
|
||||||
|
|
||||||
|
An upgrade of [version 2](#version-2) that introduces new parameters only
|
||||||
|
available with Docker Engine version **1.12.0+**
|
||||||
|
|
||||||
|
Introduces:
|
||||||
|
|
||||||
|
- [`link_local_ips`](#link_local_ips)
|
||||||
|
- ...
|
||||||
|
|
||||||
### Upgrading
|
### Upgrading
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ class CLITestCase(DockerClientTestCase):
|
|||||||
self.base_dir = 'tests/fixtures/v1-config'
|
self.base_dir = 'tests/fixtures/v1-config'
|
||||||
result = self.dispatch(['config'])
|
result = self.dispatch(['config'])
|
||||||
assert yaml.load(result.stdout) == {
|
assert yaml.load(result.stdout) == {
|
||||||
'version': '2.0',
|
'version': '2.1',
|
||||||
'services': {
|
'services': {
|
||||||
'net': {
|
'net': {
|
||||||
'image': 'busybox',
|
'image': 'busybox',
|
||||||
|
@ -13,6 +13,7 @@ from .testcases import DockerClientTestCase
|
|||||||
from compose.config import config
|
from compose.config import config
|
||||||
from compose.config import ConfigurationError
|
from compose.config import ConfigurationError
|
||||||
from compose.config.config import V2_0
|
from compose.config.config import V2_0
|
||||||
|
from compose.config.config import V2_1
|
||||||
from compose.config.types import VolumeFromSpec
|
from compose.config.types import VolumeFromSpec
|
||||||
from compose.config.types import VolumeSpec
|
from compose.config.types import VolumeSpec
|
||||||
from compose.const import LABEL_PROJECT
|
from compose.const import LABEL_PROJECT
|
||||||
@ -21,6 +22,7 @@ from compose.container import Container
|
|||||||
from compose.project import Project
|
from compose.project import Project
|
||||||
from compose.project import ProjectError
|
from compose.project import ProjectError
|
||||||
from compose.service import ConvergenceStrategy
|
from compose.service import ConvergenceStrategy
|
||||||
|
from tests.integration.testcases import v2_1_only
|
||||||
from tests.integration.testcases import v2_only
|
from tests.integration.testcases import v2_only
|
||||||
|
|
||||||
|
|
||||||
@ -756,6 +758,42 @@ class ProjectTest(DockerClientTestCase):
|
|||||||
with self.assertRaises(ProjectError):
|
with self.assertRaises(ProjectError):
|
||||||
project.up()
|
project.up()
|
||||||
|
|
||||||
|
@v2_1_only()
|
||||||
|
def test_up_with_network_link_local_ips(self):
|
||||||
|
config_data = config.Config(
|
||||||
|
version=V2_1,
|
||||||
|
services=[{
|
||||||
|
'name': 'web',
|
||||||
|
'image': 'busybox:latest',
|
||||||
|
'networks': {
|
||||||
|
'linklocaltest': {
|
||||||
|
'link_local_ips': ['169.254.8.8']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
volumes={},
|
||||||
|
networks={
|
||||||
|
'linklocaltest': {'driver': 'bridge'}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
project = Project.from_config(
|
||||||
|
client=self.client,
|
||||||
|
name='composetest',
|
||||||
|
config_data=config_data
|
||||||
|
)
|
||||||
|
project.up()
|
||||||
|
|
||||||
|
service_container = project.get_service('web').containers()[0]
|
||||||
|
ipam_config = service_container.inspect().get(
|
||||||
|
'NetworkSettings', {}
|
||||||
|
).get(
|
||||||
|
'Networks', {}
|
||||||
|
).get(
|
||||||
|
'composetest_linklocaltest', {}
|
||||||
|
).get('IPAMConfig', {})
|
||||||
|
assert 'LinkLocalIPs' in ipam_config
|
||||||
|
assert ipam_config['LinkLocalIPs'] == ['169.254.8.8']
|
||||||
|
|
||||||
@v2_only()
|
@v2_only()
|
||||||
def test_project_up_with_network_internal(self):
|
def test_project_up_with_network_internal(self):
|
||||||
self.require_api_version('1.23')
|
self.require_api_version('1.23')
|
||||||
|
@ -12,6 +12,7 @@ from compose.cli.docker_client import docker_client
|
|||||||
from compose.config.config import resolve_environment
|
from compose.config.config import resolve_environment
|
||||||
from compose.config.config import V1
|
from compose.config.config import V1
|
||||||
from compose.config.config import V2_0
|
from compose.config.config import V2_0
|
||||||
|
from compose.config.config import V2_1
|
||||||
from compose.config.environment import Environment
|
from compose.config.environment import Environment
|
||||||
from compose.const import API_VERSIONS
|
from compose.const import API_VERSIONS
|
||||||
from compose.const import LABEL_PROJECT
|
from compose.const import LABEL_PROJECT
|
||||||
@ -33,18 +34,22 @@ def get_links(container):
|
|||||||
return [format_link(link) for link in links]
|
return [format_link(link) for link in links]
|
||||||
|
|
||||||
|
|
||||||
def engine_version_too_low_for_v2():
|
def engine_max_version():
|
||||||
if 'DOCKER_VERSION' not in os.environ:
|
if 'DOCKER_VERSION' not in os.environ:
|
||||||
return False
|
return V2_1
|
||||||
version = os.environ['DOCKER_VERSION'].partition('-')[0]
|
version = os.environ['DOCKER_VERSION'].partition('-')[0]
|
||||||
return version_lt(version, '1.10')
|
if version_lt(version, '1.10'):
|
||||||
|
return V1
|
||||||
|
elif version_lt(version, '1.12'):
|
||||||
|
return V2_0
|
||||||
|
return V2_1
|
||||||
|
|
||||||
|
|
||||||
def v2_only():
|
def v2_only():
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
if engine_version_too_low_for_v2():
|
if engine_max_version() == V1:
|
||||||
skip("Engine version is too low")
|
skip("Engine version is too low")
|
||||||
return
|
return
|
||||||
return f(self, *args, **kwargs)
|
return f(self, *args, **kwargs)
|
||||||
@ -53,14 +58,23 @@ def v2_only():
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def v2_1_only():
|
||||||
|
def decorator(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
if engine_max_version() in (V1, V2_0):
|
||||||
|
skip('Engine version is too low')
|
||||||
|
return
|
||||||
|
return f(self, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
class DockerClientTestCase(unittest.TestCase):
|
class DockerClientTestCase(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
if engine_version_too_low_for_v2():
|
version = API_VERSIONS[engine_max_version()]
|
||||||
version = API_VERSIONS[V1]
|
|
||||||
else:
|
|
||||||
version = API_VERSIONS[V2_0]
|
|
||||||
|
|
||||||
cls.client = docker_client(Environment(), version)
|
cls.client = docker_client(Environment(), version)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -17,6 +17,7 @@ from compose.config.config import resolve_build_args
|
|||||||
from compose.config.config import resolve_environment
|
from compose.config.config import resolve_environment
|
||||||
from compose.config.config import V1
|
from compose.config.config import V1
|
||||||
from compose.config.config import V2_0
|
from compose.config.config import V2_0
|
||||||
|
from compose.config.config import V2_1
|
||||||
from compose.config.environment import Environment
|
from compose.config.environment import Environment
|
||||||
from compose.config.errors import ConfigurationError
|
from compose.config.errors import ConfigurationError
|
||||||
from compose.config.errors import VERSION_EXPLANATION
|
from compose.config.errors import VERSION_EXPLANATION
|
||||||
@ -155,6 +156,8 @@ class ConfigTest(unittest.TestCase):
|
|||||||
for version in ['2', '2.0']:
|
for version in ['2', '2.0']:
|
||||||
cfg = config.load(build_config_details({'version': version}))
|
cfg = config.load(build_config_details({'version': version}))
|
||||||
assert cfg.version == V2_0
|
assert cfg.version == V2_0
|
||||||
|
cfg = config.load(build_config_details({'version': '2.1'}))
|
||||||
|
assert cfg.version == V2_1
|
||||||
|
|
||||||
def test_v1_file_version(self):
|
def test_v1_file_version(self):
|
||||||
cfg = config.load(build_config_details({'web': {'image': 'busybox'}}))
|
cfg = config.load(build_config_details({'web': {'image': 'busybox'}}))
|
||||||
@ -182,7 +185,7 @@ class ConfigTest(unittest.TestCase):
|
|||||||
with pytest.raises(ConfigurationError) as excinfo:
|
with pytest.raises(ConfigurationError) as excinfo:
|
||||||
config.load(
|
config.load(
|
||||||
build_config_details(
|
build_config_details(
|
||||||
{'version': '2.1'},
|
{'version': '2.18'},
|
||||||
filename='filename.yml',
|
filename='filename.yml',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -344,6 +347,35 @@ class ConfigTest(unittest.TestCase):
|
|||||||
}, 'working_dir', 'filename.yml')
|
}, 'working_dir', 'filename.yml')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_load_config_link_local_ips_network(self):
|
||||||
|
base_file = config.ConfigFile(
|
||||||
|
'base.yaml',
|
||||||
|
{
|
||||||
|
'version': '2.1',
|
||||||
|
'services': {
|
||||||
|
'web': {
|
||||||
|
'image': 'example/web',
|
||||||
|
'networks': {
|
||||||
|
'foobar': {
|
||||||
|
'aliases': ['foo', 'bar'],
|
||||||
|
'link_local_ips': ['169.254.8.8']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'networks': {'foobar': {}}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
details = config.ConfigDetails('.', [base_file])
|
||||||
|
web_service = config.load(details).services[0]
|
||||||
|
assert web_service['networks'] == {
|
||||||
|
'foobar': {
|
||||||
|
'aliases': ['foo', 'bar'],
|
||||||
|
'link_local_ips': ['169.254.8.8']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def test_load_config_invalid_service_names(self):
|
def test_load_config_invalid_service_names(self):
|
||||||
for invalid_name in ['?not?allowed', ' ', '', '!', '/', '\xe2']:
|
for invalid_name in ['?not?allowed', ' ', '', '!', '/', '\xe2']:
|
||||||
with pytest.raises(ConfigurationError) as exc:
|
with pytest.raises(ConfigurationError) as exc:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user