Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add up flag --renew-anon-volumes (shorthand -V) #5596

Merged
merged 1 commit into from
Jan 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions compose/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ def up(self, options):
--always-recreate-deps Recreate dependent containers.
Incompatible with --no-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-start Don't start the services after creating them.
--build Build images before starting containers.
Expand All @@ -945,8 +945,10 @@ def up(self, options):
-t, --timeout TIMEOUT Use this timeout in seconds for container
shutdown when attached or when containers are
already running. (default: 10)
--remove-orphans Remove containers for services not
defined in the Compose file
-V, --renew-anon-volumes Recreate anonymous volumes instead of retrieving
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
container. Implies --abort-on-container-exit.
--scale SERVICE=NUM Scale SERVICE to NUM instances. Overrides the
Expand Down Expand Up @@ -992,6 +994,7 @@ def up(rebuild):
start=not no_start,
always_recreate_deps=always_recreate_deps,
reset_container_image=rebuild,
renew_anonymous_volumes=options.get('--renew-anon-volumes')
)

try:
Expand Down Expand Up @@ -1083,10 +1086,14 @@ def compute_exit_code(exit_value_from, attached_containers, cascade_starter, all
def convergence_strategy_from_opts(options):
no_recreate = options['--no-recreate']
force_recreate = options['--force-recreate']
renew_anonymous_volumes = options.get('--renew-anon-volumes')
if force_recreate and no_recreate:
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

if no_recreate:
Expand Down
6 changes: 4 additions & 2 deletions compose/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,8 @@ def up(self,
rescale=True,
start=True,
always_recreate_deps=False,
reset_container_image=False):
reset_container_image=False,
renew_anonymous_volumes=False):

self.initialize()
if not ignore_orphans:
Expand Down Expand Up @@ -474,7 +475,8 @@ def do(service):
rescale=rescale,
start=start,
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):
Expand Down
15 changes: 9 additions & 6 deletions compose/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,16 @@ def create_and_start(service, n):

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:
self._downscale(containers[scale:], timeout)
containers = containers[:scale]

def recreate(container):
return self.recreate_container(
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,
Expand Down Expand Up @@ -470,7 +471,7 @@ def stop_and_remove(container):
def execute_convergence_plan(self, plan, timeout=None, detached=False,
start=True, scale_override=None,
rescale=True, project_services=None,
reset_container_image=False):
reset_container_image=False, renew_anonymous_volumes=False):
(action, containers) = plan
scale = scale_override if scale_override is not None else self.scale_num
containers = sorted(containers, key=attrgetter('number'))
Expand All @@ -495,7 +496,8 @@ def execute_convergence_plan(self, plan, timeout=None, detached=False,
for c in containers:
c.reset_image(img_id)
return self._execute_convergence_recreate(
containers, scale, timeout, detached, start
containers, scale, timeout, detached, start,
renew_anonymous_volumes,
)

if action == 'start':
Expand All @@ -515,7 +517,8 @@ def execute_convergence_plan(self, plan, timeout=None, detached=False,

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.

The original container is renamed to a temporary name so that data
Expand All @@ -526,7 +529,7 @@ def recreate_container(self, container, timeout=None, attach_logs=False, start_n
container.stop(timeout=self.stop_timeout(timeout))
container.rename_to_tmp_name()
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),
quiet=True,
)
Expand Down
77 changes: 77 additions & 0 deletions tests/integration/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,25 @@ def test_execute_convergence_plan_with_image_declared_volume(self):
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_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):
service = self.create_service(
'db',
Expand Down Expand Up @@ -637,6 +656,64 @@ def test_execute_convergence_plan_when_host_volume_is_removed(self):
)
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):
service = self.create_service(
'db',
Expand Down