Merge "Added support for Auto remove the container"

This commit is contained in:
Jenkins 2017-07-20 09:38:57 +00:00 committed by Gerrit Code Review
commit 5a49fefb15
18 changed files with 116 additions and 24 deletions

View File

@ -23,6 +23,7 @@ from zun.api.controllers import link
from zun.api.controllers.v1 import collection
from zun.api.controllers.v1.schemas import containers as schema
from zun.api.controllers.v1.views import containers_view as view
from zun.api.controllers import versions
from zun.api import utils as api_utils
from zun.common import consts
from zun.common import exception
@ -33,7 +34,6 @@ from zun.common import utils
from zun.common import validation
from zun import objects
LOG = logging.getLogger(__name__)
@ -206,7 +206,7 @@ class ContainersController(base.Controller):
"""Create a new container.
:param run: if true, starts the container
:param container: a container within the request body.
:param container_dict: a container within the request body.
"""
context = pecan.request.context
compute_api = pecan.request.compute_api
@ -251,6 +251,26 @@ class ContainersController(base.Controller):
str(container_dict['memory']) + 'M'
if container_dict.get('restart_policy'):
self._check_for_restart_policy(container_dict)
auto_remove = container_dict.pop('auto_remove', None)
if auto_remove is not None:
req_version = pecan.request.version
min_version = versions.Version('', '', '', '1.3')
if req_version >= min_version:
try:
container_dict['auto_remove'] = strutils.bool_from_string(
auto_remove, strict=True)
except ValueError:
msg = _('Auto_remove value are true or false')
raise exception.InvalidValue(msg)
else:
msg = _('Invalid param auto_remove because current request '
'version is %(req_version)s. Auto_remove is only '
'supported from version %(min_version)s') % \
{'req_version': req_version,
'min_version': min_version}
raise exception.InvalidParam(msg)
container_dict['status'] = consts.CREATING
extra_spec = container_dict.get('hints', None)
new_container = objects.Container(context, **container_dict)
@ -290,8 +310,8 @@ class ContainersController(base.Controller):
context = pecan.request.context
compute_api = pecan.request.compute_api
container = compute_api.add_security_group(
context, container, security_group['name'])
compute_api.add_security_group(context, container,
security_group['name'])
pecan.response.status = 202
@pecan.expose('json')

View File

