mirror of https://github.com/docker/compose.git
Add support for start_period in healthcheck config
Improve merging strategy for healthcheck configs Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
22d9a258f4
commit
7210fdb21c
|
@ -797,16 +797,12 @@ def process_healthcheck(service_dict, service_name):
|
|||
elif 'test' in raw:
|
||||
hc['test'] = raw['test']
|
||||
|
||||
if 'interval' in raw:
|
||||
if not isinstance(raw['interval'], six.integer_types):
|
||||
hc['interval'] = parse_nanoseconds_int(raw['interval'])
|
||||
else: # Conversion has been done previously
|
||||
hc['interval'] = raw['interval']
|
||||
if 'timeout' in raw:
|
||||
if not isinstance(raw['timeout'], six.integer_types):
|
||||
hc['timeout'] = parse_nanoseconds_int(raw['timeout'])
|
||||
else: # Conversion has been done previously
|
||||
hc['timeout'] = raw['timeout']
|
||||
for field in ['interval', 'timeout', 'start_period']:
|
||||
if field in raw:
|
||||
if not isinstance(raw[field], six.integer_types):
|
||||
hc[field] = parse_nanoseconds_int(raw[field])
|
||||
else: # Conversion has been done previously
|
||||
hc[field] = raw[field]
|
||||
if 'retries' in raw:
|
||||
hc['retries'] = raw['retries']
|
||||
|
||||
|
@ -967,6 +963,7 @@ def merge_service_dicts(base, override, version):
|
|||
md.merge_field('logging', merge_logging, default={})
|
||||
merge_ports(md, base, override)
|
||||
md.merge_field('blkio_config', merge_blkio_config, default={})
|
||||
md.merge_field('healthcheck', merge_healthchecks, default={})
|
||||
|
||||
for field in set(ALLOWED_KEYS) - set(md):
|
||||
md.merge_scalar(field)
|
||||
|
@ -985,6 +982,14 @@ def merge_unique_items_lists(base, override):
|
|||
return sorted(set().union(base, override))
|
||||
|
||||
|
||||
def merge_healthchecks(base, override):
|
||||
if override.get('disabled') is True:
|
||||
return override
|
||||
result = base.copy()
|
||||
result.update(override)
|
||||
return result
|
||||
|
||||
|
||||
def merge_ports(md, base, override):
|
||||
def parse_sequence_func(seq):
|
||||
acc = []
|
||||
|
|
|
@ -309,6 +309,7 @@
|
|||
"disable": {"type": "boolean"},
|
||||
"interval": {"type": "string"},
|
||||
"retries": {"type": "number"},
|
||||
"start_period": {"type": "string"},
|
||||
"test": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
|
|
|
@ -131,6 +131,10 @@ def denormalize_service_dict(service_dict, version, image_digest=None):
|
|||
service_dict['healthcheck']['timeout']
|
||||
)
|
||||
|
||||
if 'start_period' in service_dict['healthcheck']:
|
||||
service_dict['healthcheck']['start_period'] = serialize_ns_time_value(
|
||||
service_dict['healthcheck']['start_period']
|
||||
)
|
||||
if 'ports' in service_dict and version < V3_2:
|
||||
service_dict['ports'] = [
|
||||
p.legacy_repr() if isinstance(p, types.ServicePort) else p
|
||||
|
|
|
@ -14,6 +14,7 @@ from docker.utils import parse_bytes as sdk_parse_bytes
|
|||
|
||||
from .config.errors import ConfigurationError
|
||||
from .errors import StreamParseError
|
||||
from .timeparse import MULTIPLIERS
|
||||
from .timeparse import timeparse
|
||||
|
||||
|
||||
|
@ -112,7 +113,7 @@ def microseconds_from_time_nano(time_nano):
|
|||
|
||||
|
||||
def nanoseconds_from_time_seconds(time_seconds):
|
||||
return time_seconds * 1000000000
|
||||
return int(time_seconds / MULTIPLIERS['nano'])
|
||||
|
||||
|
||||
def parse_seconds_float(value):
|
||||
|
@ -123,7 +124,7 @@ def parse_nanoseconds_int(value):
|
|||
parsed = timeparse(value or '')
|
||||
if parsed is None:
|
||||
return None
|
||||
return int(parsed * 1000000000)
|
||||
return nanoseconds_from_time_seconds(parsed)
|
||||
|
||||
|
||||
def build_string_dict(source_dict):
|
||||
|
|
|
@ -36,6 +36,7 @@ from compose.service import ConvergenceStrategy
|
|||
from compose.service import NetworkMode
|
||||
from compose.service import PidMode
|
||||
from compose.service import Service
|
||||
from compose.utils import parse_nanoseconds_int
|
||||
from tests.integration.testcases import is_cluster
|
||||
from tests.integration.testcases import no_cluster
|
||||
from tests.integration.testcases import v2_1_only
|
||||
|
@ -270,6 +271,24 @@ class ServiceTest(DockerClientTestCase):
|
|||
self.assertTrue(path.basename(actual_host_path) == path.basename(host_path),
|
||||
msg=("Last component differs: %s, %s" % (actual_host_path, host_path)))
|
||||
|
||||
def test_create_container_with_healthcheck_config(self):
|
||||
one_second = parse_nanoseconds_int('1s')
|
||||
healthcheck = {
|
||||
'test': ['true'],
|
||||
'interval': 2 * one_second,
|
||||
'timeout': 5 * one_second,
|
||||
'retries': 5,
|
||||
'start_period': 2 * one_second
|
||||
}
|
||||
service = self.create_service('db', healthcheck=healthcheck)
|
||||
container = service.create_container()
|
||||
remote_healthcheck = container.get('Config.Healthcheck')
|
||||
assert remote_healthcheck['Test'] == healthcheck['test']
|
||||
assert remote_healthcheck['Interval'] == healthcheck['interval']
|
||||
assert remote_healthcheck['Timeout'] == healthcheck['timeout']
|
||||
assert remote_healthcheck['Retries'] == healthcheck['retries']
|
||||
assert remote_healthcheck['StartPeriod'] == healthcheck['start_period']
|
||||
|
||||
def test_recreate_preserves_volume_with_trailing_slash(self):
|
||||
"""When the Compose file specifies a trailing slash in the container path, make
|
||||
sure we copy the volume over when recreating.
|
||||
|
|
|
@ -2197,6 +2197,75 @@ class ConfigTest(unittest.TestCase):
|
|||
}
|
||||
}
|
||||
|
||||
def test_merge_healthcheck_config(self):
|
||||
base = {
|
||||
'image': 'bar',
|
||||
'healthcheck': {
|
||||
'start_period': 1000,
|
||||
'interval': 3000,
|
||||
'test': ['true']
|
||||
}
|
||||
}
|
||||
|
||||
override = {
|
||||
'healthcheck': {
|
||||
'interval': 5000,
|
||||
'timeout': 10000,
|
||||
'test': ['echo', 'OK'],
|
||||
}
|
||||
}
|
||||
|
||||
actual = config.merge_service_dicts(base, override, V2_3)
|
||||
assert actual['healthcheck'] == {
|
||||
'start_period': base['healthcheck']['start_period'],
|
||||
'test': override['healthcheck']['test'],
|
||||
'interval': override['healthcheck']['interval'],
|
||||
'timeout': override['healthcheck']['timeout'],
|
||||
}
|
||||
|
||||
def test_merge_healthcheck_override_disables(self):
|
||||
base = {
|
||||
'image': 'bar',
|
||||
'healthcheck': {
|
||||
'start_period': 1000,
|
||||
'interval': 3000,
|
||||
'timeout': 2000,
|
||||
'retries': 3,
|
||||
'test': ['true']
|
||||
}
|
||||
}
|
||||
|
||||
override = {
|
||||
'healthcheck': {
|
||||
'disabled': True
|
||||
}
|
||||
}
|
||||
|
||||
actual = config.merge_service_dicts(base, override, V2_3)
|
||||
assert actual['healthcheck'] == {'disabled': True}
|
||||
|
||||
def test_merge_healthcheck_override_enables(self):
|
||||
base = {
|
||||
'image': 'bar',
|
||||
'healthcheck': {
|
||||
'disabled': True
|
||||
}
|
||||
}
|
||||
|
||||
override = {
|
||||
'healthcheck': {
|
||||
'disabled': False,
|
||||
'start_period': 1000,
|
||||
'interval': 3000,
|
||||
'timeout': 2000,
|
||||
'retries': 3,
|
||||
'test': ['true']
|
||||
}
|
||||
}
|
||||
|
||||
actual = config.merge_service_dicts(base, override, V2_3)
|
||||
assert actual['healthcheck'] == override['healthcheck']
|
||||
|
||||
def test_external_volume_config(self):
|
||||
config_details = build_config_details({
|
||||
'version': '2',
|
||||
|
@ -4008,6 +4077,7 @@ class HealthcheckTest(unittest.TestCase):
|
|||
'interval': '1s',
|
||||
'timeout': '1m',
|
||||
'retries': 3,
|
||||
'start_period': '10s'
|
||||
}},
|
||||
'.',
|
||||
)
|
||||
|
@ -4017,6 +4087,7 @@ class HealthcheckTest(unittest.TestCase):
|
|||
'interval': nanoseconds_from_time_seconds(1),
|
||||
'timeout': nanoseconds_from_time_seconds(60),
|
||||
'retries': 3,
|
||||
'start_period': nanoseconds_from_time_seconds(10)
|
||||
}
|
||||
|
||||
def test_disable(self):
|
||||
|
@ -4147,15 +4218,17 @@ class SerializeTest(unittest.TestCase):
|
|||
'test': 'exit 1',
|
||||
'interval': '1m40s',
|
||||
'timeout': '30s',
|
||||
'retries': 5
|
||||
'retries': 5,
|
||||
'start_period': '2s90ms'
|
||||
}
|
||||
}
|
||||
processed_service = config.process_service(config.ServiceConfig(
|
||||
'.', 'test', 'test', service_dict
|
||||
))
|
||||
denormalized_service = denormalize_service_dict(processed_service, V2_1)
|
||||
denormalized_service = denormalize_service_dict(processed_service, V2_3)
|
||||
assert denormalized_service['healthcheck']['interval'] == '100s'
|
||||
assert denormalized_service['healthcheck']['timeout'] == '30s'
|
||||
assert denormalized_service['healthcheck']['start_period'] == '2090ms'
|
||||
|
||||
def test_denormalize_image_has_digest(self):
|
||||
service_dict = {
|
||||
|
|
Loading…
Reference in New Issue