From ba0b3d421c2794c44129a1b2f1b5180ea3d1f238 Mon Sep 17 00:00:00 2001 From: Drew Romanyk Date: Tue, 14 Nov 2017 16:24:58 -0600 Subject: [PATCH 1/3] Fix depends on restart behavior, fixes #3397 Signed-off-by: Drew Romanyk --- compose/cli/main.py | 5 ++++- compose/project.py | 16 ++++++++++++---- tests/integration/state_test.py | 26 ++++++++++++++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/compose/cli/main.py b/compose/cli/main.py index 81f1e93cd..14471cf34 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -918,6 +918,7 @@ class TopLevelCommand(object): --no-deps Don't start linked services. --force-recreate Recreate containers even if their configuration and image haven't changed. + --always-recreate-deps Recreate dependant containers. Incompatible with --no-recreate. --no-recreate If containers already exist, don't recreate them. Incompatible with --force-recreate. @@ -938,6 +939,7 @@ class TopLevelCommand(object): setting in the Compose file if present. """ start_deps = not options['--no-deps'] + always_recreate_deps = options['--always-recreate-deps'] exit_value_from = exitval_from_opts(options, self.project) cascade_stop = options['--abort-on-container-exit'] service_names = options['SERVICE'] @@ -974,7 +976,8 @@ class TopLevelCommand(object): remove_orphans=remove_orphans, ignore_orphans=ignore_orphans, scale_override=parse_scale_args(options['--scale']), - start=not no_start + start=not no_start, + always_recreate_deps=always_recreate_deps ) if detached or no_start: diff --git a/compose/project.py b/compose/project.py index 6683a3cba..f7a55d966 100644 --- a/compose/project.py +++ b/compose/project.py @@ -442,7 +442,8 @@ class Project(object): ignore_orphans=False, scale_override=None, rescale=True, - start=True): + start=True, + always_recreate_deps=False): warn_for_swarm_mode(self.client) @@ -459,7 +460,8 @@ class Project(object): for svc in services: svc.ensure_image_exists(do_build=do_build) - plans = self._get_convergence_plans(services, strategy) + plans = self._get_convergence_plans( + services, strategy, always_recreate_deps=always_recreate_deps) scaled_services = self.get_scaled_services(services, scale_override) def do(service): @@ -503,7 +505,7 @@ class Project(object): self.networks.initialize() self.volumes.initialize() - def _get_convergence_plans(self, services, strategy): + def _get_convergence_plans(self, services, strategy, always_recreate_deps=False): plans = {} for service in services: @@ -518,7 +520,13 @@ class Project(object): log.debug('%s has upstream changes (%s)', service.name, ", ".join(updated_dependencies)) - plan = service.convergence_plan(ConvergenceStrategy.always) + containers_stopped = any((not c.is_paused and not c.is_restarting and not c.is_running + for c in service.containers(stopped=True))) + has_links = any(c.get('HostConfig.Links') for c in service.containers()) + if always_recreate_deps or containers_stopped or not has_links: + plan = service.convergence_plan(ConvergenceStrategy.always) + else: + plan = service.convergence_plan(strategy) else: plan = service.convergence_plan(strategy) diff --git a/tests/integration/state_test.py b/tests/integration/state_test.py index 0b174f69f..5992a02a4 100644 --- a/tests/integration/state_test.py +++ b/tests/integration/state_test.py @@ -130,9 +130,16 @@ class ProjectWithDependenciesTest(ProjectTestCase): self.cfg['web']['environment'] = {'NEW_VAR': '1'} new_containers = self.run_up(self.cfg) - assert set(c.name_without_project for c in new_containers - old_containers) == set( - ['web_1', 'nginx_1'] - ) + assert set(c.name_without_project for c in new_containers - old_containers) == set(['web_1']) + + def test_change_middle_always_recreate_deps(self): + old_containers = self.run_up(self.cfg, always_recreate_deps=True) + + self.cfg['web']['environment'] = {'NEW_VAR': '1'} + new_containers = self.run_up(self.cfg, always_recreate_deps=True) + + assert set(c.name_without_project + for c in new_containers - old_containers) == {'web_1', 'nginx_1'} def test_change_root(self): old_containers = self.run_up(self.cfg) @@ -140,9 +147,16 @@ class ProjectWithDependenciesTest(ProjectTestCase): self.cfg['db']['environment'] = {'NEW_VAR': '1'} new_containers = self.run_up(self.cfg) - assert set(c.name_without_project for c in new_containers - old_containers) == set( - ['db_1', 'web_1', 'nginx_1'] - ) + assert set(c.name_without_project for c in new_containers - old_containers) == set(['db_1']) + + def test_change_root_always_recreate_deps(self): + old_containers = self.run_up(self.cfg, always_recreate_deps=True) + + self.cfg['db']['environment'] = {'NEW_VAR': '1'} + new_containers = self.run_up(self.cfg, always_recreate_deps=True) + + assert set(c.name_without_project + for c in new_containers - old_containers) == {'db_1', 'web_1', 'nginx_1'} def test_change_root_no_recreate(self): old_containers = self.run_up(self.cfg) From 5db3cd60d4e5bd4c320cc23ba7cda6f619522d62 Mon Sep 17 00:00:00 2001 From: Drew Romanyk Date: Wed, 29 Nov 2017 21:06:45 -0600 Subject: [PATCH 2/3] Reword & Optimize getting stopped containers & update tests Signed-off-by: Drew Romanyk --- compose/cli/main.py | 2 +- compose/project.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compose/cli/main.py b/compose/cli/main.py index 14471cf34..338566c72 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -918,7 +918,7 @@ class TopLevelCommand(object): --no-deps Don't start linked services. --force-recreate Recreate containers even if their configuration and image haven't changed. - --always-recreate-deps Recreate dependant containers. + --always-recreate-deps Recreate dependent containers. Incompatible with --no-recreate. --no-recreate If containers already exist, don't recreate them. Incompatible with --force-recreate. diff --git a/compose/project.py b/compose/project.py index f7a55d966..b4dbb817a 100644 --- a/compose/project.py +++ b/compose/project.py @@ -520,8 +520,8 @@ class Project(object): log.debug('%s has upstream changes (%s)', service.name, ", ".join(updated_dependencies)) - containers_stopped = any((not c.is_paused and not c.is_restarting and not c.is_running - for c in service.containers(stopped=True))) + containers_stopped = len( + service.containers(stopped=True, filters={'status': ['created', 'exited']})) > 0 has_links = any(c.get('HostConfig.Links') for c in service.containers()) if always_recreate_deps or containers_stopped or not has_links: plan = service.convergence_plan(ConvergenceStrategy.always) From c63ad67e9c20ae09ceaea95f0d552973450efcb7 Mon Sep 17 00:00:00 2001 From: Drew Romanyk Date: Thu, 30 Nov 2017 22:00:10 -0600 Subject: [PATCH 3/3] Convert to use any for finding stopped containers Signed-off-by: Drew Romanyk --- compose/project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compose/project.py b/compose/project.py index b4dbb817a..a507eab96 100644 --- a/compose/project.py +++ b/compose/project.py @@ -520,8 +520,8 @@ class Project(object): log.debug('%s has upstream changes (%s)', service.name, ", ".join(updated_dependencies)) - containers_stopped = len( - service.containers(stopped=True, filters={'status': ['created', 'exited']})) > 0 + containers_stopped = any( + service.containers(stopped=True, filters={'status': ['created', 'exited']})) has_links = any(c.get('HostConfig.Links') for c in service.containers()) if always_recreate_deps or containers_stopped or not has_links: plan = service.convergence_plan(ConvergenceStrategy.always)