Support health check for Docker containers
The docker has supported healthcheck. It is useful for application container. Zun may integration 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: I1f4f6a5536a33ecb9ac9c032b119a9349e3616ee
This commit is contained in:
parent
99816f5d23
commit
68df96cb2f
@ -355,6 +355,12 @@ class ContainersController(base.Controller):
|
||||
pci_req = self._create_pci_requests_for_sriov_ports(context,
|
||||
requested_networks)
|
||||
|
||||
healthcheck = container_dict.pop('healthcheck', {})
|
||||
if healthcheck:
|
||||
api_utils.version_check('healthcheck', '1.22')
|
||||
healthcheck['test'] = healthcheck.pop('cmd', '')
|
||||
container_dict['healthcheck'] = healthcheck
|
||||
|
||||
mounts = container_dict.pop('mounts', [])
|
||||
if mounts:
|
||||
api_utils.version_check('mounts', '1.11')
|
||||
|
@ -38,6 +38,7 @@ _legacy_container_properties = {
|
||||
'availability_zone': parameter_types.availability_zone,
|
||||
'auto_heal': parameter_types.boolean,
|
||||
'privileged': parameter_types.boolean,
|
||||
'healthcheck': parameter_types.healthcheck,
|
||||
}
|
||||
|
||||
legacy_container_create = {
|
||||
|
@ -172,6 +172,30 @@ availability_zone = {
|
||||
'maxLength': 255,
|
||||
}
|
||||
|
||||
healthcheck = {
|
||||
'type': ['object', 'null'],
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'cmd': {
|
||||
'type': ['string'],
|
||||
'minLength': 1,
|
||||
'maxLength': 255
|
||||
},
|
||||
'interval': {
|
||||
'type': ['integer', 'null']
|
||||
},
|
||||
'retries': {
|
||||
'type': ['integer', 'null']
|
||||
},
|
||||
'timeout': {
|
||||
'type': ['integer', 'null']
|
||||
}
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
||||
}
|
||||
|
||||
mounts = {
|
||||
'type': ['array', 'null'],
|
||||
'items': {
|
||||
|
@ -48,6 +48,7 @@ _basic_keys = (
|
||||
'disk',
|
||||
'auto_heal',
|
||||
'privileged',
|
||||
'healthcheck',
|
||||
)
|
||||
|
||||
|
||||
|
@ -51,13 +51,14 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
* 1.16 - Modify restart_policy to capsule spec content
|
||||
* 1.17 - Add support for detaching ports
|
||||
* 1.18 - Modify the response of network list
|
||||
* 1.19 - Intoduce container resize API
|
||||
* 1.19 - Introduce container resize API
|
||||
* 1.20 - Convert type of 'command' from string to list
|
||||
* 1.21 - Add support privileged
|
||||
* 1.22 - Add healthcheck to container create
|
||||
"""
|
||||
|
||||
BASE_VER = '1.1'
|
||||
CURRENT_MAX_VER = '1.21'
|
||||
CURRENT_MAX_VER = '1.22'
|
||||
|
||||
|
||||
class Version(object):
|
||||
|
@ -177,3 +177,9 @@ user documentation.
|
||||
----
|
||||
|
||||
Support privileged container
|
||||
|
||||
1.22
|
||||
----
|
||||
|
||||
Add healthcheck to container create
|
||||
|
||||
|
@ -301,6 +301,18 @@ class DockerDriver(driver.ContainerDriver):
|
||||
if container.disk:
|
||||
disk_size = str(container.disk) + 'G'
|
||||
host_config['storage_opt'] = {'size': disk_size}
|
||||
# The time unit in docker of heath checking is us, and the unit
|
||||
# of interval and timeout is seconds.
|
||||
if container.healthcheck:
|
||||
healthcheck = {}
|
||||
healthcheck['test'] = container.healthcheck.get('test', '')
|
||||
interval = container.healthcheck.get('interval', 0)
|
||||
healthcheck['interval'] = interval * 10 ** 9
|
||||
healthcheck['retries'] = int(container.healthcheck.
|
||||
get('retries', 0))
|
||||
timeout = container.healthcheck.get('timeout', 0)
|
||||
healthcheck['timeout'] = timeout * 10 ** 9
|
||||
kwargs['healthcheck'] = healthcheck
|
||||
|
||||
kwargs['host_config'] = docker.create_host_config(**host_config)
|
||||
if image['tag']:
|
||||
|
@ -0,0 +1,36 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""add healthcheck to containers
|
||||
|
||||
Revision ID: 2fb377a5a519
|
||||
Revises: 105626c4f972
|
||||
Create Date: 2018-05-03 11:27:00.722445
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2fb377a5a519'
|
||||
down_revision = '105626c4f972'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import zun
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.batch_alter_table('container', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column(
|
||||
'healthcheck', zun.db.sqlalchemy.models.JSONEncodedDict(),
|
||||
nullable=True))
|
@ -171,6 +171,7 @@ class Container(Base):
|
||||
ForeignKey('capsule.id', ondelete='CASCADE'))
|
||||
started_at = Column(DateTime)
|
||||
privileged = Column(Boolean, default=False)
|
||||
healthcheck = Column(JSONEncodedDict)
|
||||
|
||||
|
||||
class VolumeMapping(Base):
|
||||
|
@ -64,7 +64,8 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
# Version 1.32: Add 'exec_instances' attribute
|
||||
# Version 1.33: Change 'command' to List type
|
||||
# Version 1.34: Add privileged to container
|
||||
VERSION = '1.34'
|
||||
# Version 1.35: Add 'healthcheck' attribute
|
||||
VERSION = '1.35'
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
@ -107,6 +108,7 @@ class Container(base.ZunPersistentObject, base.ZunObject):
|
||||
'exec_instances': fields.ListOfObjectsField('ExecInstance',
|
||||
nullable=True),
|
||||
'privileged': fields.BooleanField(nullable=True),
|
||||
'healthcheck': z_fields.JsonField(nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
@ -26,7 +26,7 @@ from zun.tests.unit.db import base
|
||||
|
||||
|
||||
PATH_PREFIX = '/v1'
|
||||
CURRENT_VERSION = "container 1.21"
|
||||
CURRENT_VERSION = "container 1.22"
|
||||
|
||||
|
||||
class FunctionalTest(base.DbTestCase):
|
||||
|
@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest):
|
||||
'default_version':
|
||||
{'id': 'v1',
|
||||
'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}],
|
||||
'max_version': '1.21',
|
||||
'max_version': '1.22',
|
||||
'min_version': '1.1',
|
||||
'status': 'CURRENT'},
|
||||
'description': 'Zun is an OpenStack project which '
|
||||
@ -37,7 +37,7 @@ class TestRootController(api_base.FunctionalTest):
|
||||
'versions': [{'id': 'v1',
|
||||
'links': [{'href': 'http://localhost/v1/',
|
||||
'rel': 'self'}],
|
||||
'max_version': '1.21',
|
||||
'max_version': '1.22',
|
||||
'min_version': '1.1',
|
||||
'status': 'CURRENT'}]}
|
||||
|
||||
|
@ -121,6 +121,16 @@ class TestContainerController(api_base.FunctionalTest):
|
||||
params=params, content_type='application/json',
|
||||
headers=headers)
|
||||
|
||||
def test_run_container_healthcheck_wrong_value(self):
|
||||
params = ('{"name": "MyDocker", "image": "ubuntu",'
|
||||
'"command": ["env"], "memory": "512",'
|
||||
'"environment": {"key1": "val1", "key2": "val2"},'
|
||||
'"healthcheck": "test"}')
|
||||
with self.assertRaisesRegex(AppError,
|
||||
"Invalid input for field"):
|
||||
self.post('/v1/containers?run=true',
|
||||
params=params, content_type='application/json')
|
||||
|
||||
@patch('zun.network.neutron.NeutronAPI.get_available_network')
|
||||
@patch('zun.compute.api.API.container_create')
|
||||
@patch('zun.compute.api.API.image_search')
|
||||
|
@ -117,6 +117,7 @@ class TestDockerDriver(base.DriverTestCase):
|
||||
image = {'path': '', 'image': '', 'repo': 'test', 'tag': 'test'}
|
||||
mock_container = self.mock_default_container
|
||||
mock_container.status = 'Creating'
|
||||
mock_container.healthcheck = {}
|
||||
networks = [{'network': 'fake-network'}]
|
||||
volumes = []
|
||||
fake_port = {'mac_address': 'fake_mac'}
|
||||
@ -183,6 +184,7 @@ class TestDockerDriver(base.DriverTestCase):
|
||||
image = {'path': '', 'image': '', 'repo': 'test', 'tag': 'test'}
|
||||
mock_container = self.mock_default_container
|
||||
mock_container.status = 'Creating'
|
||||
mock_container.healthcheck = {}
|
||||
networks = [{'network': 'fake-network'}]
|
||||
volumes = []
|
||||
fake_port = {'mac_address': 'fake_mac'}
|
||||
@ -251,6 +253,7 @@ class TestDockerDriver(base.DriverTestCase):
|
||||
image = {'path': '', 'image': '', 'repo': 'test', 'tag': 'test'}
|
||||
mock_container = self.mock_default_container
|
||||
mock_container.status = 'Creating'
|
||||
mock_container.healthcheck = {}
|
||||
mock_container.runtime = None
|
||||
networks = [{'network': 'fake-network'}]
|
||||
volumes = []
|
||||
|
@ -103,6 +103,10 @@ def get_test_container(**kwargs):
|
||||
'capsule_id': kwargs.get('capsule_id', 42),
|
||||
'started_at': kwargs.get('started_at'),
|
||||
'privileged': kwargs.get('privileged', False),
|
||||
'healthcheck': kwargs.get('healthcheck',
|
||||
{"retries": "2", "timeout": 3,
|
||||
"test": "stat /etc/passwd || exit 1",
|
||||
"interval": 3}),
|
||||
}
|
||||
|
||||
|
||||
|
@ -344,7 +344,7 @@ class TestObject(test_base.TestCase, _TestObject):
|
||||
# For more information on object version testing, read
|
||||
# https://docs.openstack.org/zun/latest/
|
||||
object_data = {
|
||||
'Container': '1.34-22c46c6ae571b83295c3dac74fe8772f',
|
||||
'Container': '1.35-7cadf071bb6865a6da6b7be581ce76f6',
|
||||
'VolumeMapping': '1.1-50df6202f7846a136a91444c38eba841',
|
||||
'Image': '1.1-330e6205c80b99b59717e1cfc6a79935',
|
||||
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
|
||||
|
Loading…
x
Reference in New Issue
Block a user