Use individual volumes for recreate instead of volumes_from
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
This commit is contained in:
parent
4997facbb4
commit
417d9c2d51
5 changed files with 152 additions and 40 deletions
|
|
@ -317,10 +317,7 @@ class TopLevelCommand(Command):
|
|||
}
|
||||
|
||||
if options['-e']:
|
||||
# Merge environment from config with -e command line
|
||||
container_options['environment'] = dict(
|
||||
parse_environment(service.options.get('environment')),
|
||||
**parse_environment(options['-e']))
|
||||
container_options['environment'] = parse_environment(options['-e'])
|
||||
|
||||
if options['--entrypoint']:
|
||||
container_options['entrypoint'] = options.get('--entrypoint')
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ class Container(object):
|
|||
def image(self):
|
||||
return self.dictionary['Image']
|
||||
|
||||
@property
|
||||
def image_config(self):
|
||||
return self.client.inspect_image(self.image)
|
||||
|
||||
@property
|
||||
def short_id(self):
|
||||
return self.id[:10]
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ from __future__ import absolute_import
|
|||
from collections import namedtuple
|
||||
import logging
|
||||
import re
|
||||
from operator import attrgetter
|
||||
import sys
|
||||
import six
|
||||
from operator import attrgetter
|
||||
|
||||
import six
|
||||
from docker.errors import APIError
|
||||
from docker.utils import create_host_config, LogConfig
|
||||
|
||||
from .config import DOCKER_CONFIG_KEYS
|
||||
from .config import DOCKER_CONFIG_KEYS, merge_environment
|
||||
from .container import Container, get_container_name
|
||||
from .progress_stream import stream_output, StreamOutputError
|
||||
|
||||
|
|
@ -183,10 +183,10 @@ class Service(object):
|
|||
Create a container for this service. If the image doesn't exist, attempt to pull
|
||||
it.
|
||||
"""
|
||||
override_options['volumes_from'] = self._get_volumes_from(previous_container)
|
||||
container_options = self._get_container_create_options(
|
||||
override_options,
|
||||
one_off=one_off,
|
||||
previous_container=previous_container,
|
||||
)
|
||||
|
||||
if (do_build and
|
||||
|
|
@ -247,6 +247,12 @@ class Service(object):
|
|||
self.client.rename(
|
||||
container.id,
|
||||
'%s_%s' % (container.short_id, container.name))
|
||||
|
||||
override_options = dict(
|
||||
override_options,
|
||||
environment=merge_environment(
|
||||
override_options.get('environment'),
|
||||
{'affinity:container': '=' + container.id}))
|
||||
new_container = self.create_container(
|
||||
do_build=False,
|
||||
previous_container=container,
|
||||
|
|
@ -326,7 +332,7 @@ class Service(object):
|
|||
links.append((external_link, link_name))
|
||||
return links
|
||||
|
||||
def _get_volumes_from(self, previous_container=None):
|
||||
def _get_volumes_from(self):
|
||||
volumes_from = []
|
||||
for volume_source in self.volumes_from:
|
||||
if isinstance(volume_source, Service):
|
||||
|
|
@ -339,9 +345,6 @@ class Service(object):
|
|||
elif isinstance(volume_source, Container):
|
||||
volumes_from.append(volume_source.id)
|
||||
|
||||
if previous_container:
|
||||
volumes_from.append(previous_container.id)
|
||||
|
||||
return volumes_from
|
||||
|
||||
def _get_net(self):
|
||||
|
|
@ -363,7 +366,11 @@ class Service(object):
|
|||
|
||||
return net
|
||||
|
||||
def _get_container_create_options(self, override_options, one_off=False):
|
||||
def _get_container_create_options(
|
||||
self,
|
||||
override_options,
|
||||
one_off=False,
|
||||
previous_container=None):
|
||||
container_options = dict(
|
||||
(k, self.options[k])
|
||||
for k in DOCKER_CONFIG_KEYS if k in self.options)
|
||||
|
|
@ -396,11 +403,19 @@ class Service(object):
|
|||
ports.append(port)
|
||||
container_options['ports'] = ports
|
||||
|
||||
override_options['binds'] = merge_volume_bindings(
|
||||
container_options.get('volumes') or [],
|
||||
previous_container)
|
||||
|
||||
if 'volumes' in container_options:
|
||||
container_options['volumes'] = dict(
|
||||
(parse_volume_spec(v).internal, {})
|
||||
for v in container_options['volumes'])
|
||||
|
||||
container_options['environment'] = merge_environment(
|
||||
self.options.get('environment'),
|
||||
override_options.get('environment'))
|
||||
|
||||
if self.can_be_built():
|
||||
container_options['image'] = self.full_name
|
||||
|
||||
|
|
@ -418,11 +433,6 @@ class Service(object):
|
|||
options = dict(self.options, **override_options)
|
||||
port_bindings = build_port_bindings(options.get('ports') or [])
|
||||
|
||||
volume_bindings = dict(
|
||||
build_volume_binding(parse_volume_spec(volume))
|
||||
for volume in options.get('volumes') or []
|
||||
if ':' in volume)
|
||||
|
||||
privileged = options.get('privileged', False)
|
||||
cap_add = options.get('cap_add', None)
|
||||
cap_drop = options.get('cap_drop', None)
|
||||
|
|
@ -447,8 +457,8 @@ class Service(object):
|
|||
return create_host_config(
|
||||
links=self._get_links(link_to_self=one_off),
|
||||
port_bindings=port_bindings,
|
||||
binds=volume_bindings,
|
||||
volumes_from=options.get('volumes_from'),
|
||||
binds=options.get('binds'),
|
||||
volumes_from=self._get_volumes_from(),
|
||||
privileged=privileged,
|
||||
network_mode=self._get_net(),
|
||||
devices=devices,
|
||||
|
|
@ -531,6 +541,50 @@ class Service(object):
|
|||
stream_output(output, sys.stdout)
|
||||
|
||||
|
||||
def get_container_data_volumes(container, volumes_option):
|
||||
"""Find the container data volumes that are in `volumes_option`, and return
|
||||
a mapping of volume bindings for those volumes.
|
||||
"""
|
||||
volumes = []
|
||||
|
||||
volumes_option = volumes_option or []
|
||||
container_volumes = container.get('Volumes') or {}
|
||||
image_volumes = container.image_config['ContainerConfig'].get('Volumes') or {}
|
||||
|
||||
for volume in set(volumes_option + image_volumes.keys()):
|
||||
volume = parse_volume_spec(volume)
|
||||
# No need to preserve host volumes
|
||||
if volume.external:
|
||||
continue
|
||||
|
||||
volume_path = container_volumes.get(volume.internal)
|
||||
# New volume, doesn't exist in the old container
|
||||
if not volume_path:
|
||||
continue
|
||||
|
||||
# Copy existing volume from old container
|
||||
volume = volume._replace(external=volume_path)
|
||||
volumes.append(build_volume_binding(volume))
|
||||
|
||||
return dict(volumes)
|
||||
|
||||
|
||||
def merge_volume_bindings(volumes_option, previous_container):
|
||||
"""Return a list of volume bindings for a container. Container data volumes
|
||||
are replaced by those from the previous container.
|
||||
"""
|
||||
volume_bindings = dict(
|
||||
build_volume_binding(parse_volume_spec(volume))
|
||||
for volume in volumes_option or []
|
||||
if ':' in volume)
|
||||
|
||||
if previous_container:
|
||||
volume_bindings.update(
|
||||
get_container_data_volumes(previous_container, volumes_option))
|
||||
|
||||
return volume_bindings
|
||||
|
||||
|
||||
NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue