mirror of https://github.com/docker/docker-py.git
Merge pull request #1294 from aanand/add-healthcheck
Add support for passing healthcheck to create_container
This commit is contained in:
commit
2ff7371ae7
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
Loading…
Reference in New Issue