Merge "Added support for Auto remove the container"
This commit is contained in:
commit
5a49fefb15
@ -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')
|
||||
|
@ -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,
|
||||
|
@ -38,7 +38,8 @@ _basic_keys = (
|
||||
'status_detail',
|
||||
'interactive',
|
||||
'image_driver',
|
||||
'security_groups'
|
||||
'security_groups',
|
||||
'auto_remove',
|
||||
)
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -13,8 +13,10 @@
|
||||
|
||||
CONTAINER_STATUSES = (
|
||||
ERROR, RUNNING, STOPPED, PAUSED, UNKNOWN, CREATING, CREATED,
|
||||
DELETED,
|
||||
) = (
|
||||
'Error', 'Running', 'Stopped', 'Paused', 'Unknown', 'Creating', 'Created',
|
||||
'Deleted',
|
||||
)
|
||||
|
||||
TASK_STATES = (
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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],
|
||||
|
@ -74,6 +74,10 @@ command = {
|
||||
'type': ['string', 'null']
|
||||
}
|
||||
|
||||
auto_remove = {
|
||||
'type': ['boolean', 'null']
|
||||
}
|
||||
|
||||
cpu = {
|
||||
'type': ['number', 'string', 'null'],
|
||||
'pattern': '^[0-9]*(\.([0-9]+))?$',
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
||||
|
@ -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))
|
@ -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):
|
||||
|
@ -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),
|
||||
|
@ -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'}]}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
}
|
||||
|
||||
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user