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)