Require volumes_from a container to be explicit in V2 config.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
c3968a439f
commit
b76dc1e05e
11 changed files with 166 additions and 53 deletions
|
|
@ -292,7 +292,7 @@ def load_volumes(config_files):
|
|||
|
||||
|
||||
def load_services(working_dir, filename, service_configs, version):
|
||||
def build_service(service_name, service_dict):
|
||||
def build_service(service_name, service_dict, service_names):
|
||||
service_config = ServiceConfig.with_abs_paths(
|
||||
working_dir,
|
||||
filename,
|
||||
|
|
@ -305,13 +305,17 @@ def load_services(working_dir, filename, service_configs, version):
|
|||
validate_against_service_schema(service_dict, service_config.name, version)
|
||||
validate_paths(service_dict)
|
||||
|
||||
service_dict = finalize_service(service_config._replace(config=service_dict))
|
||||
service_dict = finalize_service(
|
||||
service_config._replace(config=service_dict),
|
||||
service_names,
|
||||
version)
|
||||
service_dict['name'] = service_config.name
|
||||
return service_dict
|
||||
|
||||
def build_services(service_config):
|
||||
service_names = service_config.keys()
|
||||
return sort_service_dicts([
|
||||
build_service(name, service_dict)
|
||||
build_service(name, service_dict, service_names)
|
||||
for name, service_dict in service_config.items()
|
||||
])
|
||||
|
||||
|
|
@ -504,7 +508,7 @@ def process_service(service_config):
|
|||
return service_dict
|
||||
|
||||
|
||||
def finalize_service(service_config):
|
||||
def finalize_service(service_config, service_names, version):
|
||||
service_dict = dict(service_config.config)
|
||||
|
||||
if 'environment' in service_dict or 'env_file' in service_dict:
|
||||
|
|
@ -513,7 +517,9 @@ def finalize_service(service_config):
|
|||
|
||||
if 'volumes_from' in service_dict:
|
||||
service_dict['volumes_from'] = [
|
||||
VolumeFromSpec.parse(vf) for vf in service_dict['volumes_from']]
|
||||
VolumeFromSpec.parse(vf, service_names, version)
|
||||
for vf in service_dict['volumes_from']
|
||||
]
|
||||
|
||||
if 'volumes' in service_dict:
|
||||
service_dict['volumes'] = [
|
||||
|
|
|
|||
|
|
@ -11,10 +11,16 @@ from compose.config.errors import ConfigurationError
|
|||
from compose.const import IS_WINDOWS_PLATFORM
|
||||
|
||||
|
||||
class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode')):
|
||||
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
|
||||
return func(service_names, volume_from_config)
|
||||
|
||||
@classmethod
|
||||
def parse(cls, volume_from_config):
|
||||
def parse_v1(cls, service_names, volume_from_config):
|
||||
parts = volume_from_config.split(':')
|
||||
if len(parts) > 2:
|
||||
raise ConfigurationError(
|
||||
|
|
@ -27,7 +33,39 @@ class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode')):
|
|||
else:
|
||||
source, mode = parts
|
||||
|
||||
return cls(source, mode)
|
||||
type = 'service' if source in service_names else 'container'
|
||||
return cls(source, mode, type)
|
||||
|
||||
@classmethod
|
||||
def parse_v2(cls, service_names, volume_from_config):
|
||||
parts = volume_from_config.split(':')
|
||||
if len(parts) > 3:
|
||||
raise ConfigurationError(
|
||||
"volume_from {} has incorrect format, should be one of "
|
||||
"'<service name>[:<mode>]' or "
|
||||
"'container:<container name>[:<mode>]'".format(volume_from_config))
|
||||
|
||||
if len(parts) == 1:
|
||||
source = parts[0]
|
||||
return cls(source, 'rw', 'service')
|
||||
|
||||
if len(parts) == 2:
|
||||
if parts[0] == 'container':
|
||||
type, source = parts
|
||||
return cls(source, 'rw', type)
|
||||
|
||||
source, mode = parts
|
||||
return cls(source, mode, 'service')
|
||||
|
||||
if len(parts) == 3:
|
||||
type, source, mode = parts
|
||||
if type not in ('service', 'container'):
|
||||
raise ConfigurationError(
|
||||
"Unknown volumes_from type '{}' in '{}'".format(
|
||||
type,
|
||||
volume_from_config))
|
||||
|
||||
return cls(source, mode, type)
|
||||
|
||||
|
||||
def parse_restart_spec(restart_config):
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class Project(object):
|
|||
|
||||
for service_dict in config_data.services:
|
||||
links = project.get_links(service_dict)
|
||||
volumes_from = project.get_volumes_from(service_dict)
|
||||
volumes_from = get_volumes_from(project, service_dict)
|
||||
net = project.get_net(service_dict)
|
||||
|
||||
project.services.append(
|
||||
|
|
@ -162,28 +162,6 @@ class Project(object):
|
|||
del service_dict['links']
|
||||
return links
|
||||
|
||||
def get_volumes_from(self, service_dict):
|
||||
volumes_from = []
|
||||
if 'volumes_from' in service_dict:
|
||||
for volume_from_spec in service_dict.get('volumes_from', []):
|
||||
# Get service
|
||||
try:
|
||||
service = self.get_service(volume_from_spec.source)
|
||||
volume_from_spec = volume_from_spec._replace(source=service)
|
||||
except NoSuchService:
|
||||
try:
|
||||
container = Container.from_id(self.client, volume_from_spec.source)
|
||||
volume_from_spec = volume_from_spec._replace(source=container)
|
||||
except APIError:
|
||||
raise ConfigurationError(
|
||||
'Service "%s" mounts volumes from "%s", which is '
|
||||
'not the name of a service or container.' % (
|
||||
service_dict['name'],
|
||||
volume_from_spec.source))
|
||||
volumes_from.append(volume_from_spec)
|
||||
del service_dict['volumes_from']
|
||||
return volumes_from
|
||||
|
||||
def get_net(self, service_dict):
|
||||
net = service_dict.pop('net', None)
|
||||
if not net:
|
||||
|
|
@ -465,6 +443,34 @@ def remove_links(service_dicts):
|
|||
del s['links']
|
||||
|
||||
|
||||
def get_volumes_from(project, service_dict):
|
||||
volumes_from = service_dict.pop('volumes_from', None)
|
||||
if not volumes_from:
|
||||
return []
|
||||
|
||||
def build_volume_from(spec):
|
||||
if spec.type == 'service':
|
||||
try:
|
||||
return spec._replace(source=project.get_service(spec.source))
|
||||
except NoSuchService:
|
||||
pass
|
||||
|
||||
if spec.type == 'container':
|
||||
try:
|
||||
container = Container.from_id(project.client, spec.source)
|
||||
return spec._replace(source=container)
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
raise ConfigurationError(
|
||||
"Service \"{}\" mounts volumes from \"{}\", which is not the name "
|
||||
"of a service or container.".format(
|
||||
service_dict['name'],
|
||||
spec.source))
|
||||
|
||||
return [build_volume_from(vf) for vf in volumes_from]
|
||||
|
||||
|
||||
class NoSuchService(Exception):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue