Support health check for Docker containers
The docker has supported healthcheck. It is useful for application container. Zun may integrate these options. --health-cmd Command to run to check health --health-interval Time between running the check --health-retries Consecutive failures needed to report unhealthy --health-timeout Maximum time to allow one check to run In zun, the four parameter are cmd, interval, retries, timeout. Partial-Implements: blueprint support-healthycheck Change-Id: I2f9f48aef17f0d92835db13c6112550bda5e31bf
This commit is contained in:
@@ -31,7 +31,7 @@ if not LOG.handlers:
|
||||
HEADER_NAME = "OpenStack-API-Version"
|
||||
SERVICE_TYPE = "container"
|
||||
MIN_API_VERSION = '1.1'
|
||||
MAX_API_VERSION = '1.21'
|
||||
MAX_API_VERSION = '1.22'
|
||||
DEFAULT_API_VERSION = MAX_API_VERSION
|
||||
|
||||
_SUBSTITUTIONS = {}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
from oslo_utils import netutils
|
||||
from six.moves.urllib import parse
|
||||
@@ -276,6 +277,52 @@ def parse_nets(ns):
|
||||
return nets
|
||||
|
||||
|
||||
def parse_health(hc_str):
|
||||
err_msg = ("Invalid healthcheck argument '%s'. healthcheck arguments"
|
||||
" must be of the form --healthcheck <cmd='command',"
|
||||
"interval=time,retries=integer,timeout=time>, and the unit "
|
||||
"of time is s(seconds), m(minutes), h(hours).") % hc_str
|
||||
keys = ["cmd", "interval", "retries", "timeout"]
|
||||
health_info = {}
|
||||
for kv_str in hc_str[0].split(","):
|
||||
try:
|
||||
k, v = kv_str.split("=", 1)
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
except ValueError:
|
||||
raise apiexec.CommandError(err_msg)
|
||||
if k in keys:
|
||||
if health_info.get(k):
|
||||
raise apiexec.CommandError(err_msg)
|
||||
elif k in ['interval', 'timeout']:
|
||||
health_info[k] = _convert_healthcheck_para(v, err_msg)
|
||||
elif k == "retries":
|
||||
health_info[k] = int(v)
|
||||
else:
|
||||
health_info[k] = v
|
||||
else:
|
||||
raise apiexec.CommandError(err_msg)
|
||||
return health_info
|
||||
|
||||
|
||||
def _convert_healthcheck_para(time, err_msg):
|
||||
int_pattern = '^\d+$'
|
||||
time_pattern = '^\d+(s|m|h)$'
|
||||
ret = 0
|
||||
if re.match(int_pattern, time):
|
||||
ret = int(time)
|
||||
elif re.match(time_pattern, time):
|
||||
if time.endswith('s'):
|
||||
ret = int(time.split('s')[0])
|
||||
elif time.endswith('m'):
|
||||
ret = int(time.split('m')[0]) * 60
|
||||
elif time.endswith('h'):
|
||||
ret = int(time.split('h')[0]) * 3600
|
||||
else:
|
||||
raise apiexec.CommandError(err_msg)
|
||||
return ret
|
||||
|
||||
|
||||
def normalise_file_path_to_url(path):
|
||||
if parse.urlparse(path).scheme:
|
||||
return path
|
||||
|
||||
@@ -185,6 +185,19 @@ class CreateContainer(command.ShowOne):
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Give extended privileges to this container')
|
||||
parser.add_argument(
|
||||
'--healthcheck',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar='<cmd=test_cmd,interval=time,retries=n,timeout=time>',
|
||||
help='Specify a test cmd to perform to check that the container'
|
||||
'is healthy. '
|
||||
'cmd: Command to run to check health. '
|
||||
'interval: Time between running the check (|s|m|h)'
|
||||
' (default 0s). '
|
||||
'retries: Consecutive failures needed to report unhealthy.'
|
||||
'timeout: Maximum time to allow one check to run (s|m|h)'
|
||||
' (default 0s).')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -218,6 +231,9 @@ class CreateContainer(command.ShowOne):
|
||||
opts['disk'] = parsed_args.disk
|
||||
opts['availability_zone'] = parsed_args.availability_zone
|
||||
opts['auto_heal'] = parsed_args.auto_heal
|
||||
if parsed_args.healthcheck:
|
||||
opts['healthcheck'] = \
|
||||
zun_utils.parse_health(parsed_args.healthcheck)
|
||||
|
||||
opts = zun_utils.remove_null_parms(**opts)
|
||||
container = client.containers.create(**opts)
|
||||
@@ -811,6 +827,19 @@ class RunContainer(command.ShowOne):
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Give extended privileges to this container')
|
||||
parser.add_argument(
|
||||
'--healthcheck',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar='<cmd=test_cmd,interval=time,retries=n,timeout=time>',
|
||||
help='Specify a test cmd to perform to check that the container'
|
||||
'is healthy. '
|
||||
'cmd: Command to run to check health. '
|
||||
'interval: Time between running the check (s|m|h)'
|
||||
' (default 0s). '
|
||||
'retries: Consecutive failures needed to report unhealthy.'
|
||||
'timeout: Maximum time to allow one check to run (s|m|h)'
|
||||
' (default 0s).')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@@ -844,6 +873,9 @@ class RunContainer(command.ShowOne):
|
||||
opts['disk'] = parsed_args.disk
|
||||
opts['availability_zone'] = parsed_args.availability_zone
|
||||
opts['auto_heal'] = parsed_args.auto_heal
|
||||
if parsed_args.healthcheck:
|
||||
opts['healthcheck'] = \
|
||||
zun_utils.parse_health(parsed_args.healthcheck)
|
||||
|
||||
opts = zun_utils.remove_null_parms(**opts)
|
||||
container = client.containers.run(**opts)
|
||||
|
||||
@@ -246,7 +246,7 @@ class ShellTest(utils.TestCase):
|
||||
project_domain_id='', project_domain_name='',
|
||||
user_domain_id='', user_domain_name='', profile=None,
|
||||
endpoint_override=None, insecure=False, cacert=None,
|
||||
version=api_versions.APIVersion('1.21'))
|
||||
version=api_versions.APIVersion('1.22'))
|
||||
|
||||
def test_main_option_region(self):
|
||||
self.make_env()
|
||||
@@ -274,7 +274,7 @@ class ShellTest(utils.TestCase):
|
||||
project_domain_id='', project_domain_name='',
|
||||
user_domain_id='', user_domain_name='', profile=None,
|
||||
endpoint_override=None, insecure=False, cacert=None,
|
||||
version=api_versions.APIVersion('1.21'))
|
||||
version=api_versions.APIVersion('1.22'))
|
||||
|
||||
@mock.patch('zunclient.client.Client')
|
||||
def test_main_endpoint_internal(self, mock_client):
|
||||
@@ -288,7 +288,7 @@ class ShellTest(utils.TestCase):
|
||||
project_domain_id='', project_domain_name='',
|
||||
user_domain_id='', user_domain_name='', profile=None,
|
||||
endpoint_override=None, insecure=False, cacert=None,
|
||||
version=api_versions.APIVersion('1.21'))
|
||||
version=api_versions.APIVersion('1.22'))
|
||||
|
||||
|
||||
class ShellTestKeystoneV3(ShellTest):
|
||||
@@ -320,4 +320,4 @@ class ShellTestKeystoneV3(ShellTest):
|
||||
user_domain_id='', user_domain_name='Default',
|
||||
endpoint_override=None, insecure=False, profile=None,
|
||||
cacert=None,
|
||||
version=api_versions.APIVersion('1.21'))
|
||||
version=api_versions.APIVersion('1.22'))
|
||||
|
||||
@@ -38,6 +38,7 @@ CONTAINER1 = {'id': '1234',
|
||||
'disk': '20',
|
||||
'auto_heal': False,
|
||||
'privileged': False,
|
||||
'healthcheck': {}
|
||||
}
|
||||
|
||||
CONTAINER2 = {'id': '1235',
|
||||
@@ -59,6 +60,7 @@ CONTAINER2 = {'id': '1235',
|
||||
'hostname': 'testhost',
|
||||
'auto_heal': False,
|
||||
'privileged': True,
|
||||
'healthcheck': {}
|
||||
}
|
||||
|
||||
NETWORK1 = {'net_id': '99e90853-e1fd-4c57-a116-9e335deaa592',
|
||||
|
||||
@@ -25,7 +25,8 @@ CREATION_ATTRIBUTES = ['name', 'image', 'command', 'cpu', 'memory',
|
||||
'restart_policy', 'interactive', 'image_driver',
|
||||
'security_groups', 'hints', 'nets', 'auto_remove',
|
||||
'runtime', 'hostname', 'mounts', 'disk',
|
||||
'availability_zone', 'auto_heal', 'privileged']
|
||||
'availability_zone', 'auto_heal', 'privileged',
|
||||
'healthcheck']
|
||||
|
||||
|
||||
class Container(base.Resource):
|
||||
|
||||
@@ -153,6 +153,18 @@ def _show_container(container):
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Give extended privileges to this container')
|
||||
@utils.arg('--healthcheck',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar='<cmd=command,interval=time,retries=integer,timeout=time>',
|
||||
help='Specify a test cmd to perform to check that the container'
|
||||
'is healthy. '
|
||||
'cmd: Command to run to check health. '
|
||||
'interval: Time between running the check (s|m|h)'
|
||||
' (default 0s). '
|
||||
'retries: Consecutive failures needed to report unhealthy. '
|
||||
'timeout: Maximum time to allow one check to run (s|m|h)'
|
||||
' (default 0s).')
|
||||
def do_create(cs, args):
|
||||
"""Create a container."""
|
||||
opts = {}
|
||||
@@ -175,6 +187,8 @@ def do_create(cs, args):
|
||||
opts['availability_zone'] = args.availability_zone
|
||||
opts['auto_heal'] = args.auto_heal
|
||||
opts['command'] = args.command
|
||||
if args.healthcheck:
|
||||
opts['healthcheck'] = zun_utils.parse_health(args.healthcheck)
|
||||
|
||||
if args.security_group:
|
||||
opts['security_groups'] = args.security_group
|
||||
@@ -647,6 +661,18 @@ def do_kill(cs, args):
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Give extended privileges to this container')
|
||||
@utils.arg('--healthcheck',
|
||||
action='append',
|
||||
default=[],
|
||||
metavar='<cmd=command,interval=time,retries=integer,timeout=time>',
|
||||
help='Specify a test cmd to perform to check that the container'
|
||||
'is healthy. '
|
||||
'cmd: Command to run to check health. '
|
||||
'interval: Time between running the check (s|m|h)'
|
||||
' (default 0s). '
|
||||
'retries: Consecutive failures needed to report unhealthy. '
|
||||
'timeout: Maximum time to allow one check to run (s|m|h)'
|
||||
' (default 0s).')
|
||||
def do_run(cs, args):
|
||||
"""Run a command in a new container."""
|
||||
opts = {}
|
||||
@@ -669,6 +695,8 @@ def do_run(cs, args):
|
||||
opts['availability_zone'] = args.availability_zone
|
||||
opts['auto_heal'] = args.auto_heal
|
||||
opts['command'] = args.command
|
||||
if args.healthcheck:
|
||||
opts['healthcheck'] = zun_utils.parse_health(args.healthcheck)
|
||||
|
||||
if args.security_group:
|
||||
opts['security_groups'] = args.security_group
|
||||
|
||||
Reference in New Issue
Block a user