summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAanand Prasad <aanand.prasad@gmail.com>2016-11-16 16:12:05 +0000
committerGitHub <noreply@github.com>2016-11-16 16:12:05 +0000
commit2ff7371ae7703033f981e1b137a3be0caf7a4f9c (patch)
tree6058dfc282e6612275222d54f1f4f5757a6d4950
parentf262dd6da983300f00c5969f5af03492bef07508 (diff)
parente4b6d0dca6d667c6defd10a9d507f7b4c11e6eb2 (diff)
downloaddocker-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.py7
-rw-r--r--docker/types/__init__.py1
-rw-r--r--docker/types/healthcheck.py53
-rw-r--r--docker/utils/utils.py14
-rw-r--r--tests/helpers.py9
-rw-r--r--tests/integration/healthcheck_test.py51
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")