Make 'version' a string

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
This commit is contained in:
Aanand Prasad 2016-01-29 15:58:38 +00:00
parent a8de582425
commit aeef61fcd8
27 changed files with 160 additions and 98 deletions

View File

@ -14,6 +14,8 @@ import six
import yaml import yaml
from cached_property import cached_property 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_VERSIONS from ..const import COMPOSEFILE_VERSIONS
from .errors import CircularReference from .errors import CircularReference
from .errors import ComposeFileNotFound from .errors import ComposeFileNotFound
@ -129,25 +131,34 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
@cached_property @cached_property
def version(self): def version(self):
version = self.config.get('version', 1) version = self.config.get('version', V1)
if isinstance(version, dict): if isinstance(version, dict):
log.warn("Unexpected type for field 'version', in file {} assuming " log.warn("Unexpected type for field 'version', in file {} assuming "
"version is the name of a service, and defaulting to " "version is the name of a service, and defaulting to "
"Compose file version 1".format(self.filename)) "Compose file version 1".format(self.filename))
return 1 return V1
if version == '2':
version = V2_0
if version not in COMPOSEFILE_VERSIONS:
raise ConfigurationError(
'Invalid Compose file version: {0}'.format(version))
return version return version
def get_service(self, name): def get_service(self, name):
return self.get_service_dicts()[name] return self.get_service_dicts()[name]
def get_service_dicts(self): def get_service_dicts(self):
return self.config if self.version == 1 else self.config.get('services', {}) return self.config if self.version == V1 else self.config.get('services', {})
def get_volumes(self): def get_volumes(self):
return {} if self.version == 1 else self.config.get('volumes', {}) return {} if self.version == V1 else self.config.get('volumes', {})
def get_networks(self): def get_networks(self):
return {} if self.version == 1 else self.config.get('networks', {}) return {} if self.version == V1 else self.config.get('networks', {})
class Config(namedtuple('_Config', 'version services volumes networks')): class Config(namedtuple('_Config', 'version services volumes networks')):
@ -209,10 +220,6 @@ def validate_config_version(config_files):
next_file.filename, next_file.filename,
next_file.version)) next_file.version))
if main_file.version not in COMPOSEFILE_VERSIONS:
raise ConfigurationError(
'Invalid Compose file version: {0}'.format(main_file.version))
def get_default_config_files(base_dir): def get_default_config_files(base_dir):
(candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, base_dir) (candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, base_dir)
@ -276,7 +283,7 @@ def load(config_details):
main_file, main_file,
[file.get_service_dicts() for file in config_details.config_files]) [file.get_service_dicts() for file in config_details.config_files])
if main_file.version >= 2: if main_file.version != V1:
for service_dict in service_dicts: for service_dict in service_dicts:
match_named_volumes(service_dict, volumes) match_named_volumes(service_dict, volumes)
@ -361,7 +368,7 @@ def process_config_file(config_file, service_name=None):
interpolated_config = interpolate_environment_variables(service_dicts, 'service') interpolated_config = interpolate_environment_variables(service_dicts, 'service')
if config_file.version == 2: if config_file.version == V2_0:
processed_config = dict(config_file.config) processed_config = dict(config_file.config)
processed_config['services'] = services = interpolated_config processed_config['services'] = services = interpolated_config
processed_config['volumes'] = interpolate_environment_variables( processed_config['volumes'] = interpolate_environment_variables(
@ -369,7 +376,7 @@ def process_config_file(config_file, service_name=None):
processed_config['networks'] = interpolate_environment_variables( processed_config['networks'] = interpolate_environment_variables(
config_file.get_networks(), 'network') config_file.get_networks(), 'network')
if config_file.version == 1: if config_file.version == V1:
processed_config = services = interpolated_config processed_config = services = interpolated_config
config_file = config_file._replace(config=processed_config) config_file = config_file._replace(config=processed_config)
@ -653,7 +660,7 @@ def merge_service_dicts(base, override, version):
if field in base or field in override: if field in base or field in override:
d[field] = override.get(field, base.get(field)) d[field] = override.get(field, base.get(field))
if version == 1: if version == V1:
legacy_v1_merge_image_or_build(d, base, override) legacy_v1_merge_image_or_build(d, base, override)
else: else:
merge_build(d, base, override) merge_build(d, base, override)

View File

@ -1,18 +1,18 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"type": "object", "type": "object",
"id": "fields_schema_v2.json", "id": "fields_schema_v2.0.json",
"properties": { "properties": {
"version": { "version": {
"enum": [2] "type": "string"
}, },
"services": { "services": {
"id": "#/properties/services", "id": "#/properties/services",
"type": "object", "type": "object",
"patternProperties": { "patternProperties": {
"^[a-zA-Z0-9._-]+$": { "^[a-zA-Z0-9._-]+$": {
"$ref": "service_schema_v2.json#/definitions/service" "$ref": "service_schema_v2.0.json#/definitions/service"
} }
}, },
"additionalProperties": false "additionalProperties": false

View File

@ -1,6 +1,6 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"id": "service_schema_v2.json", "id": "service_schema_v2.0.json",
"type": "object", "type": "object",

View File

@ -7,6 +7,7 @@ from __future__ import unicode_literals
import os import os
from collections import namedtuple from collections import namedtuple
from compose.config.config import V1
from compose.config.errors import ConfigurationError from compose.config.errors import ConfigurationError
from compose.const import IS_WINDOWS_PLATFORM from compose.const import IS_WINDOWS_PLATFORM
@ -16,7 +17,7 @@ class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode type')):
# TODO: drop service_names arg when v1 is removed # TODO: drop service_names arg when v1 is removed
@classmethod @classmethod
def parse(cls, volume_from_config, service_names, version): def parse(cls, volume_from_config, service_names, version):
func = cls.parse_v1 if version == 1 else cls.parse_v2 func = cls.parse_v1 if version == V1 else cls.parse_v2
return func(service_names, volume_from_config) return func(service_names, volume_from_config)
@classmethod @classmethod

View File

@ -14,9 +14,12 @@ LABEL_PROJECT = 'com.docker.compose.project'
LABEL_SERVICE = 'com.docker.compose.service' LABEL_SERVICE = 'com.docker.compose.service'
LABEL_VERSION = 'com.docker.compose.version' LABEL_VERSION = 'com.docker.compose.version'
LABEL_CONFIG_HASH = 'com.docker.compose.config-hash' LABEL_CONFIG_HASH = 'com.docker.compose.config-hash'
COMPOSEFILE_VERSIONS = (1, 2)
COMPOSEFILE_V1 = '1'
COMPOSEFILE_V2_0 = '2.0'
COMPOSEFILE_VERSIONS = (COMPOSEFILE_V1, COMPOSEFILE_V2_0)
API_VERSIONS = { API_VERSIONS = {
1: '1.21', COMPOSEFILE_V1: '1.21',
2: '1.22', COMPOSEFILE_V2_0: '1.22',
} }

View File

@ -10,6 +10,7 @@ from docker.errors import NotFound
from . import parallel from . import parallel
from .config import ConfigurationError from .config import ConfigurationError
from .config.config import V1
from .config.sort_services import get_container_name_from_network_mode from .config.sort_services import get_container_name_from_network_mode
from .config.sort_services import get_service_name_from_network_mode from .config.sort_services import get_service_name_from_network_mode
from .const import DEFAULT_TIMEOUT from .const import DEFAULT_TIMEOUT
@ -56,7 +57,7 @@ class Project(object):
""" """
Construct a Project from a config.Config object. Construct a Project from a config.Config object.
""" """
use_networking = (config_data.version and config_data.version >= 2) use_networking = (config_data.version and config_data.version != V1)
project = cls(name, [], client, use_networking=use_networking) project = cls(name, [], client, use_networking=use_networking)
network_config = config_data.networks or {} network_config = config_data.networks or {}
@ -94,7 +95,7 @@ class Project(object):
network_mode = project.get_network_mode(service_dict, networks) network_mode = project.get_network_mode(service_dict, networks)
volumes_from = get_volumes_from(project, service_dict) volumes_from = get_volumes_from(project, service_dict)
if config_data.version == 2: if config_data.version != V1:
service_volumes = service_dict.get('volumes', []) service_volumes = service_dict.get('volumes', [])
for volume_spec in service_volumes: for volume_spec in service_volumes:
if volume_spec.is_named_volume: if volume_spec.is_named_volume:

View File

@ -23,8 +23,8 @@ exe = EXE(pyz,
'DATA' 'DATA'
), ),
( (
'compose/config/fields_schema_v2.json', 'compose/config/fields_schema_v2.0.json',
'compose/config/fields_schema_v2.json', 'compose/config/fields_schema_v2.0.json',
'DATA' 'DATA'
), ),
( (
@ -33,8 +33,8 @@ exe = EXE(pyz,
'DATA' 'DATA'
), ),
( (
'compose/config/service_schema_v2.json', 'compose/config/service_schema_v2.0.json',
'compose/config/service_schema_v2.json', 'compose/config/service_schema_v2.0.json',
'DATA' 'DATA'
), ),
( (

View File

@ -177,7 +177,7 @@ class CLITestCase(DockerClientTestCase):
output = yaml.load(result.stdout) output = yaml.load(result.stdout)
expected = { expected = {
'version': 2, 'version': '2.0',
'volumes': {'data': {'driver': 'local'}}, 'volumes': {'data': {'driver': 'local'}},
'networks': {'front': {}}, 'networks': {'front': {}},
'services': { 'services': {

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
myweb: myweb:
build: '.' build: '.'

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
simple: simple:
image: busybox:latest image: busybox:latest

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
foo: foo:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
web: web:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
simple: simple:
image: busybox:latest image: busybox:latest

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
web: web:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
simple: simple:
image: busybox:latest image: busybox:latest

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
web: web:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
web: web:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
bridge: bridge:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
networks: networks:
foo: {} foo: {}

View File

@ -1,5 +1,5 @@
version: 2 version: "2"
services: services:
simple: simple:

View File

@ -1,5 +1,5 @@
version: 2 version: "2"
volumes: volumes:
data: data:

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
simple: simple:
image: busybox:latest image: busybox:latest

View File

@ -1,4 +1,4 @@
version: 2 version: "2"
services: services:
simple: simple:
image: busybox:latest image: busybox:latest

View File

@ -10,6 +10,7 @@ from docker.errors import NotFound
from .testcases import DockerClientTestCase 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.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
@ -112,7 +113,7 @@ class ProjectTest(DockerClientTestCase):
name='composetest', name='composetest',
client=self.client, client=self.client,
config_data=build_service_dicts({ config_data=build_service_dicts({
'version': 2, 'version': V2_0,
'services': { 'services': {
'net': { 'net': {
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -139,7 +140,7 @@ class ProjectTest(DockerClientTestCase):
return Project.from_config( return Project.from_config(
name='composetest', name='composetest',
config_data=build_service_dicts({ config_data=build_service_dicts({
'version': 2, 'version': V2_0,
'services': { 'services': {
'web': { 'web': {
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -559,7 +560,7 @@ class ProjectTest(DockerClientTestCase):
@v2_only() @v2_only()
def test_project_up_networks(self): def test_project_up_networks(self):
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -592,7 +593,7 @@ class ProjectTest(DockerClientTestCase):
@v2_only() @v2_only()
def test_up_with_ipam_config(self): def test_up_with_ipam_config(self):
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[], services=[],
volumes={}, volumes={},
networks={ networks={
@ -651,7 +652,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32)) vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name) full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -677,7 +678,7 @@ class ProjectTest(DockerClientTestCase):
base_file = config.ConfigFile( base_file = config.ConfigFile(
'base.yml', 'base.yml',
{ {
'version': 2, 'version': V2_0,
'services': { 'services': {
'simple': {'image': 'busybox:latest', 'command': 'top'}, 'simple': {'image': 'busybox:latest', 'command': 'top'},
'another': { 'another': {
@ -696,7 +697,7 @@ class ProjectTest(DockerClientTestCase):
override_file = config.ConfigFile( override_file = config.ConfigFile(
'override.yml', 'override.yml',
{ {
'version': 2, 'version': V2_0,
'services': { 'services': {
'another': { 'another': {
'logging': { 'logging': {
@ -729,7 +730,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32)) vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name) full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -754,7 +755,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32)) vol_name = '{0:x}'.format(random.getrandbits(32))
full_vol_name = 'composetest_{0}'.format(vol_name) full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -779,7 +780,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32)) vol_name = '{0:x}'.format(random.getrandbits(32))
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -802,7 +803,7 @@ class ProjectTest(DockerClientTestCase):
full_vol_name = 'composetest_{0}'.format(vol_name) full_vol_name = 'composetest_{0}'.format(vol_name)
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -841,7 +842,7 @@ class ProjectTest(DockerClientTestCase):
full_vol_name = 'composetest_{0}'.format(vol_name) full_vol_name = 'composetest_{0}'.format(vol_name)
self.client.create_volume(vol_name) self.client.create_volume(vol_name)
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -866,7 +867,7 @@ class ProjectTest(DockerClientTestCase):
vol_name = '{0:x}'.format(random.getrandbits(32)) vol_name = '{0:x}'.format(random.getrandbits(32))
config_data = config.Config( config_data = config.Config(
version=2, version=V2_0,
services=[{ services=[{
'name': 'web', 'name': 'web',
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -895,7 +896,7 @@ class ProjectTest(DockerClientTestCase):
base_file = config.ConfigFile( base_file = config.ConfigFile(
'base.yml', 'base.yml',
{ {
'version': 2, 'version': V2_0,
'services': { 'services': {
'simple': { 'simple': {
'image': 'busybox:latest', 'image': 'busybox:latest',

View File

@ -10,6 +10,8 @@ from pytest import skip
from .. import unittest from .. import unittest
from compose.cli.docker_client import docker_client 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 V2_0
from compose.const import API_VERSIONS from compose.const import API_VERSIONS
from compose.const import LABEL_PROJECT from compose.const import LABEL_PROJECT
from compose.progress_stream import stream_output from compose.progress_stream import stream_output
@ -54,9 +56,9 @@ class DockerClientTestCase(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
if engine_version_too_low_for_v2(): if engine_version_too_low_for_v2():
version = API_VERSIONS[1] version = API_VERSIONS[V1]
else: else:
version = API_VERSIONS[2] version = API_VERSIONS[V2_0]
cls.client = docker_client(version) cls.client = docker_client(version)

View File

@ -14,14 +14,15 @@ import pytest
from compose.config import config from compose.config import config
from compose.config.config import resolve_build_args 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 V2_0
from compose.config.errors import ConfigurationError from compose.config.errors import ConfigurationError
from compose.config.types import VolumeSpec from compose.config.types import VolumeSpec
from compose.const import IS_WINDOWS_PLATFORM from compose.const import IS_WINDOWS_PLATFORM
from tests import mock from tests import mock
from tests import unittest from tests import unittest
DEFAULT_VERSION = V2 = 2 DEFAULT_VERSION = V2_0
V1 = 1
def make_service_dict(name, service_dict, working_dir, filename=None): def make_service_dict(name, service_dict, working_dir, filename=None):
@ -78,7 +79,7 @@ class ConfigTest(unittest.TestCase):
def test_load_v2(self): def test_load_v2(self):
config_data = config.load( config_data = config.load(
build_config_details({ build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'foo': {'image': 'busybox'}, 'foo': {'image': 'busybox'},
'bar': {'image': 'busybox', 'environment': ['FOO=1']}, 'bar': {'image': 'busybox', 'environment': ['FOO=1']},
@ -143,9 +144,55 @@ class ConfigTest(unittest.TestCase):
} }
}) })
def test_valid_versions(self):
for version in ['2', '2.0']:
cfg = config.load(build_config_details({'version': version}))
assert cfg.version == V2_0
def test_v1_file_version(self):
cfg = config.load(build_config_details({'web': {'image': 'busybox'}}))
assert cfg.version == V1
assert list(s['name'] for s in cfg.services) == ['web']
cfg = config.load(build_config_details({'version': {'image': 'busybox'}}))
assert cfg.version == V1
assert list(s['name'] for s in cfg.services) == ['version']
def test_wrong_version_type(self):
for version in [None, 2, 2.0]:
with pytest.raises(ConfigurationError):
config.load(
build_config_details(
{'version': version},
filename='filename.yml',
)
)
def test_unsupported_version(self):
with pytest.raises(ConfigurationError):
config.load(
build_config_details(
{'version': '2.1'},
filename='filename.yml',
)
)
def test_v1_file_with_version_is_invalid(self):
for version in [1, "1"]:
with pytest.raises(ConfigurationError):
config.load(
build_config_details(
{
'version': version,
'web': {'image': 'busybox'},
},
filename='filename.yml',
)
)
def test_named_volume_config_empty(self): def test_named_volume_config_empty(self):
config_details = build_config_details({ config_details = build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'simple': {'image': 'busybox'} 'simple': {'image': 'busybox'}
}, },
@ -214,7 +261,7 @@ class ConfigTest(unittest.TestCase):
with self.assertRaises(ConfigurationError): with self.assertRaises(ConfigurationError):
config.load( config.load(
build_config_details( build_config_details(
{'version': 2, 'services': {'web': 'busybox:latest'}}, {'version': '2', 'services': {'web': 'busybox:latest'}},
'working_dir', 'working_dir',
'filename.yml' 'filename.yml'
) )
@ -224,7 +271,7 @@ class ConfigTest(unittest.TestCase):
with self.assertRaises(ConfigurationError): with self.assertRaises(ConfigurationError):
config.load( config.load(
build_config_details({ build_config_details({
'version': 2, 'version': '2',
'services': {'web': 'busybox:latest'}, 'services': {'web': 'busybox:latest'},
'networks': { 'networks': {
'invalid': {'foo', 'bar'} 'invalid': {'foo', 'bar'}
@ -246,7 +293,7 @@ class ConfigTest(unittest.TestCase):
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
config.load( config.load(
build_config_details({ build_config_details({
'version': 2, 'version': '2',
'services': {invalid_name: {'image': 'busybox'}} 'services': {invalid_name: {'image': 'busybox'}}
}, 'working_dir', 'filename.yml') }, 'working_dir', 'filename.yml')
) )
@ -256,7 +303,7 @@ class ConfigTest(unittest.TestCase):
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
config.load(build_config_details( config.load(build_config_details(
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': {'image': 'busybox', 'name': 'bogus'}, 'web': {'image': 'busybox', 'name': 'bogus'},
} }
@ -307,7 +354,7 @@ class ConfigTest(unittest.TestCase):
config.load( config.load(
build_config_details( build_config_details(
{ {
'version': 2, 'version': '2',
'services': {1: {'image': 'busybox'}} 'services': {1: {'image': 'busybox'}}
}, },
'working_dir', 'working_dir',
@ -370,7 +417,7 @@ class ConfigTest(unittest.TestCase):
def test_load_with_multiple_files_and_empty_override_v2(self): def test_load_with_multiple_files_and_empty_override_v2(self):
base_file = config.ConfigFile( base_file = config.ConfigFile(
'base.yml', 'base.yml',
{'version': 2, 'services': {'web': {'image': 'example/web'}}}) {'version': '2', 'services': {'web': {'image': 'example/web'}}})
override_file = config.ConfigFile('override.yml', None) override_file = config.ConfigFile('override.yml', None)
details = config.ConfigDetails('.', [base_file, override_file]) details = config.ConfigDetails('.', [base_file, override_file])
@ -394,7 +441,7 @@ class ConfigTest(unittest.TestCase):
base_file = config.ConfigFile('base.yml', None) base_file = config.ConfigFile('base.yml', None)
override_file = config.ConfigFile( override_file = config.ConfigFile(
'override.tml', 'override.tml',
{'version': 2, 'services': {'web': {'image': 'example/web'}}} {'version': '2', 'services': {'web': {'image': 'example/web'}}}
) )
details = config.ConfigDetails('.', [base_file, override_file]) details = config.ConfigDetails('.', [base_file, override_file])
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
@ -494,7 +541,7 @@ class ConfigTest(unittest.TestCase):
config.load( config.load(
build_config_details( build_config_details(
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'build': '.', 'build': '.',
@ -509,7 +556,7 @@ class ConfigTest(unittest.TestCase):
service = config.load( service = config.load(
build_config_details({ build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'build': '.' 'build': '.'
@ -522,7 +569,7 @@ class ConfigTest(unittest.TestCase):
service = config.load( service = config.load(
build_config_details( build_config_details(
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'build': { 'build': {
@ -543,7 +590,7 @@ class ConfigTest(unittest.TestCase):
base_file = config.ConfigFile( base_file = config.ConfigFile(
'base.yaml', 'base.yaml',
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'example/web', 'image': 'example/web',
@ -556,7 +603,7 @@ class ConfigTest(unittest.TestCase):
override_file = config.ConfigFile( override_file = config.ConfigFile(
'override.yaml', 'override.yaml',
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'build': '/', 'build': '/',
@ -585,7 +632,7 @@ class ConfigTest(unittest.TestCase):
base_file = config.ConfigFile( base_file = config.ConfigFile(
'base.yaml', 'base.yaml',
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -601,7 +648,7 @@ class ConfigTest(unittest.TestCase):
base_file = config.ConfigFile( base_file = config.ConfigFile(
'base.yaml', 'base.yaml',
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox:latest', 'image': 'busybox:latest',
@ -681,7 +728,7 @@ class ConfigTest(unittest.TestCase):
config.load( config.load(
build_config_details( build_config_details(
{ {
'version': 2, 'version': '2',
'services': { 'services': {
'foo': {'image': 1}, 'foo': {'image': 1},
}, },
@ -1016,7 +1063,7 @@ class ConfigTest(unittest.TestCase):
def test_external_volume_config(self): def test_external_volume_config(self):
config_details = build_config_details({ config_details = build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'bogus': {'image': 'busybox'} 'bogus': {'image': 'busybox'}
}, },
@ -1034,7 +1081,7 @@ class ConfigTest(unittest.TestCase):
def test_external_volume_invalid_config(self): def test_external_volume_invalid_config(self):
config_details = build_config_details({ config_details = build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'bogus': {'image': 'busybox'} 'bogus': {'image': 'busybox'}
}, },
@ -1047,7 +1094,7 @@ class ConfigTest(unittest.TestCase):
def test_depends_on_orders_services(self): def test_depends_on_orders_services(self):
config_details = build_config_details({ config_details = build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'one': {'image': 'busybox', 'depends_on': ['three', 'two']}, 'one': {'image': 'busybox', 'depends_on': ['three', 'two']},
'two': {'image': 'busybox', 'depends_on': ['three']}, 'two': {'image': 'busybox', 'depends_on': ['three']},
@ -1062,7 +1109,7 @@ class ConfigTest(unittest.TestCase):
def test_depends_on_unknown_service_errors(self): def test_depends_on_unknown_service_errors(self):
config_details = build_config_details({ config_details = build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'one': {'image': 'busybox', 'depends_on': ['three']}, 'one': {'image': 'busybox', 'depends_on': ['three']},
}, },
@ -1075,7 +1122,7 @@ class ConfigTest(unittest.TestCase):
class NetworkModeTest(unittest.TestCase): class NetworkModeTest(unittest.TestCase):
def test_network_mode_standard(self): def test_network_mode_standard(self):
config_data = config.load(build_config_details({ config_data = config.load(build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox', 'image': 'busybox',
@ -1101,7 +1148,7 @@ class NetworkModeTest(unittest.TestCase):
def test_network_mode_container(self): def test_network_mode_container(self):
config_data = config.load(build_config_details({ config_data = config.load(build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox', 'image': 'busybox',
@ -1126,7 +1173,7 @@ class NetworkModeTest(unittest.TestCase):
def test_network_mode_service(self): def test_network_mode_service(self):
config_data = config.load(build_config_details({ config_data = config.load(build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox', 'image': 'busybox',
@ -1160,7 +1207,7 @@ class NetworkModeTest(unittest.TestCase):
def test_network_mode_service_nonexistent(self): def test_network_mode_service_nonexistent(self):
with pytest.raises(ConfigurationError) as excinfo: with pytest.raises(ConfigurationError) as excinfo:
config.load(build_config_details({ config.load(build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox', 'image': 'busybox',
@ -1175,7 +1222,7 @@ class NetworkModeTest(unittest.TestCase):
def test_network_mode_plus_networks_is_invalid(self): def test_network_mode_plus_networks_is_invalid(self):
with pytest.raises(ConfigurationError) as excinfo: with pytest.raises(ConfigurationError) as excinfo:
config.load(build_config_details({ config.load(build_config_details({
'version': 2, 'version': '2',
'services': { 'services': {
'web': { 'web': {
'image': 'busybox', 'image': 'busybox',
@ -2202,7 +2249,7 @@ class ExtendsTest(unittest.TestCase):
tmpdir = py.test.ensuretemp('test_extends_with_mixed_version') tmpdir = py.test.ensuretemp('test_extends_with_mixed_version')
self.addCleanup(tmpdir.remove) self.addCleanup(tmpdir.remove)
tmpdir.join('docker-compose.yml').write(""" tmpdir.join('docker-compose.yml').write("""
version: 2 version: "2"
services: services:
web: web:
extends: extends:
@ -2224,7 +2271,7 @@ class ExtendsTest(unittest.TestCase):
tmpdir = py.test.ensuretemp('test_extends_with_defined_version') tmpdir = py.test.ensuretemp('test_extends_with_defined_version')
self.addCleanup(tmpdir.remove) self.addCleanup(tmpdir.remove)
tmpdir.join('docker-compose.yml').write(""" tmpdir.join('docker-compose.yml').write("""
version: 2 version: "2"
services: services:
web: web:
extends: extends:
@ -2233,7 +2280,7 @@ class ExtendsTest(unittest.TestCase):
image: busybox image: busybox
""") """)
tmpdir.join('base.yml').write(""" tmpdir.join('base.yml').write("""
version: 2 version: "2"
services: services:
base: base:
volumes: ['/foo'] volumes: ['/foo']

View File

@ -3,13 +3,13 @@ from __future__ import unicode_literals
import pytest import pytest
from compose.config.config import V1
from compose.config.config import V2_0
from compose.config.errors import ConfigurationError from compose.config.errors import ConfigurationError
from compose.config.types import parse_extra_hosts from compose.config.types import parse_extra_hosts
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 IS_WINDOWS_PLATFORM from compose.const import IS_WINDOWS_PLATFORM
from tests.unit.config.config_test import V1
from tests.unit.config.config_test import V2
def test_parse_extra_hosts_list(): def test_parse_extra_hosts_list():
@ -91,26 +91,26 @@ class TestVolumesFromSpec(object):
VolumeFromSpec.parse('unknown:format:ro', self.services, V1) VolumeFromSpec.parse('unknown:format:ro', self.services, V1)
def test_parse_v2_from_service(self): def test_parse_v2_from_service(self):
volume_from = VolumeFromSpec.parse('servicea', self.services, V2) volume_from = VolumeFromSpec.parse('servicea', self.services, V2_0)
assert volume_from == VolumeFromSpec('servicea', 'rw', 'service') assert volume_from == VolumeFromSpec('servicea', 'rw', 'service')
def test_parse_v2_from_service_with_mode(self): def test_parse_v2_from_service_with_mode(self):
volume_from = VolumeFromSpec.parse('servicea:ro', self.services, V2) volume_from = VolumeFromSpec.parse('servicea:ro', self.services, V2_0)
assert volume_from == VolumeFromSpec('servicea', 'ro', 'service') assert volume_from == VolumeFromSpec('servicea', 'ro', 'service')
def test_parse_v2_from_container(self): def test_parse_v2_from_container(self):
volume_from = VolumeFromSpec.parse('container:foo', self.services, V2) volume_from = VolumeFromSpec.parse('container:foo', self.services, V2_0)
assert volume_from == VolumeFromSpec('foo', 'rw', 'container') assert volume_from == VolumeFromSpec('foo', 'rw', 'container')
def test_parse_v2_from_container_with_mode(self): def test_parse_v2_from_container_with_mode(self):
volume_from = VolumeFromSpec.parse('container:foo:ro', self.services, V2) volume_from = VolumeFromSpec.parse('container:foo:ro', self.services, V2_0)
assert volume_from == VolumeFromSpec('foo', 'ro', 'container') assert volume_from == VolumeFromSpec('foo', 'ro', 'container')
def test_parse_v2_invalid_type(self): def test_parse_v2_invalid_type(self):
with pytest.raises(ConfigurationError) as exc: with pytest.raises(ConfigurationError) as exc:
VolumeFromSpec.parse('bogus:foo:ro', self.services, V2) VolumeFromSpec.parse('bogus:foo:ro', self.services, V2_0)
assert "Unknown volumes_from type 'bogus'" in exc.exconly() assert "Unknown volumes_from type 'bogus'" in exc.exconly()
def test_parse_v2_invalid(self): def test_parse_v2_invalid(self):
with pytest.raises(ConfigurationError): with pytest.raises(ConfigurationError):
VolumeFromSpec.parse('unknown:format:ro', self.services, V2) VolumeFromSpec.parse('unknown:format:ro', self.services, V2_0)