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
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 .errors import CircularReference
from .errors import ComposeFileNotFound
@ -129,25 +131,34 @@ class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
@cached_property
def version(self):
version = self.config.get('version', 1)
version = self.config.get('version', V1)
if isinstance(version, dict):
log.warn("Unexpected type for field 'version', in file {} assuming "
"version is the name of a service, and defaulting to "
"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
def get_service(self, name):
return self.get_service_dicts()[name]
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):
return {} if self.version == 1 else self.config.get('volumes', {})
return {} if self.version == V1 else self.config.get('volumes', {})
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')):
@ -209,10 +220,6 @@ def validate_config_version(config_files):
next_file.filename,
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):
(candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, base_dir)
@ -276,7 +283,7 @@ def load(config_details):
main_file,
[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:
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')
if config_file.version == 2:
if config_file.version == V2_0:
processed_config = dict(config_file.config)
processed_config['services'] = services = interpolated_config
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(
config_file.get_networks(), 'network')
if config_file.version == 1:
if config_file.version == V1:
processed_config = services = interpolated_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:
d[field] = override.get(field, base.get(field))
if version == 1:
if version == V1:
legacy_v1_merge_image_or_build(d, base, override)
else:
merge_build(d, base, override)

View File

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

View File

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

View File

@ -7,6 +7,7 @@ from __future__ import unicode_literals
import os
from collections import namedtuple
from compose.config.config import V1
from compose.config.errors import ConfigurationError
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
@classmethod
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)
@classmethod

View File

@ -14,9 +14,12 @@ LABEL_PROJECT = 'com.docker.compose.project'
LABEL_SERVICE = 'com.docker.compose.service'
LABEL_VERSION = 'com.docker.compose.version'
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 = {
1: '1.21',
2: '1.22',
COMPOSEFILE_V1: '1.21',
COMPOSEFILE_V2_0: '1.22',
}

View File

@ -10,6 +10,7 @@ from docker.errors import NotFound
from . import parallel
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_service_name_from_network_mode
from .const import DEFAULT_TIMEOUT
@ -56,7 +57,7 @@ class Project(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)
network_config = config_data.networks or {}
@ -94,7 +95,7 @@ class Project(object):
network_mode = project.get_network_mode(service_dict, networks)
volumes_from = get_volumes_from(project, service_dict)
if config_data.version == 2:
if config_data.version != V1:
service_volumes = service_dict.get('volumes', [])
for volume_spec in service_volumes:
if volume_spec.is_named_volume:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,13 +3,13 @@ from __future__ import unicode_literals
import pytest
from compose.config.config import V1
from compose.config.config import V2_0
from compose.config.errors import ConfigurationError
from compose.config.types import parse_extra_hosts
from compose.config.types import VolumeFromSpec
from compose.config.types import VolumeSpec
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():
@ -91,26 +91,26 @@ class TestVolumesFromSpec(object):
VolumeFromSpec.parse('unknown:format:ro', self.services, V1)
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')
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')
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')
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')
def test_parse_v2_invalid_type(self):
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()
def test_parse_v2_invalid(self):
with pytest.raises(ConfigurationError):
VolumeFromSpec.parse('unknown:format:ro', self.services, V2)
VolumeFromSpec.parse('unknown:format:ro', self.services, V2_0)