mirror of https://github.com/docker/compose.git
Merge pull request #4147 from aanand/version-3.0
Support version 3.0 of the Compose file format
This commit is contained in:
commit
586443ba5d
|
@ -15,6 +15,7 @@ from cached_property import cached_property
|
|||
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 ..utils import build_string_dict
|
||||
from ..utils import splitdrive
|
||||
from .environment import env_vars_from_file
|
||||
|
@ -175,7 +176,10 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
|
|||
if version == '2':
|
||||
version = V2_0
|
||||
|
||||
if version not in (V2_0, V2_1):
|
||||
if version == '3':
|
||||
version = V3_0
|
||||
|
||||
if version not in (V2_0, V2_1, V3_0):
|
||||
raise ConfigurationError(
|
||||
'Version in "{}" is unsupported. {}'
|
||||
.format(self.filename, VERSION_EXPLANATION))
|
||||
|
@ -326,6 +330,14 @@ def load(config_details):
|
|||
for service_dict in service_dicts:
|
||||
match_named_volumes(service_dict, volumes)
|
||||
|
||||
services_using_deploy = [s for s in service_dicts if s.get('deploy')]
|
||||
if services_using_deploy:
|
||||
log.warn(
|
||||
"Some services ({}) use the 'deploy' key, which will be ignored. "
|
||||
"Compose does not support deploy configuration - use the experimental "
|
||||
"`docker deploy` command to deploy to a swarm."
|
||||
.format(", ".join(sorted(s['name'] for s in services_using_deploy))))
|
||||
|
||||
return Config(main_file.version, service_dicts, volumes, networks)
|
||||
|
||||
|
||||
|
@ -433,7 +445,7 @@ def process_config_file(config_file, environment, service_name=None):
|
|||
'service',
|
||||
environment)
|
||||
|
||||
if config_file.version in (V2_0, V2_1):
|
||||
if config_file.version in (V2_0, V2_1, V3_0):
|
||||
processed_config = dict(config_file.config)
|
||||
processed_config['services'] = services
|
||||
processed_config['volumes'] = interpolate_config_section(
|
||||
|
@ -446,9 +458,10 @@ def process_config_file(config_file, environment, service_name=None):
|
|||
config_file.get_networks(),
|
||||
'network',
|
||||
environment)
|
||||
|
||||
if config_file.version == V1:
|
||||
elif config_file.version == V1:
|
||||
processed_config = services
|
||||
else:
|
||||
raise Exception("Unsupported version: {}".format(repr(config_file.version)))
|
||||
|
||||
config_file = config_file._replace(config=processed_config)
|
||||
validate_against_config_schema(config_file)
|
||||
|
|
|
@ -0,0 +1,378 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "config_schema_v3.0.json",
|
||||
"type": "object",
|
||||
"required": ["version"],
|
||||
|
||||
"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": {
|
||||
"deploy": {"$ref": "#/definitions/deployment"},
|
||||
"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"},
|
||||
"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
|
||||
},
|
||||
|
||||
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||
"healthcheck": {"$ref": "#/definitions/healthcheck"},
|
||||
"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",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number", "null"]}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"mac_address": {"type": "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"}
|
||||
},
|
||||
"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"},
|
||||
"stop_grace_period": {"type": "string", "format": "duration"},
|
||||
"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},
|
||||
"working_dir": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"healthcheck": {
|
||||
"id": "#/definitions/healthcheck",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"interval": {"type":"string"},
|
||||
"timeout": {"type":"string"},
|
||||
"retries": {"type": "number"},
|
||||
"command": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"deployment": {
|
||||
"id": "#/definitions/deployment",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"replicas": {"type": "integer"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"update_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"resources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limits": {"$ref": "#/definitions/resource"},
|
||||
"reservations": {"$ref": "#/definitions/resource"}
|
||||
}
|
||||
},
|
||||
"restart_policy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"condition": {"type": "string"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"max_attempts": {"type": "integer"},
|
||||
"window": {"type": "string", "format": "duration"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"constraints": {"type": "array", "items": {"type": "string"}}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"resource": {
|
||||
"id": "#/definitions/resource",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpus": {"type": "string"},
|
||||
"memory": {"type": "string"}
|
||||
},
|
||||
"additionaProperties": false
|
||||
},
|
||||
|
||||
"network": {
|
||||
"id": "#/definitions/network",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"ipam": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"config": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subnet": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"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"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ from __future__ import unicode_literals
|
|||
|
||||
|
||||
VERSION_EXPLANATION = (
|
||||
'You might be seeing this error because you\'re using the wrong Compose '
|
||||
'file version. Either specify a version of "2" (or "2.0") and place your '
|
||||
'You might be seeing this error because you\'re using the wrong Compose file version. '
|
||||
'Either specify a supported version ("2.0", "2.1", "3.0") and place your '
|
||||
'service definitions under the `services` key, or omit the `version` key '
|
||||
'and place your service definitions at the root of the file to use '
|
||||
'version 1.\nFor more on the Compose file format versions, see '
|
||||
|
|
|
@ -6,7 +6,6 @@ import yaml
|
|||
|
||||
from compose.config import types
|
||||
from compose.config.config import V1
|
||||
from compose.config.config import V2_0
|
||||
from compose.config.config import V2_1
|
||||
|
||||
|
||||
|
@ -34,7 +33,7 @@ def denormalize_config(config):
|
|||
del net_conf['external_name']
|
||||
|
||||
version = config.version
|
||||
if version not in (V2_0, V2_1):
|
||||
if version == V1:
|
||||
version = V2_1
|
||||
|
||||
return {
|
||||
|
|
|
@ -17,15 +17,18 @@ LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
|
|||
COMPOSEFILE_V1 = '1'
|
||||
COMPOSEFILE_V2_0 = '2.0'
|
||||
COMPOSEFILE_V2_1 = '2.1'
|
||||
COMPOSEFILE_V3_0 = '3.0'
|
||||
|
||||
API_VERSIONS = {
|
||||
COMPOSEFILE_V1: '1.21',
|
||||
COMPOSEFILE_V2_0: '1.22',
|
||||
COMPOSEFILE_V2_1: '1.24',
|
||||
COMPOSEFILE_V3_0: '1.24',
|
||||
}
|
||||
|
||||
API_VERSION_TO_ENGINE_VERSION = {
|
||||
API_VERSIONS[COMPOSEFILE_V1]: '1.9.0',
|
||||
API_VERSIONS[COMPOSEFILE_V2_0]: '1.10.0',
|
||||
API_VERSIONS[COMPOSEFILE_V2_1]: '1.12.0',
|
||||
API_VERSIONS[COMPOSEFILE_V3_0]: '1.12.0',
|
||||
}
|
||||
|
|
|
@ -285,6 +285,61 @@ class CLITestCase(DockerClientTestCase):
|
|||
'volumes': {},
|
||||
}
|
||||
|
||||
def test_config_v3(self):
|
||||
self.base_dir = 'tests/fixtures/v3-full'
|
||||
result = self.dispatch(['config'])
|
||||
|
||||
assert yaml.load(result.stdout) == {
|
||||
'version': '3.0',
|
||||
'networks': {},
|
||||
'volumes': {},
|
||||
'services': {
|
||||
'web': {
|
||||
'image': 'busybox',
|
||||
'deploy': {
|
||||
'mode': 'replicated',
|
||||
'replicas': 6,
|
||||
'labels': ['FOO=BAR'],
|
||||
'update_config': {
|
||||
'parallelism': 3,
|
||||
'delay': '10s',
|
||||
'failure_action': 'continue',
|
||||
'monitor': '60s',
|
||||
'max_failure_ratio': 0.3,
|
||||
},
|
||||
'resources': {
|
||||
'limits': {
|
||||
'cpus': '0.001',
|
||||
'memory': '50M',
|
||||
},
|
||||
'reservations': {
|
||||
'cpus': '0.0001',
|
||||
'memory': '20M',
|
||||
},
|
||||
},
|
||||
'restart_policy': {
|
||||
'condition': 'on_failure',
|
||||
'delay': '5s',
|
||||
'max_attempts': 3,
|
||||
'window': '120s',
|
||||
},
|
||||
'placement': {
|
||||
'constraints': ['node=foo'],
|
||||
},
|
||||
},
|
||||
|
||||
'healthcheck': {
|
||||
'command': 'cat /etc/passwd',
|
||||
'interval': '10s',
|
||||
'timeout': '1s',
|
||||
'retries': 5,
|
||||
},
|
||||
|
||||
'stop_grace_period': '20s',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def test_ps(self):
|
||||
self.project.get_service('simple').create_container()
|
||||
result = self.dispatch(['ps'])
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
version: "3"
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 6
|
||||
labels: [FOO=BAR]
|
||||
update_config:
|
||||
parallelism: 3
|
||||
delay: 10s
|
||||
failure_action: continue
|
||||
monitor: 60s
|
||||
max_failure_ratio: 0.3
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.001'
|
||||
memory: 50M
|
||||
reservations:
|
||||
cpus: '0.0001'
|
||||
memory: 20M
|
||||
restart_policy:
|
||||
condition: on_failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
placement:
|
||||
constraints: [node=foo]
|
||||
|
||||
healthcheck:
|
||||
command: cat /etc/passwd
|
||||
interval: 10s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
|
||||
stop_grace_period: 20s
|
|
@ -18,6 +18,7 @@ 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.config.errors import ConfigurationError
|
||||
from compose.config.errors import VERSION_EXPLANATION
|
||||
|
@ -156,9 +157,14 @@ class ConfigTest(unittest.TestCase):
|
|||
for version in ['2', '2.0']:
|
||||
cfg = config.load(build_config_details({'version': version}))
|
||||
assert cfg.version == V2_0
|
||||
|
||||
cfg = config.load(build_config_details({'version': '2.1'}))
|
||||
assert cfg.version == V2_1
|
||||
|
||||
for version in ['3', '3.0']:
|
||||
cfg = config.load(build_config_details({'version': version}))
|
||||
assert cfg.version == V3_0
|
||||
|
||||
def test_v1_file_version(self):
|
||||
cfg = config.load(build_config_details({'web': {'image': 'busybox'}}))
|
||||
assert cfg.version == V1
|
||||
|
|
Loading…
Reference in New Issue