mirror of https://github.com/docker/compose.git
Add up flag `--renew-anon-volumes` (shorthand -V)
to avoid reusing the previous container's data Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
fd1e8024f7
commit
b07091ac5f
|
@ -936,7 +936,7 @@ class TopLevelCommand(object):
|
||||||
--always-recreate-deps Recreate dependent containers.
|
--always-recreate-deps Recreate dependent containers.
|
||||||
Incompatible with --no-recreate.
|
Incompatible with --no-recreate.
|
||||||
--no-recreate If containers already exist, don't recreate
|
--no-recreate If containers already exist, don't recreate
|
||||||
them. Incompatible with --force-recreate.
|
them. Incompatible with --force-recreate and -V.
|
||||||
--no-build Don't build an image, even if it's missing.
|
--no-build Don't build an image, even if it's missing.
|
||||||
--no-start Don't start the services after creating them.
|
--no-start Don't start the services after creating them.
|
||||||
--build Build images before starting containers.
|
--build Build images before starting containers.
|
||||||
|
@ -945,8 +945,10 @@ class TopLevelCommand(object):
|
||||||
-t, --timeout TIMEOUT Use this timeout in seconds for container
|
-t, --timeout TIMEOUT Use this timeout in seconds for container
|
||||||
shutdown when attached or when containers are
|
shutdown when attached or when containers are
|
||||||
already running. (default: 10)
|
already running. (default: 10)
|
||||||
--remove-orphans Remove containers for services not
|
-V, --renew-anon-volumes Recreate anonymous volumes instead of retrieving
|
||||||
defined in the Compose file
|
data from the previous containers.
|
||||||
|
--remove-orphans Remove containers for services not defined
|
||||||
|
in the Compose file.
|
||||||
--exit-code-from SERVICE Return the exit code of the selected service
|
--exit-code-from SERVICE Return the exit code of the selected service
|
||||||
container. Implies --abort-on-container-exit.
|
container. Implies --abort-on-container-exit.
|
||||||
--scale SERVICE=NUM Scale SERVICE to NUM instances. Overrides the
|
--scale SERVICE=NUM Scale SERVICE to NUM instances. Overrides the
|
||||||
|
@ -992,6 +994,7 @@ class TopLevelCommand(object):
|
||||||
start=not no_start,
|
start=not no_start,
|
||||||
always_recreate_deps=always_recreate_deps,
|
always_recreate_deps=always_recreate_deps,
|
||||||
reset_container_image=rebuild,
|
reset_container_image=rebuild,
|
||||||
|
renew_anonymous_volumes=options.get('--renew-anon-volumes')
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1083,10 +1086,14 @@ def compute_exit_code(exit_value_from, attached_containers, cascade_starter, all
|
||||||
def convergence_strategy_from_opts(options):
|
def convergence_strategy_from_opts(options):
|
||||||
no_recreate = options['--no-recreate']
|
no_recreate = options['--no-recreate']
|
||||||
force_recreate = options['--force-recreate']
|
force_recreate = options['--force-recreate']
|
||||||
|
renew_anonymous_volumes = options.get('--renew-anon-volumes')
|
||||||
if force_recreate and no_recreate:
|
if force_recreate and no_recreate:
|
||||||
raise UserError("--force-recreate and --no-recreate cannot be combined.")
|
raise UserError("--force-recreate and --no-recreate cannot be combined.")
|
||||||
|
|
||||||
if force_recreate:
|
if no_recreate and renew_anonymous_volumes:
|
||||||
|
raise UserError('--no-recreate and --renew-anon-volumes cannot be combined.')
|
||||||
|
|
||||||
|
if force_recreate or renew_anonymous_volumes:
|
||||||
return ConvergenceStrategy.always
|
return ConvergenceStrategy.always
|
||||||
|
|
||||||
if no_recreate:
|
if no_recreate:
|
||||||
|
|
|
@ -445,7 +445,8 @@ class Project(object):
|
||||||
rescale=True,
|
rescale=True,
|
||||||
start=True,
|
start=True,
|
||||||
always_recreate_deps=False,
|
always_recreate_deps=False,
|
||||||
reset_container_image=False):
|
reset_container_image=False,
|
||||||
|
renew_anonymous_volumes=False):
|
||||||
|
|
||||||
self.initialize()
|
self.initialize()
|
||||||
if not ignore_orphans:
|
if not ignore_orphans:
|
||||||
|
@ -474,7 +475,8 @@ class Project(object):
|
||||||
rescale=rescale,
|
rescale=rescale,
|
||||||
start=start,
|
start=start,
|
||||||
project_services=scaled_services,
|
project_services=scaled_services,
|
||||||
reset_container_image=reset_container_image
|
reset_container_image=reset_container_image,
|
||||||
|
renew_anonymous_volumes=renew_anonymous_volumes,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_deps(service):
|
def get_deps(service):
|
||||||
|
|
|
@ -409,7 +409,8 @@ class Service(object):
|
||||||
|
|
||||||
return containers
|
return containers
|
||||||
|
|
||||||
def _execute_convergence_recreate(self, containers, scale, timeout, detached, start):
|
def _execute_convergence_recreate(self, containers, scale, timeout, detached, start,
|
||||||
|
renew_anonymous_volumes):
|
||||||
if scale is not None and len(containers) > scale:
|
if scale is not None and len(containers) > scale:
|
||||||
self._downscale(containers[scale:], timeout)
|
self._downscale(containers[scale:], timeout)
|
||||||
containers = containers[:scale]
|
containers = containers[:scale]
|
||||||
|
@ -417,7 +418,7 @@ class Service(object):
|
||||||
def recreate(container):
|
def recreate(container):
|
||||||
return self.recreate_container(
|
return self.recreate_container(
|
||||||
container, timeout=timeout, attach_logs=not detached,
|
container, timeout=timeout, attach_logs=not detached,
|
||||||
start_new_container=start
|
start_new_container=start, renew_anonymous_volumes=renew_anonymous_volumes
|
||||||
)
|
)
|
||||||
containers, errors = parallel_execute(
|
containers, errors = parallel_execute(
|
||||||
containers,
|
containers,
|
||||||
|
@ -470,7 +471,7 @@ class Service(object):
|
||||||
def execute_convergence_plan(self, plan, timeout=None, detached=False,
|
def execute_convergence_plan(self, plan, timeout=None, detached=False,
|
||||||
start=True, scale_override=None,
|
start=True, scale_override=None,
|
||||||
rescale=True, project_services=None,
|
rescale=True, project_services=None,
|
||||||
reset_container_image=False):
|
reset_container_image=False, renew_anonymous_volumes=False):
|
||||||
(action, containers) = plan
|
(action, containers) = plan
|
||||||
scale = scale_override if scale_override is not None else self.scale_num
|
scale = scale_override if scale_override is not None else self.scale_num
|
||||||
containers = sorted(containers, key=attrgetter('number'))
|
containers = sorted(containers, key=attrgetter('number'))
|
||||||
|
@ -495,7 +496,8 @@ class Service(object):
|
||||||
for c in containers:
|
for c in containers:
|
||||||
c.reset_image(img_id)
|
c.reset_image(img_id)
|
||||||
return self._execute_convergence_recreate(
|
return self._execute_convergence_recreate(
|
||||||
containers, scale, timeout, detached, start
|
containers, scale, timeout, detached, start,
|
||||||
|
renew_anonymous_volumes,
|
||||||
)
|
)
|
||||||
|
|
||||||
if action == 'start':
|
if action == 'start':
|
||||||
|
@ -515,7 +517,8 @@ class Service(object):
|
||||||
|
|
||||||
raise Exception("Invalid action: {}".format(action))
|
raise Exception("Invalid action: {}".format(action))
|
||||||
|
|
||||||
def recreate_container(self, container, timeout=None, attach_logs=False, start_new_container=True):
|
def recreate_container(self, container, timeout=None, attach_logs=False, start_new_container=True,
|
||||||
|
renew_anonymous_volumes=False):
|
||||||
"""Recreate a container.
|
"""Recreate a container.
|
||||||
|
|
||||||
The original container is renamed to a temporary name so that data
|
The original container is renamed to a temporary name so that data
|
||||||
|
@ -526,7 +529,7 @@ class Service(object):
|
||||||
container.stop(timeout=self.stop_timeout(timeout))
|
container.stop(timeout=self.stop_timeout(timeout))
|
||||||
container.rename_to_tmp_name()
|
container.rename_to_tmp_name()
|
||||||
new_container = self.create_container(
|
new_container = self.create_container(
|
||||||
previous_container=container,
|
previous_container=container if not renew_anonymous_volumes else None,
|
||||||
number=container.labels.get(LABEL_CONTAINER_NUMBER),
|
number=container.labels.get(LABEL_CONTAINER_NUMBER),
|
||||||
quiet=True,
|
quiet=True,
|
||||||
)
|
)
|
||||||
|
|
|
@ -589,6 +589,25 @@ class ServiceTest(DockerClientTestCase):
|
||||||
assert [mount['Destination'] for mount in new_container.get('Mounts')] == ['/data']
|
assert [mount['Destination'] for mount in new_container.get('Mounts')] == ['/data']
|
||||||
assert new_container.get_mount('/data')['Source'] == volume_path
|
assert new_container.get_mount('/data')['Source'] == volume_path
|
||||||
|
|
||||||
|
def test_execute_convergence_plan_with_image_declared_volume_renew(self):
|
||||||
|
service = Service(
|
||||||
|
project='composetest',
|
||||||
|
name='db',
|
||||||
|
client=self.client,
|
||||||
|
build={'context': 'tests/fixtures/dockerfile-with-volume'},
|
||||||
|
)
|
||||||
|
|
||||||
|
old_container = create_and_start_container(service)
|
||||||
|
assert [mount['Destination'] for mount in old_container.get('Mounts')] == ['/data']
|
||||||
|
volume_path = old_container.get_mount('/data')['Source']
|
||||||
|
|
||||||
|
new_container, = service.execute_convergence_plan(
|
||||||
|
ConvergencePlan('recreate', [old_container]), renew_anonymous_volumes=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert [mount['Destination'] for mount in new_container.get('Mounts')] == ['/data']
|
||||||
|
assert new_container.get_mount('/data')['Source'] != volume_path
|
||||||
|
|
||||||
def test_execute_convergence_plan_when_image_volume_masks_config(self):
|
def test_execute_convergence_plan_when_image_volume_masks_config(self):
|
||||||
service = self.create_service(
|
service = self.create_service(
|
||||||
'db',
|
'db',
|
||||||
|
@ -637,6 +656,64 @@ class ServiceTest(DockerClientTestCase):
|
||||||
)
|
)
|
||||||
assert new_container.get_mount('/data')['Source'] != host_path
|
assert new_container.get_mount('/data')['Source'] != host_path
|
||||||
|
|
||||||
|
def test_execute_convergence_plan_anonymous_volume_renew(self):
|
||||||
|
service = self.create_service(
|
||||||
|
'db',
|
||||||
|
image='busybox',
|
||||||
|
volumes=[VolumeSpec(None, '/data', 'rw')])
|
||||||
|
|
||||||
|
old_container = create_and_start_container(service)
|
||||||
|
assert (
|
||||||
|
[mount['Destination'] for mount in old_container.get('Mounts')] ==
|
||||||
|
['/data']
|
||||||
|
)
|
||||||
|
volume_path = old_container.get_mount('/data')['Source']
|
||||||
|
|
||||||
|
new_container, = service.execute_convergence_plan(
|
||||||
|
ConvergencePlan('recreate', [old_container]),
|
||||||
|
renew_anonymous_volumes=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
[mount['Destination'] for mount in new_container.get('Mounts')] ==
|
||||||
|
['/data']
|
||||||
|
)
|
||||||
|
assert new_container.get_mount('/data')['Source'] != volume_path
|
||||||
|
|
||||||
|
def test_execute_convergence_plan_anonymous_volume_recreate_then_renew(self):
|
||||||
|
service = self.create_service(
|
||||||
|
'db',
|
||||||
|
image='busybox',
|
||||||
|
volumes=[VolumeSpec(None, '/data', 'rw')])
|
||||||
|
|
||||||
|
old_container = create_and_start_container(service)
|
||||||
|
assert (
|
||||||
|
[mount['Destination'] for mount in old_container.get('Mounts')] ==
|
||||||
|
['/data']
|
||||||
|
)
|
||||||
|
volume_path = old_container.get_mount('/data')['Source']
|
||||||
|
|
||||||
|
mid_container, = service.execute_convergence_plan(
|
||||||
|
ConvergencePlan('recreate', [old_container]),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
[mount['Destination'] for mount in mid_container.get('Mounts')] ==
|
||||||
|
['/data']
|
||||||
|
)
|
||||||
|
assert mid_container.get_mount('/data')['Source'] == volume_path
|
||||||
|
|
||||||
|
new_container, = service.execute_convergence_plan(
|
||||||
|
ConvergencePlan('recreate', [mid_container]),
|
||||||
|
renew_anonymous_volumes=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
[mount['Destination'] for mount in new_container.get('Mounts')] ==
|
||||||
|
['/data']
|
||||||
|
)
|
||||||
|
assert new_container.get_mount('/data')['Source'] != volume_path
|
||||||
|
|
||||||
def test_execute_convergence_plan_without_start(self):
|
def test_execute_convergence_plan_without_start(self):
|
||||||
service = self.create_service(
|
service = self.create_service(
|
||||||
'db',
|
'db',
|
||||||
|
|
Loading…
Reference in New Issue