Fixed depends_on recreation behaviour for issue #6589

Previously any containers which did *not* have any links were always recreated.
In order to fix depends_on and preserve expected links recreation behaviour, we now only use the ConvergenceStrategy.always recreation strategy for a service if any of the the following conditions are true:
* --always-recreate-deps flag provided
* service container is stopped
* service defines links but the container does not have any
* container has links but the service definition does not

Signed-off-by: joeweoj <joewardell@gmail.com>
This commit is contained in:
joeweoj 2019-03-19 11:16:51 +00:00
parent 3b846ac8de
commit 8a339946fa
No known key found for this signature in database
GPG Key ID: DE85DA44854CA404
2 changed files with 143 additions and 2 deletions

View File

@ -586,8 +586,10 @@ class Project(object):
", ".join(updated_dependencies))
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:
service_has_links = any(service.get_link_names())
container_has_links = any(c.get('HostConfig.Links') for c in service.containers())
should_recreate_for_links = service_has_links ^ container_has_links
if always_recreate_deps or containers_stopped or should_recreate_for_links:
plan = service.convergence_plan(ConvergenceStrategy.always)
else:
plan = service.convergence_plan(strategy)

View File

@ -5,6 +5,8 @@ by `docker-compose up`.
from __future__ import absolute_import
from __future__ import unicode_literals
import copy
import py
from docker.errors import ImageNotFound
@ -209,6 +211,143 @@ class ProjectWithDependenciesTest(ProjectTestCase):
}
class ProjectWithDependsOnDependenciesTest(ProjectTestCase):
def setUp(self):
super(ProjectWithDependsOnDependenciesTest, self).setUp()
self.cfg = {
'version': '2',
'services': {
'db': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
},
'web': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
'depends_on': ['db'],
},
'nginx': {
'image': 'busybox:latest',
'command': 'tail -f /dev/null',
'depends_on': ['web'],
},
}
}
def test_up(self):
local_cfg = copy.deepcopy(self.cfg)
containers = self.run_up(local_cfg)
assert set(c.service for c in containers) == set(['db', 'web', 'nginx'])
def test_change_leaf(self):
local_cfg = copy.deepcopy(self.cfg)
old_containers = self.run_up(local_cfg)
local_cfg['services']['nginx']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(local_cfg)
assert set(c.service for c in new_containers - old_containers) == set(['nginx'])
def test_change_middle(self):
local_cfg = copy.deepcopy(self.cfg)
old_containers = self.run_up(local_cfg)
local_cfg['services']['web']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(local_cfg)
assert set(c.service for c in new_containers - old_containers) == set(['web'])
def test_change_middle_always_recreate_deps(self):
local_cfg = copy.deepcopy(self.cfg)
old_containers = self.run_up(local_cfg, always_recreate_deps=True)
local_cfg['services']['web']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(local_cfg, always_recreate_deps=True)
assert set(c.service for c in new_containers - old_containers) == set(['web', 'nginx'])
def test_change_root(self):
local_cfg = copy.deepcopy(self.cfg)
old_containers = self.run_up(local_cfg)
local_cfg['services']['db']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(local_cfg)
assert set(c.service for c in new_containers - old_containers) == set(['db'])
def test_change_root_always_recreate_deps(self):
local_cfg = copy.deepcopy(self.cfg)
old_containers = self.run_up(local_cfg, always_recreate_deps=True)
local_cfg['services']['db']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(local_cfg, always_recreate_deps=True)
assert set(c.service for c in new_containers - old_containers) == set(['db', 'web', 'nginx'])
def test_change_root_no_recreate(self):
local_cfg = copy.deepcopy(self.cfg)
old_containers = self.run_up(local_cfg)
local_cfg['services']['db']['environment'] = {'NEW_VAR': '1'}
new_containers = self.run_up(
local_cfg,
strategy=ConvergenceStrategy.never)
assert new_containers - old_containers == set()
def test_service_removed_while_down(self):
local_cfg = copy.deepcopy(self.cfg)
next_cfg = copy.deepcopy(self.cfg)
del next_cfg['services']['db']
del next_cfg['services']['web']['depends_on']
containers = self.run_up(local_cfg)
assert set(c.service for c in containers) == set(['db', 'web', 'nginx'])
project = self.make_project(local_cfg)
project.stop(timeout=1)
next_containers = self.run_up(next_cfg)
assert set(c.service for c in next_containers) == set(['web', 'nginx'])
def test_service_removed_while_up(self):
local_cfg = copy.deepcopy(self.cfg)
containers = self.run_up(local_cfg)
assert set(c.service for c in containers) == set(['db', 'web', 'nginx'])
del local_cfg['services']['db']
del local_cfg['services']['web']['depends_on']
containers = self.run_up(local_cfg)
assert set(c.service for c in containers) == set(['web', 'nginx'])
def test_dependency_removed(self):
local_cfg = copy.deepcopy(self.cfg)
next_cfg = copy.deepcopy(self.cfg)
del next_cfg['services']['nginx']['depends_on']
containers = self.run_up(local_cfg, service_names=['nginx'])
assert set(c.service for c in containers) == set(['db', 'web', 'nginx'])
project = self.make_project(local_cfg)
project.stop(timeout=1)
next_containers = self.run_up(next_cfg, service_names=['nginx'])
assert set(c.service for c in next_containers if c.is_running) == set(['nginx'])
def test_dependency_added(self):
local_cfg = copy.deepcopy(self.cfg)
del local_cfg['services']['nginx']['depends_on']
containers = self.run_up(local_cfg, service_names=['nginx'])
assert set(c.service for c in containers) == set(['nginx'])
local_cfg['services']['nginx']['depends_on'] = ['db']
containers = self.run_up(local_cfg, service_names=['nginx'])
assert set(c.service for c in containers) == set(['nginx', 'db'])
class ServiceStateTest(DockerClientTestCase):
"""Test cases for Service.convergence_plan."""