@ -21,6 +21,7 @@ _container_properties = {
'cpu': parameter_types.cpu,
'memory': parameter_types.memory,
'workdir': parameter_types.workdir,
'auto_remove': parameter_types.auto_remove,
'image_pull_policy': parameter_types.image_pull_policy,
'labels': parameter_types.labels,
'environment': parameter_types.environment,

View File

@ -38,7 +38,8 @@ _basic_keys = (
'status_detail',
'interactive',
'image_driver',
'security_groups'
'security_groups',
'auto_remove',
)

View File

@ -35,10 +35,11 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 1.1 - Initial version
* 1.2 - Support user specify pre created networks
* 1.3 - Add auto_remove to container
"""
BASE_VER = '1.1'
CURRENT_MAX_VER = '1.2'
CURRENT_MAX_VER = '1.3'
class Version(object):

View File

@ -35,3 +35,10 @@ user documentation.
[{u'network': u'test'}]
[{u'network': u'test2'}]
[{u'v6-fixed-ip': u'2f:33:45'}]
1.3
---
Add 'auto_remove' field for creating a container.
With this field, the container will be automatically removed if it exists.
The new one will be created instead.

View File

@ -13,8 +13,10 @@
CONTAINER_STATUSES = (
ERROR, RUNNING, STOPPED, PAUSED, UNKNOWN, CREATING, CREATED,
DELETED,
) = (
'Error', 'Running', 'Stopped', 'Paused', 'Unknown', 'Creating', 'Created',
'Deleted',
)
TASK_STATES = (

View File

@ -319,6 +319,10 @@ class InvalidParameterValue(Invalid):
message = _("%(err)s")
class InvalidParam(Invalid):
message = _('Invalid param %(param)s')
class PatchError(Invalid):
message = _("Couldn't apply patch '%(patch)s'. Reason: %(reason)s")

View File

@ -40,9 +40,10 @@ synchronized = lockutils.synchronized_with_prefix('zun-')
VALID_STATES = {
'commit': [consts.RUNNING, consts.STOPPED, consts.PAUSED],
'delete': [consts.CREATED, consts.ERROR, consts.STOPPED],
'delete': [consts.CREATED, consts.ERROR, consts.STOPPED, consts.DELETED],
'delete_force': [consts.CREATED, consts.CREATING, consts.ERROR,
consts.RUNNING, consts.STOPPED, consts.UNKNOWN],
consts.RUNNING, consts.STOPPED, consts.UNKNOWN,
consts.DELETED],
'start': [consts.CREATED, consts.STOPPED, consts.ERROR],
'stop': [consts.RUNNING],
'reboot': [consts.CREATED, consts.RUNNING, consts.STOPPED, consts.ERROR],

View File

@ -74,6 +74,10 @@ command = {
'type': ['string', 'null']
}
auto_remove = {
'type': ['boolean', 'null']
}
cpu = {
'type': ['number', 'string', 'null'],
'pattern': '^[0-9]*(\.([0-9]+))?$',

View File

@ -20,7 +20,7 @@ docker_group = cfg.OptGroup(name='docker',
docker_opts = [
cfg.StrOpt('docker_remote_api_version',
default='1.23',
default='1.25',
help='Docker remote api version. Override it according to '
'specific docker api version in your environment.'),
cfg.IntOpt('default_timeout',

View File

@ -45,13 +45,17 @@ def is_not_found(e):
def handle_not_found(e, context, container, do_not_raise=False):
container.status = consts.ERROR
container.status_reason = six.text_type(e)
if container.auto_remove:
container.status = consts.DELETED
else:
container.status = consts.ERROR
container.status_reason = six.text_type(e)
container.save(context)
if do_not_raise:
return
raise exception.Conflict(message=_('the container is in Error state'))
raise exception.Conflict(message=_(
"Cannot act on container in '%s' state") % container.status)
def wrap_docker_error(function):
@ -122,6 +126,8 @@ class DockerDriver(driver.ContainerDriver):
# host_config['pid_mode'] = 'container:%s' % sandbox_id
host_config['ipc_mode'] = 'container:%s' % sandbox_id
host_config['volumes_from'] = sandbox_id
if container.auto_remove:
host_config['auto_remove'] = container.auto_remove
if container.memory is not None:
host_config['mem_limit'] = container.memory
if container.cpu is not None:
@ -197,16 +203,23 @@ class DockerDriver(driver.ContainerDriver):
db_containers = objects.Container.list_by_host(context, CONF.host)
for db_container in db_containers:
if db_container.status in (consts.CREATING, consts.DELETED):
# Skip populating db record since the container is in a
# unstable state.
continue
container_id = db_container.container_id
docker_container = id_to_container_map.get(container_id)
if docker_container:
self._populate_container(db_container, docker_container)
else:
if db_container.status != consts.CREATING:
# Print a warning message if the container was recorded in
# DB but missing in docker.
if not docker_container:
if db_container.auto_remove:
db_container.status = consts.DELETED
db_container.save(context)
else:
LOG.warning("Container was recorded in DB but missing in "
"docker")
continue
self._populate_container(db_container, docker_container)
return db_containers

View File

@ -0,0 +1,34 @@
# 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 auto_remove to container
Revision ID: 75315e219cfb
Revises: 648c25faa0be
Create Date: 2017-06-30 22:10:22.261595
"""
# revision identifiers, used by Alembic.
revision = '75315e219cfb'
down_revision = '648c25faa0be'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('container',
sa.Column('auto_remove', sa.Boolean(),
nullable=True))

View File

@ -154,6 +154,7 @@ class Container(Base):
websocket_url = Column(String(255))
websocket_token = Column(String(255))
security_groups = Column(JSONEncodedList)
auto_remove = Column(Boolean, default=False)
class Image(Base):

View File

@ -37,7 +37,8 @@ class Container(base.ZunPersistentObject, base.ZunObject):
# Version 1.15: Combine tty and stdin_open
# Version 1.16: Add websocket_url and token
# Version 1.17: Add security_groups
VERSION = '1.17'
# Version 1.18: Add auto_remove
VERSION = '1.18'
fields = {
'id': fields.IntegerField(),
@ -55,6 +56,7 @@ class Container(base.ZunPersistentObject, base.ZunObject):
'task_state': z_fields.TaskStateField(nullable=True),
'environment': fields.DictOfStringsField(nullable=True),
'workdir': fields.StringField(nullable=True),
'auto_remove': fields.BooleanField(nullable=True),
'ports': z_fields.ListOfIntegersField(nullable=True),
'hostname': fields.StringField(nullable=True),
'labels': fields.DictOfStringsField(nullable=True),

View File

@ -25,7 +25,7 @@ class TestRootController(api_base.FunctionalTest):
u'default_version':
{u'id': u'v1',
u'links': [{u'href': u'http://localhost/v1/', u'rel': u'self'}],
u'max_version': u'1.2',
u'max_version': u'1.3',
u'min_version': u'1.1',
u'status': u'CURRENT'},
u'description': u'Zun is an OpenStack project which '
@ -33,7 +33,7 @@ class TestRootController(api_base.FunctionalTest):
u'versions': [{u'id': u'v1',
u'links': [{u'href': u'http://localhost/v1/',
u'rel': u'self'}],
u'max_version': u'1.2',
u'max_version': u'1.3',
u'min_version': u'1.1',
u'status': u'CURRENT'}]}

View File

@ -187,7 +187,7 @@ class TestDockerDriver(base.DriverTestCase):
return_value='404 Not Found') as mock_init:
self.mock_docker.inspect_container = mock.Mock(
side_effect=errors.APIError('Error', '', ''))
mock_container = mock.MagicMock()
mock_container = mock.MagicMock(auto_remove=False)
result_container = self.driver.show(self.context, mock_container)
self.mock_docker.inspect_container.assert_called_once_with(
mock_container.container_id)

View File

@ -66,7 +66,8 @@ def get_test_container(**kwargs):
'websocket_url': 'ws://127.0.0.1:6784/4c03164962fa/attach/'
'ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1',
'websocket_token': '7878038e-957c-4d52-ae19-1e9561784e7b',
'security_groups': kwargs.get('security_groups', ['default'])
'security_groups': kwargs.get('security_groups', ['default']),
'auto_remove': kwargs.get('auto_remove', False),
}

View File

@ -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.17-dd8ca54832bf3a0ff78249a5a0ab4143',
'Container': '1.18-e611f6b0fbc9f5f5c1164545b1630268',
'Image': '1.0-0b976be24f4f6ee0d526e5c981ce0633',
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
'NUMANode': '1.0-cba878b70b2f8b52f1e031b41ac13b4e',