diff options
| author | Aanand Prasad <aanand.prasad@gmail.com> | 2016-11-16 16:12:05 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-11-16 16:12:05 +0000 |
| commit | 2ff7371ae7703033f981e1b137a3be0caf7a4f9c (patch) | |
| tree | 6058dfc282e6612275222d54f1f4f5757a6d4950 | |
| parent | f262dd6da983300f00c5969f5af03492bef07508 (diff) | |
| parent | e4b6d0dca6d667c6defd10a9d507f7b4c11e6eb2 (diff) | |
| download | docker-py-2ff7371ae7703033f981e1b137a3be0caf7a4f9c.tar.gz | |
Merge pull request #1294 from aanand/add-healthcheck
Add support for passing healthcheck to create_container
| -rw-r--r-- | docker/api/container.py | 7 | ||||
| -rw-r--r-- | docker/types/__init__.py | 1 | ||||
| -rw-r--r-- | docker/types/healthcheck.py | 53 | ||||
| -rw-r--r-- | docker/utils/utils.py | 14 | ||||
| -rw-r--r-- | tests/helpers.py | 9 | ||||
| -rw-r--r-- | tests/integration/healthcheck_test.py | 51 |
6 files changed, 130 insertions, 5 deletions
diff --git a/docker/api/container.py b/docker/api/container.py index d71d17a..338b79f 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -115,7 +115,8 @@ class ContainerApiMixin(object): cpu_shares=None, working_dir=None, domainname=None, memswap_limit=None, cpuset=None, host_config=None, mac_address=None, labels=None, volume_driver=None, - stop_signal=None, networking_config=None): + stop_signal=None, networking_config=None, + healthcheck=None): if isinstance(volumes, six.string_types): volumes = [volumes, ] @@ -130,7 +131,7 @@ class ContainerApiMixin(object): tty, mem_limit, ports, environment, dns, volumes, volumes_from, network_disabled, entrypoint, cpu_shares, working_dir, domainname, memswap_limit, cpuset, host_config, mac_address, labels, - volume_driver, stop_signal, networking_config, + volume_driver, stop_signal, networking_config, healthcheck, ) return self.create_container_from_config(config, name) @@ -365,7 +366,7 @@ class ContainerApiMixin(object): cap_drop=cap_drop, volumes_from=volumes_from, devices=devices, network_mode=network_mode, restart_policy=restart_policy, extra_hosts=extra_hosts, read_only=read_only, pid_mode=pid_mode, - ipc_mode=ipc_mode, security_opt=security_opt, ulimits=ulimits + ipc_mode=ipc_mode, security_opt=security_opt, ulimits=ulimits, ) start_config = None diff --git a/docker/types/__init__.py b/docker/types/__init__.py index 71c0c97..a7c3a56 100644 --- a/docker/types/__init__.py +++ b/docker/types/__init__.py @@ -4,4 +4,5 @@ from .services import ( ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy, TaskTemplate, UpdateConfig ) +from .healthcheck import Healthcheck from .swarm import SwarmSpec, SwarmExternalCA diff --git a/docker/types/healthcheck.py b/docker/types/healthcheck.py new file mode 100644 index 0000000..ba63d21 --- /dev/null +++ b/docker/types/healthcheck.py @@ -0,0 +1,53 @@ +from .base import DictType + +import six + + +class Healthcheck(DictType): + def __init__(self, **kwargs): + test = kwargs.get('test', kwargs.get('Test')) + if isinstance(test, six.string_types): + test = ["CMD-SHELL", test] + + interval = kwargs.get('interval', kwargs.get('Interval')) + timeout = kwargs.get('timeout', kwargs.get('Timeout')) + retries = kwargs.get('retries', kwargs.get('Retries')) + + super(Healthcheck, self).__init__({ + 'Test': test, + 'Interval': interval, + 'Timeout': timeout, + 'Retries': retries + }) + + @property + def test(self): + return self['Test'] + + @test.setter + def test(self, value): + self['Test'] = value + + @property + def interval(self): + return self['Interval'] + + @interval.setter + def interval(self, value): + self['Interval'] = value + + @property + def timeout(self): + return self['Timeout'] + + @timeout.setter + def timeout(self, value): + self['Timeout'] = value + + @property + def retries(self): + return self['Retries'] + + @retries.setter + def retries(self, value): + self['Retries'] = value diff --git a/docker/utils/utils.py b/docker/utils/utils.py index 97261cd..f61b5dd 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -18,7 +18,7 @@ import six from .. import constants from .. import errors from .. import tls -from ..types import Ulimit, LogConfig +from ..types import Ulimit, LogConfig, Healthcheck if six.PY2: from urllib import splitnport @@ -1029,6 +1029,7 @@ def create_container_config( entrypoint=None, cpu_shares=None, working_dir=None, domainname=None, memswap_limit=None, cpuset=None, host_config=None, mac_address=None, labels=None, volume_driver=None, stop_signal=None, networking_config=None, + healthcheck=None, ): if isinstance(command, six.string_types): command = split_command(command) @@ -1057,6 +1058,11 @@ def create_container_config( 'stop_signal was only introduced in API version 1.21' ) + if healthcheck is not None and version_lt(version, '1.24'): + raise errors.InvalidVersion( + 'Health options were only introduced in API version 1.24' + ) + if compare_version('1.19', version) < 0: if volume_driver is not None: raise errors.InvalidVersion( @@ -1113,6 +1119,9 @@ def create_container_config( # Force None, an empty list or dict causes client.start to fail volumes_from = None + if healthcheck and isinstance(healthcheck, dict): + healthcheck = Healthcheck(**healthcheck) + attach_stdin = False attach_stdout = False attach_stderr = False @@ -1164,5 +1173,6 @@ def create_container_config( 'MacAddress': mac_address, 'Labels': labels, 'VolumeDriver': volume_driver, - 'StopSignal': stop_signal + 'StopSignal': stop_signal, + 'Healthcheck': healthcheck, } diff --git a/tests/helpers.py b/tests/helpers.py index 529b727..f8b3e61 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -2,6 +2,7 @@ import os import os.path import tarfile import tempfile +import time import docker import pytest @@ -47,3 +48,11 @@ def requires_api_version(version): ), reason="API version is too low (< {0})".format(version) ) + + +def wait_on_condition(condition, delay=0.1, timeout=40): + start_time = time.time() + while not condition(): + if time.time() - start_time > timeout: + raise AssertionError("Timeout: %s" % condition) + time.sleep(delay) diff --git a/tests/integration/healthcheck_test.py b/tests/integration/healthcheck_test.py new file mode 100644 index 0000000..9c0f398 --- /dev/null +++ b/tests/integration/healthcheck_test.py @@ -0,0 +1,51 @@ +from .base import BaseIntegrationTest +from .base import BUSYBOX +from .. import helpers + +SECOND = 1000000000 + + +def wait_on_health_status(client, container, status): + def condition(): + res = client.inspect_container(container) + return res['State']['Health']['Status'] == status + return helpers.wait_on_condition(condition) + + +class HealthcheckTest(BaseIntegrationTest): + + @helpers.requires_api_version('1.24') + def test_healthcheck_shell_command(self): + container = self.client.create_container( + BUSYBOX, 'top', healthcheck=dict(test='echo "hello world"')) + self.tmp_containers.append(container) + + res = self.client.inspect_container(container) + assert res['Config']['Healthcheck']['Test'] == \ + ['CMD-SHELL', 'echo "hello world"'] + + @helpers.requires_api_version('1.24') + def test_healthcheck_passes(self): + container = self.client.create_container( + BUSYBOX, 'top', healthcheck=dict( + test="true", + interval=1*SECOND, + timeout=1*SECOND, + retries=1, + )) + self.tmp_containers.append(container) + self.client.start(container) + wait_on_health_status(self.client, container, "healthy") + + @helpers.requires_api_version('1.24') + def test_healthcheck_fails(self): + container = self.client.create_container( + BUSYBOX, 'top', healthcheck=dict( + test="false", + interval=1*SECOND, + timeout=1*SECOND, + retries=1, + )) + self.tmp_containers.append(container) + self.client.start(container) + wait_on_health_status(self.client, container, "unhealthy") |
