Expand depends_on to allow different conditions (service_start, service_healthy)
Rework "up" and "start" to wait on conditional state of dependent services Add integration tests Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
f6edd610f3
commit
04394b1d0a
10 changed files with 228 additions and 18 deletions
|
|
@ -928,7 +928,7 @@ class CLITestCase(DockerClientTestCase):
|
|||
assert foo_container.get('HostConfig.NetworkMode') == \
|
||||
'container:{}'.format(bar_container.id)
|
||||
|
||||
@v3_only()
|
||||
@v2_1_only()
|
||||
def test_up_with_healthcheck(self):
|
||||
def wait_on_health_status(container, status):
|
||||
def condition():
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ from compose.config.types import VolumeSpec
|
|||
from compose.const import LABEL_PROJECT
|
||||
from compose.const import LABEL_SERVICE
|
||||
from compose.container import Container
|
||||
from compose.errors import HealthCheckFailed
|
||||
from compose.errors import NoHealthCheckConfigured
|
||||
from compose.project import Project
|
||||
from compose.project import ProjectError
|
||||
from compose.service import ConvergenceStrategy
|
||||
|
|
@ -1375,3 +1377,115 @@ class ProjectTest(DockerClientTestCase):
|
|||
ctnr for ctnr in project._labeled_containers()
|
||||
if ctnr.labels.get(LABEL_SERVICE) == 'service1'
|
||||
]) == 0
|
||||
|
||||
@v2_1_only()
|
||||
def test_project_up_healthy_dependency(self):
|
||||
config_dict = {
|
||||
'version': '2.1',
|
||||
'services': {
|
||||
'svc1': {
|
||||
'image': 'busybox:latest',
|
||||
'command': 'top',
|
||||
'healthcheck': {
|
||||
'test': 'exit 0',
|
||||
'retries': 1,
|
||||
'timeout': '10s',
|
||||
'interval': '0.1s'
|
||||
},
|
||||
},
|
||||
'svc2': {
|
||||
'image': 'busybox:latest',
|
||||
'command': 'top',
|
||||
'depends_on': {
|
||||
'svc1': {'condition': 'service_healthy'},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
config_data = build_config(config_dict)
|
||||
project = Project.from_config(
|
||||
name='composetest', config_data=config_data, client=self.client
|
||||
)
|
||||
project.up()
|
||||
containers = project.containers()
|
||||
assert len(containers) == 2
|
||||
|
||||
svc1 = project.get_service('svc1')
|
||||
svc2 = project.get_service('svc2')
|
||||
assert 'svc1' in svc2.get_dependency_names()
|
||||
assert svc1.is_healthy()
|
||||
|
||||
@v2_1_only()
|
||||
def test_project_up_unhealthy_dependency(self):
|
||||
config_dict = {
|
||||
'version': '2.1',
|
||||
'services': {
|
||||
'svc1': {
|
||||
'image': 'busybox:latest',
|
||||
'command': 'top',
|
||||
'healthcheck': {
|
||||
'test': 'exit 1',
|
||||
'retries': 1,
|
||||
'timeout': '10s',
|
||||
'interval': '0.1s'
|
||||
},
|
||||
},
|
||||
'svc2': {
|
||||
'image': 'busybox:latest',
|
||||
'command': 'top',
|
||||
'depends_on': {
|
||||
'svc1': {'condition': 'service_healthy'},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
config_data = build_config(config_dict)
|
||||
project = Project.from_config(
|
||||
name='composetest', config_data=config_data, client=self.client
|
||||
)
|
||||
with pytest.raises(HealthCheckFailed):
|
||||
project.up()
|
||||
containers = project.containers()
|
||||
assert len(containers) == 1
|
||||
|
||||
svc1 = project.get_service('svc1')
|
||||
svc2 = project.get_service('svc2')
|
||||
assert 'svc1' in svc2.get_dependency_names()
|
||||
with pytest.raises(HealthCheckFailed):
|
||||
svc1.is_healthy()
|
||||
|
||||
@v2_1_only()
|
||||
def test_project_up_no_healthcheck_dependency(self):
|
||||
config_dict = {
|
||||
'version': '2.1',
|
||||
'services': {
|
||||
'svc1': {
|
||||
'image': 'busybox:latest',
|
||||
'command': 'top',
|
||||
'healthcheck': {
|
||||
'disabled': True
|
||||
},
|
||||
},
|
||||
'svc2': {
|
||||
'image': 'busybox:latest',
|
||||
'command': 'top',
|
||||
'depends_on': {
|
||||
'svc1': {'condition': 'service_healthy'},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
config_data = build_config(config_dict)
|
||||
project = Project.from_config(
|
||||
name='composetest', config_data=config_data, client=self.client
|
||||
)
|
||||
with pytest.raises(NoHealthCheckConfigured):
|
||||
project.up()
|
||||
containers = project.containers()
|
||||
assert len(containers) == 1
|
||||
|
||||
svc1 = project.get_service('svc1')
|
||||
svc2 = project.get_service('svc2')
|
||||
assert 'svc1' in svc2.get_dependency_names()
|
||||
with pytest.raises(NoHealthCheckConfigured):
|
||||
svc1.is_healthy()
|
||||
|
|
|
|||
|
|
@ -920,7 +920,10 @@ class ConfigTest(unittest.TestCase):
|
|||
'build': {'context': os.path.abspath('/')},
|
||||
'image': 'example/web',
|
||||
'volumes': [VolumeSpec.parse('/home/user/project:/code')],
|
||||
'depends_on': ['db', 'other'],
|
||||
'depends_on': {
|
||||
'db': {'condition': 'container_start'},
|
||||
'other': {'condition': 'container_start'},
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'db',
|
||||
|
|
@ -3055,7 +3058,9 @@ class ExtendsTest(unittest.TestCase):
|
|||
image: example
|
||||
""")
|
||||
services = load_from_filename(str(tmpdir.join('docker-compose.yml')))
|
||||
assert service_sort(services)[2]['depends_on'] == ['other']
|
||||
assert service_sort(services)[2]['depends_on'] == {
|
||||
'other': {'condition': 'container_start'}
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ deps = {
|
|||
|
||||
|
||||
def get_deps(obj):
|
||||
return deps[obj]
|
||||
return [(dep, None) for dep in deps[obj]]
|
||||
|
||||
|
||||
def test_parallel_execute():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue