Merge pull request #1294 from aanand/add-healthcheck

Add support for passing healthcheck to create_container
This commit is contained in:
Aanand Prasad 2016-11-16 16:12:05 +00:00 committed by GitHub
commit 2ff7371ae7
6 changed files with 130 additions and 5 deletions

View File

@ -115,7 +115,8 @@ class ContainerApiMixin(object):
cpu_shares=None, working_dir=None, domainname=None, cpu_shares=None, working_dir=None, domainname=None,
memswap_limit=None, cpuset=None, host_config=None, memswap_limit=None, cpuset=None, host_config=None,
mac_address=None, labels=None, volume_driver=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): if isinstance(volumes, six.string_types):
volumes = [volumes, ] volumes = [volumes, ]
@ -130,7 +131,7 @@ class ContainerApiMixin(object):
tty, mem_limit, ports, environment, dns, volumes, volumes_from, tty, mem_limit, ports, environment, dns, volumes, volumes_from,
network_disabled, entrypoint, cpu_shares, working_dir, domainname, network_disabled, entrypoint, cpu_shares, working_dir, domainname,
memswap_limit, cpuset, host_config, mac_address, labels, 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) 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, cap_drop=cap_drop, volumes_from=volumes_from, devices=devices,
network_mode=network_mode, restart_policy=restart_policy, network_mode=network_mode, restart_policy=restart_policy,
extra_hosts=extra_hosts, read_only=read_only, pid_mode=pid_mode, 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 start_config = None

View File

@ -4,4 +4,5 @@ from .services import (
ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy, ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy,
TaskTemplate, UpdateConfig TaskTemplate, UpdateConfig
) )
from .healthcheck import Healthcheck
from .swarm import SwarmSpec, SwarmExternalCA from .swarm import SwarmSpec, SwarmExternalCA

View File

@ -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

View File

@ -18,7 +18,7 @@ import six
from .. import constants from .. import constants
from .. import errors from .. import errors
from .. import tls from .. import tls
from ..types import Ulimit, LogConfig from ..types import Ulimit, LogConfig, Healthcheck
if six.PY2: if six.PY2:
from urllib import splitnport from urllib import splitnport
@ -1029,6 +1029,7 @@ def create_container_config(
entrypoint=None, cpu_shares=None, working_dir=None, domainname=None, entrypoint=None, cpu_shares=None, working_dir=None, domainname=None,
memswap_limit=None, cpuset=None, host_config=None, mac_address=None, memswap_limit=None, cpuset=None, host_config=None, mac_address=None,
labels=None, volume_driver=None, stop_signal=None, networking_config=None, labels=None, volume_driver=None, stop_signal=None, networking_config=None,
healthcheck=None,
): ):
if isinstance(command, six.string_types): if isinstance(command, six.string_types):
command = split_command(command) command = split_command(command)
@ -1057,6 +1058,11 @@ def create_container_config(
'stop_signal was only introduced in API version 1.21' '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 compare_version('1.19', version) < 0:
if volume_driver is not None: if volume_driver is not None:
raise errors.InvalidVersion( raise errors.InvalidVersion(
@ -1113,6 +1119,9 @@ def create_container_config(
# Force None, an empty list or dict causes client.start to fail # Force None, an empty list or dict causes client.start to fail
volumes_from = None volumes_from = None
if healthcheck and isinstance(healthcheck, dict):
healthcheck = Healthcheck(**healthcheck)
attach_stdin = False attach_stdin = False
attach_stdout = False attach_stdout = False
attach_stderr = False attach_stderr = False
@ -1164,5 +1173,6 @@ def create_container_config(
'MacAddress': mac_address, 'MacAddress': mac_address,
'Labels': labels, 'Labels': labels,
'VolumeDriver': volume_driver, 'VolumeDriver': volume_driver,
'StopSignal': stop_signal 'StopSignal': stop_signal,
'Healthcheck': healthcheck,
} }

View File

@ -2,6 +2,7 @@ import os
import os.path import os.path
import tarfile import tarfile
import tempfile import tempfile
import time
import docker import docker
import pytest import pytest
@ -47,3 +48,11 @@ def requires_api_version(version):
), ),
reason="API version is too low (< {0})".format(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)

View File

@ -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")