Remove container object
Following on from removing the k8s specific APIs in I1f6f04a35dfbb39f217487fea104ded035b75569 the objects associated with these APIs need removal. Remove the container object, drop the db table and remove references to the container object. The docker_conductor has also been removed as this was used for managing containers using Magnum objects. Change-Id: I288fa7a9717519b1ae8195820975676d99b4d6d2 Partially-Implements: blueprint delete-container-endpoint Co-Authored-By: Spyros Trigazis <strigazi@gmail.com>
This commit is contained in:
parent
d92c76f6d1
commit
40aa6550f1
@ -27,13 +27,6 @@
|
||||
"rc:get_all": "rule:default",
|
||||
"rc:update": "rule:default",
|
||||
|
||||
"container:create": "rule:admin_or_user",
|
||||
"container:delete": "rule:admin_or_user",
|
||||
"container:detail": "rule:default",
|
||||
"container:get": "rule:default",
|
||||
"container:get_all": "rule:default",
|
||||
"container:update": "rule:admin_or_user",
|
||||
|
||||
"certificate:create": "rule:admin_or_user",
|
||||
"certificate:get": "rule:admin_or_user",
|
||||
|
||||
|
@ -28,7 +28,6 @@ from magnum.common import short_id
|
||||
from magnum.conductor.handlers import bay_conductor
|
||||
from magnum.conductor.handlers import ca_conductor
|
||||
from magnum.conductor.handlers import conductor_listener
|
||||
from magnum.conductor.handlers import docker_conductor
|
||||
from magnum.conductor.handlers import indirection_api
|
||||
from magnum.conductor.handlers import k8s_conductor
|
||||
from magnum.i18n import _LI
|
||||
@ -51,7 +50,6 @@ def main():
|
||||
conductor_id = short_id.generate_id()
|
||||
endpoints = [
|
||||
indirection_api.Handler(),
|
||||
docker_conductor.Handler(),
|
||||
k8s_conductor.Handler(),
|
||||
bay_conductor.Handler(),
|
||||
conductor_listener.Handler(),
|
||||
|
@ -18,11 +18,9 @@ from docker import client
|
||||
from docker import tls
|
||||
from docker.utils import utils
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from magnum.conductor.handlers.common import cert_manager
|
||||
from magnum.conductor import utils as conductor_utils
|
||||
from magnum import objects
|
||||
|
||||
|
||||
docker_opts = [
|
||||
@ -76,15 +74,6 @@ def is_docker_api_version_atleast(docker, version):
|
||||
return False
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def docker_for_container(context, container):
|
||||
if uuidutils.is_uuid_like(container):
|
||||
container = objects.Container.get_by_uuid(context, container)
|
||||
bay = conductor_utils.retrieve_bay(context, container.bay_uuid)
|
||||
with docker_for_bay(context, bay) as docker:
|
||||
yield docker
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def docker_for_bay(context, bay):
|
||||
baymodel = conductor_utils.retrieve_baymodel(context, bay)
|
||||
|
@ -59,39 +59,6 @@ class API(rpc_service.API):
|
||||
return self._call('rc_show', rc_ident=rc_ident,
|
||||
bay_ident=bay_ident)
|
||||
|
||||
# Container operations
|
||||
|
||||
def container_create(self, container):
|
||||
return self._call('container_create', container=container)
|
||||
|
||||
def container_delete(self, container_uuid):
|
||||
return self._call('container_delete', container_uuid=container_uuid)
|
||||
|
||||
def container_show(self, container_uuid):
|
||||
return self._call('container_show', container_uuid=container_uuid)
|
||||
|
||||
def container_reboot(self, container_uuid):
|
||||
return self._call('container_reboot', container_uuid=container_uuid)
|
||||
|
||||
def container_stop(self, container_uuid):
|
||||
return self._call('container_stop', container_uuid=container_uuid)
|
||||
|
||||
def container_start(self, container_uuid):
|
||||
return self._call('container_start', container_uuid=container_uuid)
|
||||
|
||||
def container_pause(self, container_uuid):
|
||||
return self._call('container_pause', container_uuid=container_uuid)
|
||||
|
||||
def container_unpause(self, container_uuid):
|
||||
return self._call('container_unpause', container_uuid=container_uuid)
|
||||
|
||||
def container_logs(self, container_uuid):
|
||||
return self._call('container_logs', container_uuid=container_uuid)
|
||||
|
||||
def container_exec(self, container_uuid, command):
|
||||
return self._call('container_exec', container_uuid=container_uuid,
|
||||
command=command)
|
||||
|
||||
# CA operations
|
||||
|
||||
def sign_certificate(self, bay, certificate):
|
||||
|
@ -1,212 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""Magnum Docker RPC handler."""
|
||||
|
||||
import functools
|
||||
|
||||
from docker import errors
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from magnum.common import docker_utils
|
||||
from magnum.common import exception
|
||||
from magnum.common import utils as magnum_utils
|
||||
from magnum.i18n import _LE
|
||||
from magnum import objects
|
||||
from magnum.objects import fields
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def wrap_container_exception(f):
|
||||
def wrapped(self, context, *args, **kwargs):
|
||||
try:
|
||||
return f(self, context, *args, **kwargs)
|
||||
except Exception as e:
|
||||
container_uuid = None
|
||||
if 'container_uuid' in kwargs:
|
||||
container_uuid = kwargs.get('container_uuid')
|
||||
elif 'container' in kwargs:
|
||||
container_uuid = kwargs.get('container').uuid
|
||||
|
||||
LOG.exception(_LE("Error while connect to docker "
|
||||
"container %s"), container_uuid)
|
||||
raise exception.ContainerException(
|
||||
"Docker internal Error: %s" % str(e))
|
||||
return functools.wraps(f)(wrapped)
|
||||
|
||||
|
||||
class Handler(object):
|
||||
|
||||
def __init__(self):
|
||||
super(Handler, self).__init__()
|
||||
|
||||
@staticmethod
|
||||
def _find_container_by_name(docker, name):
|
||||
try:
|
||||
for info in docker.list_instances(inspect=True):
|
||||
if info['Config'].get('Hostname') == name:
|
||||
return info
|
||||
except errors.APIError as e:
|
||||
if e.response.status_code != 404:
|
||||
raise
|
||||
return {}
|
||||
|
||||
def _encode_utf8(self, value):
|
||||
if six.PY2 and not isinstance(value, unicode):
|
||||
value = unicode(value)
|
||||
return value.encode('utf-8')
|
||||
|
||||
# Container operations
|
||||
|
||||
@wrap_container_exception
|
||||
def container_create(self, context, container):
|
||||
with docker_utils.docker_for_container(context, container) as docker:
|
||||
name = container.name
|
||||
container_uuid = container.uuid
|
||||
image = container.image
|
||||
LOG.debug('Creating container with image %s name %s', image, name)
|
||||
try:
|
||||
image_repo, image_tag = docker_utils.parse_docker_image(image)
|
||||
docker.pull(image_repo, tag=image_tag)
|
||||
docker.inspect_image(self._encode_utf8(container.image))
|
||||
kwargs = {'name': name,
|
||||
'hostname': container_uuid,
|
||||
'command': container.command,
|
||||
'environment': container.environment}
|
||||
if docker_utils.is_docker_api_version_atleast(docker, '1.19'):
|
||||
if container.memory is not None:
|
||||
kwargs['host_config'] = {
|
||||
'Memory':
|
||||
magnum_utils.get_docker_quantity(container.memory)}
|
||||
else:
|
||||
kwargs['mem_limit'] = container.memory
|
||||
|
||||
docker.create_container(image, **kwargs)
|
||||
container.status = fields.ContainerStatus.STOPPED
|
||||
return container
|
||||
except errors.APIError:
|
||||
container.status = fields.ContainerStatus.ERROR
|
||||
raise
|
||||
finally:
|
||||
container.save()
|
||||
|
||||
@wrap_container_exception
|
||||
def container_delete(self, context, container_uuid):
|
||||
LOG.debug("container_delete %s", container_uuid)
|
||||
with docker_utils.docker_for_container(context,
|
||||
container_uuid) as docker:
|
||||
docker_id = self._find_container_by_name(docker,
|
||||
container_uuid)
|
||||
if not docker_id:
|
||||
return None
|
||||
return docker.remove_container(docker_id)
|
||||
|
||||
@wrap_container_exception
|
||||
def container_show(self, context, container_uuid):
|
||||
LOG.debug("container_show %s", container_uuid)
|
||||
with docker_utils.docker_for_container(context,
|
||||
container_uuid) as docker:
|
||||
container = objects.Container.get_by_uuid(context, container_uuid)
|
||||
try:
|
||||
docker_id = self._find_container_by_name(docker,
|
||||
container_uuid)
|
||||
if not docker_id:
|
||||
LOG.exception(_LE("Can not find docker instance with %s,"
|
||||
"set it to Error status"),
|
||||
container_uuid)
|
||||
container.status = fields.ContainerStatus.ERROR
|
||||
container.save()
|
||||
return container
|
||||
result = docker.inspect_container(docker_id)
|
||||
status = result.get('State')
|
||||
if status:
|
||||
if status.get('Error') is True:
|
||||
container.status = fields.ContainerStatus.ERROR
|
||||
elif status.get('Paused'):
|
||||
container.status = fields.ContainerStatus.PAUSED
|
||||
elif status.get('Running'):
|
||||
container.status = fields.ContainerStatus.RUNNING
|
||||
else:
|
||||
container.status = fields.ContainerStatus.STOPPED
|
||||
container.save()
|
||||
return container
|
||||
except errors.APIError as api_error:
|
||||
error_message = str(api_error)
|
||||
if '404' in error_message:
|
||||
container.status = fields.ContainerStatus.ERROR
|
||||
container.save()
|
||||
return container
|
||||
raise
|
||||
|
||||
@wrap_container_exception
|
||||
def _container_action(self, context, container_uuid, status, docker_func):
|
||||
LOG.debug("%s container %s ...", docker_func, container_uuid)
|
||||
with docker_utils.docker_for_container(context,
|
||||
container_uuid) as docker:
|
||||
docker_id = self._find_container_by_name(docker,
|
||||
container_uuid)
|
||||
result = getattr(docker, docker_func)(docker_id)
|
||||
container = objects.Container.get_by_uuid(context,
|
||||
container_uuid)
|
||||
container.status = status
|
||||
container.save()
|
||||
return result
|
||||
|
||||
def container_reboot(self, context, container_uuid):
|
||||
return self._container_action(context, container_uuid,
|
||||
fields.ContainerStatus.RUNNING,
|
||||
'restart')
|
||||
|
||||
def container_stop(self, context, container_uuid):
|
||||
return self._container_action(context, container_uuid,
|
||||
fields.ContainerStatus.STOPPED, 'stop')
|
||||
|
||||
def container_start(self, context, container_uuid):
|
||||
return self._container_action(context, container_uuid,
|
||||
fields.ContainerStatus.RUNNING, 'start')
|
||||
|
||||
def container_pause(self, context, container_uuid):
|
||||
return self._container_action(context, container_uuid,
|
||||
fields.ContainerStatus.PAUSED, 'pause')
|
||||
|
||||
def container_unpause(self, context, container_uuid):
|
||||
return self._container_action(context, container_uuid,
|
||||
fields.ContainerStatus.RUNNING,
|
||||
'unpause')
|
||||
|
||||
@wrap_container_exception
|
||||
def container_logs(self, context, container_uuid):
|
||||
LOG.debug("container_logs %s", container_uuid)
|
||||
with docker_utils.docker_for_container(context,
|
||||
container_uuid) as docker:
|
||||
docker_id = self._find_container_by_name(docker,
|
||||
container_uuid)
|
||||
return {'output': docker.logs(docker_id)}
|
||||
|
||||
@wrap_container_exception
|
||||
def container_exec(self, context, container_uuid, command):
|
||||
LOG.debug("container_exec %s command %s",
|
||||
container_uuid, command)
|
||||
with docker_utils.docker_for_container(context,
|
||||
container_uuid) as docker:
|
||||
docker_id = self._find_container_by_name(docker,
|
||||
container_uuid)
|
||||
if docker_utils.is_docker_library_version_atleast('1.2.0'):
|
||||
create_res = docker.exec_create(docker_id, command, True,
|
||||
True, False)
|
||||
exec_output = docker.exec_start(create_res, False, False,
|
||||
False)
|
||||
else:
|
||||
exec_output = docker.execute(docker_id, command)
|
||||
return {'output': exec_output}
|
@ -27,10 +27,10 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('docker_remote_api_version',
|
||||
'magnum.conductor.handlers.docker_conductor',
|
||||
'magnum.common.docker_utils',
|
||||
group='docker')
|
||||
CONF.import_opt('default_timeout',
|
||||
'magnum.conductor.handlers.docker_conductor',
|
||||
'magnum.common.docker_utils',
|
||||
group='docker')
|
||||
|
||||
COE_CLASS_PATH = {
|
||||
|
@ -204,90 +204,6 @@ class Connection(object):
|
||||
:raises: BayModelNotFound
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_container_list(self, context, filters=None,
|
||||
limit=None, marker=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Get matching containers.
|
||||
|
||||
Return a list of the specified columns for all containers that match
|
||||
the specified filters.
|
||||
|
||||
:param context: The security context
|
||||
:param filters: Filters to apply. Defaults to None.
|
||||
|
||||
:param limit: Maximum number of containers to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
:param sort_key: Attribute by which results should be sorted.
|
||||
:param sort_dir: direction in which results should be sorted.
|
||||
(asc, desc)
|
||||
:returns: A list of tuples of the specified columns.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_container(self, values):
|
||||
"""Create a new container.
|
||||
|
||||
:param values: A dict containing several items used to identify
|
||||
and track the container, and several dicts which are
|
||||
passed
|
||||
into the Drivers when managing this container. For
|
||||
example:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
'uuid': uuidutils.generate_uuid(),
|
||||
'name': 'example',
|
||||
'type': 'virt'
|
||||
}
|
||||
:returns: A container.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_container_by_id(self, context, container_id):
|
||||
"""Return a container.
|
||||
|
||||
:param context: The security context
|
||||
:param container_id: The id of a container.
|
||||
:returns: A container.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_container_by_uuid(self, context, container_uuid):
|
||||
"""Return a container.
|
||||
|
||||
:param context: The security context
|
||||
:param container_uuid: The uuid of a container.
|
||||
:returns: A container.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_container_by_name(self, context, container_name):
|
||||
"""Return a container.
|
||||
|
||||
:param context: The security context
|
||||
:param container_name: The name of a container.
|
||||
:returns: A container.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def destroy_container(self, container_id):
|
||||
"""Destroy a container and all associated interfaces.
|
||||
|
||||
:param container_id: The id or uuid of a container.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_container(self, container_id, values):
|
||||
"""Update properties of a container.
|
||||
|
||||
:param container_id: The id or uuid of a container.
|
||||
:returns: A container.
|
||||
:raises: ContainerNotFound
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_rc_list(self, context, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
|
@ -0,0 +1,28 @@
|
||||
# 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.
|
||||
"""remove container object
|
||||
|
||||
Revision ID: 1f196a3dabae
|
||||
Revises: e0653b2d5271
|
||||
Create Date: 2016-06-02 11:42:42.200992
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1f196a3dabae'
|
||||
down_revision = 'e0653b2d5271'
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.drop_table('container')
|
@ -198,11 +198,6 @@ class Connection(api.Connection):
|
||||
if query.count() != 0:
|
||||
query.delete()
|
||||
|
||||
query = model_query(models.Container, session=session)
|
||||
query = self._add_containers_filters(query, {'bay_uuid': bay_uuid})
|
||||
if query.count() != 0:
|
||||
query.delete()
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.Bay, session=session)
|
||||
@ -370,103 +365,6 @@ class Connection(api.Connection):
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
def _add_containers_filters(self, query, filters):
|
||||
if filters is None:
|
||||
filters = {}
|
||||
|
||||
filter_names = ['name', 'image', 'project_id', 'user_id',
|
||||
'memory', 'bay_uuid']
|
||||
for name in filter_names:
|
||||
if name in filters:
|
||||
query = query.filter_by(**{name: filters[name]})
|
||||
|
||||
return query
|
||||
|
||||
def get_container_list(self, context, filters=None, limit=None,
|
||||
marker=None, sort_key=None, sort_dir=None):
|
||||
query = model_query(models.Container)
|
||||
query = self._add_tenant_filters(context, query)
|
||||
query = self._add_containers_filters(query, filters)
|
||||
return _paginate_query(models.Container, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
def create_container(self, values):
|
||||
# ensure defaults are present for new containers
|
||||
if not values.get('uuid'):
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
|
||||
container = models.Container()
|
||||
container.update(values)
|
||||
try:
|
||||
container.save()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.ContainerAlreadyExists(uuid=values['uuid'])
|
||||
return container
|
||||
|
||||
def get_container_by_id(self, context, container_id):
|
||||
query = model_query(models.Container)
|
||||
query = self._add_tenant_filters(context, query)
|
||||
query = query.filter_by(id=container_id)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.ContainerNotFound(container=container_id)
|
||||
|
||||
def get_container_by_uuid(self, context, container_uuid):
|
||||
query = model_query(models.Container)
|
||||
query = self._add_tenant_filters(context, query)
|
||||
query = query.filter_by(uuid=container_uuid)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.ContainerNotFound(container=container_uuid)
|
||||
|
||||
def get_container_by_name(self, context, container_name):
|
||||
query = model_query(models.Container)
|
||||
query = self._add_tenant_filters(context, query)
|
||||
query = query.filter_by(name=container_name)
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.ContainerNotFound(container=container_name)
|
||||
except MultipleResultsFound:
|
||||
raise exception.Conflict('Multiple containers exist with same '
|
||||
'name. Please use the container uuid '
|
||||
'instead.')
|
||||
|
||||
def destroy_container(self, container_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.Container, session=session)
|
||||
query = add_identity_filter(query, container_id)
|
||||
count = query.delete()
|
||||
if count != 1:
|
||||
raise exception.ContainerNotFound(container_id)
|
||||
|
||||
def update_container(self, container_id, values):
|
||||
# NOTE(dtantsur): this can lead to very strange errors
|
||||
if 'uuid' in values:
|
||||
msg = _("Cannot overwrite UUID for an existing Container.")
|
||||
raise exception.InvalidParameterValue(err=msg)
|
||||
|
||||
return self._do_update_container(container_id, values)
|
||||
|
||||
def _do_update_container(self, container_id, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = model_query(models.Container, session=session)
|
||||
query = add_identity_filter(query, container_id)
|
||||
try:
|
||||
ref = query.with_lockmode('update').one()
|
||||
except NoResultFound:
|
||||
raise exception.ContainerNotFound(container=container_id)
|
||||
|
||||
if 'provision_state' in values:
|
||||
values['provision_updated_at'] = timeutils.utcnow()
|
||||
|
||||
ref.update(values)
|
||||
return ref
|
||||
|
||||
def _add_rcs_filters(self, query, filters):
|
||||
if filters is None:
|
||||
filters = {}
|
||||
|
@ -179,27 +179,6 @@ class BayModel(Base):
|
||||
master_lb_enabled = Column(Boolean, default=False)
|
||||
|
||||
|
||||
class Container(Base):
|
||||
"""Represents a container."""
|
||||
|
||||
__tablename__ = 'container'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint('uuid', name='uniq_container0uuid'),
|
||||
table_args()
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
project_id = Column(String(255))
|
||||
user_id = Column(String(255))
|
||||
uuid = Column(String(36))
|
||||
name = Column(String(255))
|
||||
image = Column(String(255))
|
||||
command = Column(String(255))
|
||||
bay_uuid = Column(String(36))
|
||||
status = Column(String(20))
|
||||
memory = Column(String(255))
|
||||
environment = Column(JSONEncodedDict)
|
||||
|
||||
|
||||
class ReplicationController(Base):
|
||||
"""Represents a pod replication controller."""
|
||||
|
||||
|
@ -15,13 +15,11 @@
|
||||
from magnum.objects import bay
|
||||
from magnum.objects import baymodel
|
||||
from magnum.objects import certificate
|
||||
from magnum.objects import container
|
||||
from magnum.objects import magnum_service
|
||||
from magnum.objects import replicationcontroller as rc
|
||||
from magnum.objects import x509keypair
|
||||
|
||||
|
||||
Container = container.Container
|
||||
Bay = bay.Bay
|
||||
BayModel = baymodel.BayModel
|
||||
MagnumService = magnum_service.MagnumService
|
||||
@ -30,7 +28,6 @@ X509KeyPair = x509keypair.X509KeyPair
|
||||
Certificate = certificate.Certificate
|
||||
__all__ = (Bay,
|
||||
BayModel,
|
||||
Container,
|
||||
MagnumService,
|
||||
ReplicationController,
|
||||
X509KeyPair,
|
||||
|
@ -1,186 +0,0 @@
|
||||
# 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.
|
||||
|
||||
from oslo_versionedobjects import fields
|
||||
|
||||
from magnum.db import api as dbapi
|
||||
from magnum.objects import base
|
||||
from magnum.objects import fields as m_fields
|
||||
|
||||
|
||||
@base.MagnumObjectRegistry.register
|
||||
class Container(base.MagnumPersistentObject, base.MagnumObject,
|
||||
base.MagnumObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Add memory field
|
||||
# Version 1.2: Add environment field
|
||||
# Version 1.3: Add filters to list()
|
||||
VERSION = '1.3'
|
||||
|
||||
dbapi = dbapi.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
'uuid': fields.StringField(nullable=True),
|
||||
'name': fields.StringField(nullable=True),
|
||||
'project_id': fields.StringField(nullable=True),
|
||||
'user_id': fields.StringField(nullable=True),
|
||||
'image': fields.StringField(nullable=True),
|
||||
'command': fields.StringField(nullable=True),
|
||||
'bay_uuid': fields.StringField(nullable=True),
|
||||
'status': m_fields.ContainerStatusField(nullable=True),
|
||||
'memory': fields.StringField(nullable=True),
|
||||
'environment': fields.DictOfStringsField(nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(container, db_container):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in container.fields:
|
||||
container[field] = db_container[field]
|
||||
|
||||
container.obj_reset_changes()
|
||||
return container
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object_list(db_objects, cls, context):
|
||||
"""Converts a list of database entities to a list of formal objects."""
|
||||
return [Container._from_db_object(cls(context), obj)
|
||||
for obj in db_objects]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, container_id):
|
||||
"""Find a container based on its integer id and return a Container object.
|
||||
|
||||
:param container_id: the id of a container.
|
||||
:param context: Security context
|
||||
:returns: a :class:`Container` object.
|
||||
"""
|
||||
db_container = cls.dbapi.get_container_by_id(context, container_id)
|
||||
container = Container._from_db_object(cls(context), db_container)
|
||||
return container
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
"""Find a container based on uuid and return a :class:`Container` object.
|
||||
|
||||
:param uuid: the uuid of a container.
|
||||
:param context: Security context
|
||||
:returns: a :class:`Container` object.
|
||||
"""
|
||||
db_container = cls.dbapi.get_container_by_uuid(context, uuid)
|
||||
container = Container._from_db_object(cls(context), db_container)
|
||||
return container
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_name(cls, context, name):
|
||||
"""Find a container based on name and return a Container object.
|
||||
|
||||
:param name: the logical name of a container.
|
||||
:param context: Security context
|
||||
:returns: a :class:`Container` object.
|
||||
"""
|
||||
db_bay = cls.dbapi.get_container_by_name(context, name)
|
||||
bay = Container._from_db_object(cls(context), db_bay)
|
||||
return bay
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list(cls, context, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None, filters=None):
|
||||
"""Return a list of Container objects.
|
||||
|
||||
:param context: Security context.
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
:param marker: pagination marker for large data sets.
|
||||
:param sort_key: column to sort results by.
|
||||
:param sort_dir: direction to sort. "asc" or "desc".
|
||||
:param filters: filters when list containers, the filter name could be
|
||||
'name', 'image', 'project_id', 'user_id', 'memory',
|
||||
'bay_uuid'. For example, filters={'bay_uuid': '1'}
|
||||
:returns: a list of :class:`Container` object.
|
||||
|
||||
"""
|
||||
db_containers = cls.dbapi.get_container_list(context, limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir,
|
||||
filters=filters)
|
||||
return Container._from_db_object_list(db_containers, cls, context)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context=None):
|
||||
"""Create a Container record in the DB.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: Container(context)
|
||||
|
||||
"""
|
||||
values = self.obj_get_changes()
|
||||
db_container = self.dbapi.create_container(values)
|
||||
self._from_db_object(self, db_container)
|
||||
|
||||
@base.remotable
|
||||
def destroy(self, context=None):
|
||||
"""Delete the Container from the DB.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: Container(context)
|
||||
"""
|
||||
self.dbapi.destroy_container(self.uuid)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def save(self, context=None):
|
||||
"""Save updates to this Container.
|
||||
|
||||
Updates will be made column by column based on the result
|
||||
of self.what_changed().
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: Container(context)
|
||||
"""
|
||||
updates = self.obj_get_changes()
|
||||
self.dbapi.update_container(self.uuid, updates)
|
||||
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def refresh(self, context=None):
|
||||
"""Loads updates for this Container.
|
||||
|
||||
Loads a container with the same uuid from the database and
|
||||
checks for updated attributes. Updates are applied from
|
||||
the loaded container column by column, if there are any updates.
|
||||
|
||||
:param context: Security context. NOTE: This should only
|
||||
be used internally by the indirection_api.
|
||||
Unfortunately, RPC requires context as the first
|
||||
argument, even though we don't use it.
|
||||
A context should be set when instantiating the
|
||||
object, e.g.: Container(context)
|
||||
"""
|
||||
current = self.__class__.get_by_uuid(self._context, uuid=self.uuid)
|
||||
for field in self.fields:
|
||||
if self.obj_attr_is_set(field) and self[field] != current[field]:
|
||||
self[field] = current[field]
|
@ -25,7 +25,6 @@ import magnum.common.service
|
||||
import magnum.common.x509.config
|
||||
import magnum.conductor.config
|
||||
import magnum.conductor.handlers.bay_conductor
|
||||
import magnum.conductor.handlers.docker_conductor
|
||||
import magnum.conductor.handlers.k8s_conductor
|
||||
import magnum.db
|
||||
import magnum.drivers.common.template_def
|
||||
|
@ -39,13 +39,6 @@ policy_data = """
|
||||
"rc:detail": "",
|
||||
"rc:get": "",
|
||||
"rc:get_all": "",
|
||||
"rc:update": "",
|
||||
|
||||
"container:create": "",
|
||||
"container:delete": "",
|
||||
"container:detail": "",
|
||||
"container:get": "",
|
||||
"container:get_all": "",
|
||||
"container:update": ""
|
||||
"rc:update": ""
|
||||
}
|
||||
"""
|
||||
|
@ -29,123 +29,6 @@ CONF.import_opt('default_timeout', 'magnum.common.docker_utils',
|
||||
|
||||
|
||||
class TestDockerUtils(base.BaseTestCase):
|
||||
@mock.patch('magnum.common.docker_utils.DockerHTTPClient')
|
||||
@mock.patch.object(docker_utils, 'cert_manager')
|
||||
@mock.patch.object(docker_utils.objects.BayModel, 'get_by_uuid')
|
||||
@mock.patch.object(docker_utils.objects.Bay, 'get_by_name')
|
||||
def test_docker_for_container(self, mock_get_bay_by_name,
|
||||
mock_get_baymodel_by_uuid,
|
||||
mock_cert_manager,
|
||||
mock_docker_client):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay.api_address = 'https://1.2.3.4:2376'
|
||||
mock_get_bay_by_name.return_value = mock_bay
|
||||
mock_baymodel = mock.MagicMock()
|
||||
mock_baymodel.tls_disabled = False
|
||||
mock_get_baymodel_by_uuid.return_value = mock_baymodel
|
||||
mock_ca_cert = mock.MagicMock()
|
||||
mock_magnum_key = mock.MagicMock()
|
||||
mock_magnum_cert = mock.MagicMock()
|
||||
mock_cert_manager.create_client_files.return_value = (
|
||||
mock_ca_cert, mock_magnum_key, mock_magnum_cert
|
||||
)
|
||||
|
||||
mock_docker = mock.MagicMock()
|
||||
mock_docker_client.return_value = mock_docker
|
||||
|
||||
with docker_utils.docker_for_container(mock.sentinel.context,
|
||||
mock_container) as docker:
|
||||
self.assertEqual(mock_docker, docker)
|
||||
|
||||
mock_get_bay_by_name.assert_called_once_with(mock.sentinel.context,
|
||||
mock_container.bay_uuid)
|
||||
mock_get_baymodel_by_uuid.assert_called_once_with(
|
||||
mock.sentinel.context, mock_bay.baymodel_id)
|
||||
mock_docker_client.assert_called_once_with(
|
||||
'https://1.2.3.4:2376',
|
||||
CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout,
|
||||
ca_cert=mock_ca_cert.name,
|
||||
client_key=mock_magnum_key.name,
|
||||
client_cert=mock_magnum_cert.name)
|
||||
|
||||
@mock.patch('magnum.common.docker_utils.DockerHTTPClient')
|
||||
@mock.patch.object(docker_utils, 'cert_manager')
|
||||
@mock.patch.object(docker_utils.objects.BayModel, 'get_by_uuid')
|
||||
@mock.patch.object(docker_utils.objects.Bay, 'get_by_name')
|
||||
@mock.patch.object(docker_utils.objects.Container, 'get_by_uuid')
|
||||
def test_docker_for_container_uuid(self, mock_get_container_by_uuid,
|
||||
mock_get_bay_by_name,
|
||||
mock_get_baymodel_by_uuid,
|
||||
mock_cert_manager,
|
||||
mock_docker_client):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_container.uuid = '8e48ffb1-754d-4f21-bdd0-1a39bf796389'
|
||||
mock_get_container_by_uuid.return_value = mock_container
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay.api_address = 'https://1.2.3.4:2376'
|
||||
mock_get_bay_by_name.return_value = mock_bay
|
||||
mock_baymodel = mock.MagicMock()
|
||||
mock_baymodel.tls_disabled = False
|
||||
mock_get_baymodel_by_uuid.return_value = mock_baymodel
|
||||
mock_ca_cert = mock.MagicMock()
|
||||
mock_magnum_key = mock.MagicMock()
|
||||
mock_magnum_cert = mock.MagicMock()
|
||||
mock_cert_manager.create_client_files.return_value = (
|
||||
mock_ca_cert, mock_magnum_key, mock_magnum_cert
|
||||
)
|
||||
|
||||
mock_docker = mock.MagicMock()
|
||||
mock_docker_client.return_value = mock_docker
|
||||
|
||||
with docker_utils.docker_for_container(
|
||||
mock.sentinel.context, mock_container.uuid) as docker:
|
||||
self.assertEqual(mock_docker, docker)
|
||||
|
||||
mock_get_container_by_uuid.assert_called_once_with(
|
||||
mock.sentinel.context, mock_container.uuid
|
||||
)
|
||||
mock_get_bay_by_name.assert_called_once_with(mock.sentinel.context,
|
||||
mock_container.bay_uuid)
|
||||
mock_get_baymodel_by_uuid.assert_called_once_with(
|
||||
mock.sentinel.context, mock_bay.baymodel_id)
|
||||
mock_docker_client.assert_called_once_with(
|
||||
'https://1.2.3.4:2376',
|
||||
CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout,
|
||||
ca_cert=mock_ca_cert.name,
|
||||
client_key=mock_magnum_key.name,
|
||||
client_cert=mock_magnum_cert.name)
|
||||
|
||||
@mock.patch('magnum.common.docker_utils.DockerHTTPClient')
|
||||
@mock.patch.object(docker_utils.objects.BayModel, 'get_by_uuid')
|
||||
@mock.patch.object(docker_utils.objects.Bay, 'get_by_name')
|
||||
def test_docker_for_container_tls_disabled(self, mock_get_bay_by_name,
|
||||
mock_get_baymodel_by_uuid,
|
||||
mock_docker_client):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_bay = mock.MagicMock()
|
||||
mock_bay.api_address = 'tcp://1.2.3.4:2376'
|
||||
mock_get_bay_by_name.return_value = mock_bay
|
||||
mock_baymodel = mock.MagicMock()
|
||||
mock_baymodel.tls_disabled = True
|
||||
mock_get_baymodel_by_uuid.return_value = mock_baymodel
|
||||
mock_docker = mock.MagicMock()
|
||||
mock_docker_client.return_value = mock_docker
|
||||
|
||||
with docker_utils.docker_for_container(mock.sentinel.context,
|
||||
mock_container) as docker:
|
||||
self.assertEqual(mock_docker, docker)
|
||||
|
||||
mock_get_bay_by_name.assert_called_once_with(mock.sentinel.context,
|
||||
mock_container.bay_uuid)
|
||||
mock_get_baymodel_by_uuid.assert_called_once_with(
|
||||
mock.sentinel.context, mock_bay.baymodel_id)
|
||||
mock_docker_client.assert_called_once_with(
|
||||
'tcp://1.2.3.4:2376',
|
||||
CONF.docker.docker_remote_api_version,
|
||||
CONF.docker.default_timeout)
|
||||
|
||||
def test_is_docker_api_version_atleast(self):
|
||||
|
||||
|
@ -1,558 +0,0 @@
|
||||
# Copyright 2015 Rackspace All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
import docker
|
||||
from docker import errors
|
||||
import mock
|
||||
import six
|
||||
|
||||
from magnum.common import docker_utils
|
||||
from magnum.common import exception
|
||||
from magnum.conductor.handlers import docker_conductor
|
||||
from magnum import objects
|
||||
from magnum.objects import fields
|
||||
from magnum.tests import base
|
||||
|
||||
|
||||
class TestDockerHandler(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestDockerHandler, self).setUp()
|
||||
self.conductor = docker_conductor.Handler()
|
||||
dfc_patcher = mock.patch.object(docker_utils,
|
||||
'docker_for_container')
|
||||
docker_for_container = dfc_patcher.start()
|
||||
self.dfc_context_manager = docker_for_container.return_value
|
||||
self.mock_docker = mock.MagicMock()
|
||||
self.dfc_context_manager.__enter__.return_value = self.mock_docker
|
||||
self.addCleanup(dfc_patcher.stop)
|
||||
|
||||
@mock.patch.object(docker_utils, 'is_docker_api_version_atleast')
|
||||
def _test_container_create(self, container_dict, expected_kwargs,
|
||||
mock_version, expected_image='test_image',
|
||||
expected_tag='some_tag',
|
||||
api_version='1.18'):
|
||||
|
||||
mock_version.return_value = (float(api_version) > 1.18)
|
||||
|
||||
name = container_dict.pop('name')
|
||||
mock_container = mock.MagicMock(**container_dict)
|
||||
type(mock_container).name = mock.PropertyMock(return_value=name)
|
||||
|
||||
container = self.conductor.container_create(
|
||||
None, mock_container)
|
||||
|
||||
utf8_image = self.conductor._encode_utf8(mock_container.image)
|
||||
self.mock_docker.inspect_image.assert_called_once_with(utf8_image)
|
||||
self.mock_docker.pull.assert_called_once_with(expected_image,
|
||||
tag=expected_tag)
|
||||
self.mock_docker.create_container.assert_called_once_with(
|
||||
mock_container.image, **expected_kwargs)
|
||||
self.assertEqual(fields.ContainerStatus.STOPPED, container.status)
|
||||
|
||||
def test_container_create(self):
|
||||
container_dict = {
|
||||
'name': 'some-name',
|
||||
'uuid': 'some-uuid',
|
||||
'image': 'test_image:some_tag',
|
||||
'command': None,
|
||||
'memory': None,
|
||||
'environment': None,
|
||||
}
|
||||
expected_kwargs = {
|
||||
'name': 'some-name',
|
||||
'hostname': 'some-uuid',
|
||||
'command': None,
|
||||
'mem_limit': None,
|
||||
'environment': None,
|
||||
}
|
||||
self._test_container_create(container_dict, expected_kwargs)
|
||||
|
||||
def test_container_create_api_1_19(self):
|
||||
container_dict = {
|
||||
'name': 'some-name',
|
||||
'uuid': 'some-uuid',
|
||||
'image': 'test_image:some_tag',
|
||||
'command': None,
|
||||
'memory': '100m',
|
||||
'environment': None,
|
||||
}
|
||||
expected_kwargs = {
|
||||
'name': 'some-name',
|
||||
'hostname': 'some-uuid',
|
||||
'command': None,
|
||||
'host_config': {'Memory': 100 * 1024 * 1024},
|
||||
'environment': None,
|
||||
}
|
||||
self._test_container_create(container_dict, expected_kwargs,
|
||||
api_version='1.19')
|
||||
|
||||
def test_container_create_with_command(self):
|
||||
container_dict = {
|
||||
'name': 'some-name',
|
||||
'uuid': 'some-uuid',
|
||||
'image': 'test_image:some_tag',
|
||||
'command': 'env',
|
||||
'memory': None,
|
||||
'environment': None,
|
||||
}
|
||||
expected_kwargs = {
|
||||
'name': 'some-name',
|
||||
'hostname': 'some-uuid',
|
||||
'command': 'env',
|
||||
'mem_limit': None,
|
||||
'environment': None,
|
||||
}
|
||||
self._test_container_create(container_dict, expected_kwargs)
|
||||
|
||||
def test_container_create_with_memory(self):
|
||||
container_dict = {
|
||||
'name': 'some-name',
|
||||
'uuid': 'some-uuid',
|
||||
'image': 'test_image:some_tag',
|
||||
'command': None,
|
||||
'memory': '512m',
|
||||
'environment': None,
|
||||
}
|
||||
expected_kwargs = {
|
||||
'name': 'some-name',
|
||||
'hostname': 'some-uuid',
|
||||
'command': None,
|
||||
'mem_limit': '512m',
|
||||
'environment': None,
|
||||
}
|
||||
self._test_container_create(container_dict, expected_kwargs)
|
||||
|
||||
def test_container_create_with_environment(self):
|
||||
container_dict = {
|
||||
'name': 'some-name',
|
||||
'uuid': 'some-uuid',
|
||||
'image': 'test_image:some_tag',
|
||||
'command': None,
|
||||
'memory': '512m',
|
||||
'environment': {'key1': 'val1', 'key2': 'val2'},
|
||||
}
|
||||
expected_kwargs = {
|
||||
'name': 'some-name',
|
||||
'hostname': 'some-uuid',
|
||||
'command': None,
|
||||
'mem_limit': '512m',
|
||||
'environment': {'key1': 'val1', 'key2': 'val2'},
|
||||
}
|
||||
self._test_container_create(container_dict, expected_kwargs)
|
||||
|
||||
def test_encode_utf8_unicode(self):
|
||||
image = 'some_image:some_tag'
|
||||
unicode_image = six.u(image)
|
||||
utf8_image = self.conductor._encode_utf8(unicode_image)
|
||||
self.assertEqual(unicode_image.encode('utf-8'), utf8_image)
|
||||
|
||||
@mock.patch.object(errors.APIError, '__str__')
|
||||
def test_container_create_with_failure(self, mock_init):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_container.image = 'test_image:some_tag'
|
||||
mock_init.return_value = 'hit error'
|
||||
self.mock_docker.pull = mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', ''))
|
||||
|
||||
self.assertRaises(exception.ContainerException,
|
||||
self.conductor.container_create,
|
||||
None, mock_container)
|
||||
self.mock_docker.pull.assert_called_once_with(
|
||||
'test_image',
|
||||
tag='some_tag')
|
||||
self.assertFalse(self.mock_docker.create_container.called)
|
||||
mock_init.assert_called_with()
|
||||
self.assertEqual(fields.ContainerStatus.ERROR,
|
||||
mock_container.status)
|
||||
|
||||
def test_find_container_by_name_not_found(self):
|
||||
mock_docker = mock.MagicMock()
|
||||
fake_response = mock.MagicMock()
|
||||
fake_response.content = 'not_found'
|
||||
fake_response.status_code = 404
|
||||
mock_docker.list_instances.side_effect = errors.APIError(
|
||||
'not_found', fake_response)
|
||||
ret = self.conductor._find_container_by_name(mock_docker, '1')
|
||||
self.assertEqual({}, ret)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_delete(self, mock_find_container):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
self.conductor.container_delete(None, mock_container_uuid)
|
||||
self.mock_docker.remove_container.assert_called_once_with(
|
||||
mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_delete_with_container_not_exist(self,
|
||||
mock_find_container):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = {}
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
res = self.conductor.container_delete(None, mock_container_uuid)
|
||||
self.assertIsNone(res)
|
||||
self.assertFalse(self.mock_docker.remove_container.called)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
|
||||
@mock.patch.object(errors.APIError, '__str__')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_delete_with_failure(self, mock_find_container,
|
||||
mock_init):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
mock_init.return_value = 'hit error'
|
||||
self.mock_docker.remove_container = mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', ''))
|
||||
self.assertRaises(exception.ContainerException,
|
||||
self.conductor.container_delete,
|
||||
None, mock_container_uuid)
|
||||
self.mock_docker.remove_container.assert_called_once_with(
|
||||
mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
mock_init.assert_called_with()
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_action(self, mock_find_container, mock_get_by_uuid):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_get_by_uuid.return_value = mock_container
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
self.conductor._container_action(None, mock_container_uuid,
|
||||
'fake-status', 'fake-func')
|
||||
self.assertEqual('fake-status', mock_container.status)
|
||||
|
||||
def _test_container(self, action, docker_func_name, expected_status,
|
||||
mock_find_container, mock_get_by_uuid):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_get_by_uuid.return_value = mock_container
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
action_func = getattr(self.conductor, action)
|
||||
action_func(None, mock_container_uuid)
|
||||
docker_func = getattr(self.mock_docker, docker_func_name)
|
||||
docker_func.assert_called_once_with(mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
self.assertEqual(expected_status, mock_container.status)
|
||||
|
||||
@mock.patch.object(errors.APIError, '__str__')
|
||||
def _test_container_with_failure(
|
||||
self, action, docker_func_name, mock_find_container, mock_init):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
mock_init.return_value = 'hit error'
|
||||
setattr(self.mock_docker, docker_func_name, mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', '')))
|
||||
self.assertRaises(exception.ContainerException,
|
||||
getattr(self.conductor, action),
|
||||
None, mock_container_uuid)
|
||||
docker_func = getattr(self.mock_docker, docker_func_name)
|
||||
docker_func.assert_called_once_with(mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
mock_init.assert_called_with()
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_reboot(self, mock_find_container, mock_get_by_uuid):
|
||||
self._test_container(
|
||||
'container_reboot', 'restart', fields.ContainerStatus.RUNNING,
|
||||
mock_find_container, mock_get_by_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_reboot_with_failure(self, mock_find_container):
|
||||
self._test_container_with_failure(
|
||||
'container_reboot', 'restart', mock_find_container)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_start(self, mock_find_container, mock_get_by_uuid):
|
||||
self._test_container(
|
||||
'container_start', 'start', fields.ContainerStatus.RUNNING,
|
||||
mock_find_container, mock_get_by_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_start_with_failure(self, mock_find_container):
|
||||
self._test_container_with_failure(
|
||||
'container_start', 'start', mock_find_container)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_stop(self, mock_find_container, mock_get_by_uuid):
|
||||
self._test_container(
|
||||
'container_stop', 'stop', fields.ContainerStatus.STOPPED,
|
||||
mock_find_container, mock_get_by_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_stop_with_failure(self, mock_find_container):
|
||||
self._test_container_with_failure(
|
||||
'container_stop', 'stop', mock_find_container)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_pause(self, mock_find_container, mock_get_by_uuid):
|
||||
self._test_container(
|
||||
'container_pause', 'pause', fields.ContainerStatus.PAUSED,
|
||||
mock_find_container, mock_get_by_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_pause_with_failure(self, mock_find_container):
|
||||
self._test_container_with_failure(
|
||||
'container_pause', 'pause', mock_find_container)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_unpause(self, mock_find_container, mock_get_by_uuid):
|
||||
self._test_container(
|
||||
'container_unpause', 'unpause', fields.ContainerStatus.RUNNING,
|
||||
mock_find_container, mock_get_by_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_unpause_with_failure(self, mock_find_container):
|
||||
self._test_container_with_failure(
|
||||
'container_unpause', 'unpause', mock_find_container)
|
||||
|
||||
def _test_container_show(
|
||||
self, mock_find_container, mock_get_by_uuid, container_detail=None,
|
||||
expected_status=None, mock_docker_id='2703ef2b705d'):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_get_by_uuid.return_value = mock_container
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
if container_detail is not None:
|
||||
self.mock_docker.inspect_container.return_value = container_detail
|
||||
|
||||
self.conductor.container_show(None, mock_container_uuid)
|
||||
|
||||
if mock_docker_id:
|
||||
self.mock_docker.inspect_container.assert_called_once_with(
|
||||
mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
if expected_status is not None:
|
||||
self.assertEqual(expected_status, mock_container.status)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show(self, mock_find_container, mock_get_by_uuid):
|
||||
self._test_container_show(mock_find_container, mock_get_by_uuid)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_running_state(self, mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
mock_container_detail = {'State': {'Error': '',
|
||||
'Running': True,
|
||||
'Paused': False}}
|
||||
self._test_container_show(
|
||||
mock_find_container, mock_get_by_uuid, mock_container_detail,
|
||||
fields.ContainerStatus.RUNNING)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_stop_state(self, mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
mock_container_detail = {'State': {'Error': '',
|
||||
'Running': False,
|
||||
'Paused': False}}
|
||||
self._test_container_show(
|
||||
mock_find_container, mock_get_by_uuid, mock_container_detail,
|
||||
fields.ContainerStatus.STOPPED)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_pause_state(self, mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
mock_container_detail = {'State': {'Error': '',
|
||||
'Running': True,
|
||||
'Paused': True}}
|
||||
self._test_container_show(
|
||||
mock_find_container, mock_get_by_uuid, mock_container_detail,
|
||||
fields.ContainerStatus.PAUSED)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_error_status(self, mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
mock_container_detail = {'State': {'Error': True,
|
||||
'Running': False,
|
||||
'Paused': False}}
|
||||
self._test_container_show(
|
||||
mock_find_container, mock_get_by_uuid, mock_container_detail,
|
||||
fields.ContainerStatus.ERROR)
|
||||
|
||||
def _test_container_show_with_failure(
|
||||
self, mock_find_container, mock_get_by_uuid, error,
|
||||
assert_raise=True, expected_status=None):
|
||||
mock_container = mock.MagicMock()
|
||||
mock_get_by_uuid.return_value = mock_container
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-1d6b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
with mock.patch.object(errors.APIError, '__str__',
|
||||
return_value=error) as mock_init:
|
||||
self.mock_docker.inspect_container = mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', ''))
|
||||
|
||||
if assert_raise:
|
||||
self.assertRaises(exception.ContainerException,
|
||||
self.conductor.container_show,
|
||||
None, mock_container_uuid)
|
||||
else:
|
||||
self.conductor.container_show(None, mock_container_uuid)
|
||||
|
||||
self.mock_docker.inspect_container.assert_called_once_with(
|
||||
mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
mock_init.assert_called_with()
|
||||
if expected_status is not None:
|
||||
self.assertEqual(expected_status, mock_container.status)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_failure(self, mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
self._test_container_show_with_failure(
|
||||
mock_find_container, mock_get_by_uuid, error='hit error')
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_not_found(self, mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
self._test_container_show_with_failure(
|
||||
mock_find_container, mock_get_by_uuid, error='404 error',
|
||||
assert_raise=False, expected_status=fields.ContainerStatus.ERROR)
|
||||
|
||||
@mock.patch.object(objects.Container, 'get_by_uuid')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_show_with_not_found_from_docker(self,
|
||||
mock_find_container,
|
||||
mock_get_by_uuid):
|
||||
self._test_container_show(
|
||||
mock_find_container, mock_get_by_uuid, mock_docker_id={},
|
||||
expected_status=fields.ContainerStatus.ERROR)
|
||||
|
||||
def _test_container_exec(self, mock_find_container, docker_version='1.2.2',
|
||||
deprecated=False):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
docker.version = docker_version
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
mock_create_res = mock.MagicMock()
|
||||
self.mock_docker.exec_create.return_value = mock_create_res
|
||||
self.conductor.container_exec(None, mock_container_uuid, 'ls')
|
||||
if deprecated:
|
||||
self.mock_docker.execute.assert_called_once_with(
|
||||
mock_docker_id, 'ls')
|
||||
else:
|
||||
self.mock_docker.exec_create.assert_called_once_with(
|
||||
mock_docker_id, 'ls', True, True, False)
|
||||
self. mock_docker.exec_start.assert_called_once_with(
|
||||
mock_create_res, False, False, False)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_exec(self, mock_find_container):
|
||||
self._test_container_exec(mock_find_container)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_exec_deprecated(self, mock_find_container):
|
||||
self._test_container_exec(
|
||||
mock_find_container, docker_version='0.7.0', deprecated=True)
|
||||
|
||||
def _test_container_exec_with_failure(
|
||||
self, mock_find_container, docker_version='1.2.2',
|
||||
deprecated=False):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
docker.version = docker_version
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
with mock.patch.object(errors.APIError, '__str__',
|
||||
return_value='hit error') as mock_init:
|
||||
if deprecated:
|
||||
self.mock_docker.execute = mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', ''))
|
||||
else:
|
||||
self.mock_docker.exec_create = mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', ''))
|
||||
self.assertRaises(exception.ContainerException,
|
||||
self.conductor.container_exec,
|
||||
None, mock_container_uuid, 'ls')
|
||||
if deprecated:
|
||||
self.mock_docker.execute.assert_called_once_with(
|
||||
mock_docker_id, 'ls')
|
||||
else:
|
||||
self.mock_docker.exec_create.assert_called_once_with(
|
||||
mock_docker_id, 'ls', True, True, False)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
mock_init.assert_called_with()
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_exec_with_failure(self, mock_find_container):
|
||||
self._test_container_exec_with_failure(mock_find_container)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_exec_deprecated_with_failure(self, mock_find_container):
|
||||
self._test_container_exec_with_failure(
|
||||
mock_find_container, docker_version='0.7.0', deprecated=True)
|
||||
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_logs(self, mock_find_container):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
self.conductor.container_logs(None, mock_container_uuid)
|
||||
self.mock_docker.logs.assert_called_once_with(
|
||||
mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
|
||||
@mock.patch.object(errors.APIError, '__str__')
|
||||
@mock.patch.object(docker_conductor.Handler, '_find_container_by_name')
|
||||
def test_container_logs_with_failure(self, mock_find_container, mock_init):
|
||||
mock_container_uuid = 'd545a92d-609a-428f-8edb-16b02ad20ca1'
|
||||
mock_docker_id = '2703ef2b705d'
|
||||
mock_find_container.return_value = mock_docker_id
|
||||
mock_init.return_value = 'hit error'
|
||||
self.mock_docker.logs = mock.Mock(
|
||||
side_effect=errors.APIError('Error', '', ''))
|
||||
self.assertRaises(exception.ContainerException,
|
||||
self.conductor.container_logs,
|
||||
None, mock_container_uuid)
|
||||
self.mock_docker.logs.assert_called_once_with(
|
||||
mock_docker_id)
|
||||
mock_find_container.assert_called_once_with(self.mock_docker,
|
||||
mock_container_uuid)
|
||||
mock_init.assert_called_with()
|
||||
|
||||
def test_container_common_exception(self):
|
||||
self.dfc_context_manager.__enter__.side_effect = Exception("So bad")
|
||||
for action in ('container_exec', 'container_logs', 'container_show',
|
||||
'container_delete', 'container_create',
|
||||
'_container_action'):
|
||||
func = getattr(self.conductor, action)
|
||||
self.assertRaises(exception.ContainerException,
|
||||
func, None, None)
|
@ -28,7 +28,6 @@ class RPCAPITestCase(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(RPCAPITestCase, self).setUp()
|
||||
self.fake_bay = dbutils.get_test_bay(driver='fake-driver')
|
||||
self.fake_container = dbutils.get_test_container(driver='fake-driver')
|
||||
self.fake_rc = dbutils.get_test_rc(driver='fake-driver')
|
||||
self.fake_certificate = objects.Certificate.from_db_bay(self.fake_bay)
|
||||
self.fake_certificate.csr = 'fake-csr'
|
||||
@ -125,67 +124,6 @@ class RPCAPITestCase(base.DbTestCase):
|
||||
rc_ident=self.fake_rc['uuid'],
|
||||
bay_ident=self.fake_rc['bay_uuid'])
|
||||
|
||||
def test_container_create(self):
|
||||
self._test_rpcapi('container_create',
|
||||
'call',
|
||||
version='1.0',
|
||||
container=self.fake_container)
|
||||
|
||||
def test_container_delete(self):
|
||||
self._test_rpcapi('container_delete',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_show(self):
|
||||
self._test_rpcapi('container_show',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_reboot(self):
|
||||
self._test_rpcapi('container_reboot',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_stop(self):
|
||||
self._test_rpcapi('container_stop',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_start(self):
|
||||
self._test_rpcapi('container_start',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_pause(self):
|
||||
self._test_rpcapi('container_pause',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_unpause(self):
|
||||
self._test_rpcapi('container_unpause',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_logs(self):
|
||||
self._test_rpcapi('container_logs',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'])
|
||||
|
||||
def test_container_exec(self):
|
||||
self._test_rpcapi('container_exec',
|
||||
'call',
|
||||
version='1.0',
|
||||
container_uuid=self.fake_container['uuid'],
|
||||
command=self.fake_container['command'])
|
||||
|
||||
def test_ping_conductor(self):
|
||||
self._test_rpcapi('ping_conductor',
|
||||
'call',
|
||||
|
@ -35,13 +35,6 @@ class TestConductorUtils(base.TestCase):
|
||||
self._test_retrieve_bay(rc.bay_uuid,
|
||||
mock_bay_get_by_uuid)
|
||||
|
||||
@patch('magnum.objects.Bay.get_by_uuid')
|
||||
def test_retrieve_bay_from_container(self,
|
||||
mock_bay_get_by_uuid):
|
||||
container = objects.Container({})
|
||||
container.bay_uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
|
||||
self._test_retrieve_bay(container.bay_uuid, mock_bay_get_by_uuid)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_retrieve_baymodel(self, mock_baymodel_get_by_uuid):
|
||||
expected_context = 'context'
|
||||
|
@ -210,24 +210,6 @@ class DbBayTestCase(base.DbTestCase):
|
||||
self.dbapi.get_rc_by_id,
|
||||
self.context, rc.id)
|
||||
|
||||
def test_destroy_bay_that_has_containers(self):
|
||||
bay = utils.create_test_bay()
|
||||
container = utils.create_test_container(bay_uuid=bay.uuid)
|
||||
self.assertEqual(bay.uuid, container.bay_uuid)
|
||||
self.dbapi.destroy_bay(bay.id)
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.get_container_by_id,
|
||||
self.context, container.id)
|
||||
|
||||
def test_destroy_bay_that_has_containers_by_uuid(self):
|
||||
bay = utils.create_test_bay()
|
||||
container = utils.create_test_container(bay_uuid=bay.uuid)
|
||||
self.assertEqual(bay.uuid, container.bay_uuid)
|
||||
self.dbapi.destroy_bay(bay.uuid)
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.get_container_by_id,
|
||||
self.context, container.id)
|
||||
|
||||
def test_update_bay(self):
|
||||
bay = utils.create_test_bay()
|
||||
old_nc = bay.node_count
|
||||
|
@ -1,155 +0,0 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Tests for manipulating Containers via the DB API"""
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
|
||||
from magnum.common import exception
|
||||
from magnum.tests.unit.db import base
|
||||
from magnum.tests.unit.db import utils
|
||||
|
||||
|
||||
class DbContainerTestCase(base.DbTestCase):
|
||||
|
||||
def test_create_container(self):
|
||||
utils.create_test_container()
|
||||
|
||||
def test_create_container_already_exists(self):
|
||||
utils.create_test_container()
|
||||
self.assertRaises(exception.ContainerAlreadyExists,
|
||||
utils.create_test_container)
|
||||
|
||||
def test_get_container_by_id(self):
|
||||
container = utils.create_test_container()
|
||||
res = self.dbapi.get_container_by_id(self.context, container.id)
|
||||
self.assertEqual(container.id, res.id)
|
||||
self.assertEqual(container.uuid, res.uuid)
|
||||
|
||||
def test_get_container_by_uuid(self):
|
||||
container = utils.create_test_container()
|
||||
res = self.dbapi.get_container_by_uuid(self.context,
|
||||
container.uuid)
|
||||
self.assertEqual(container.id, res.id)
|
||||
self.assertEqual(container.uuid, res.uuid)
|
||||
|
||||
def test_get_container_by_name(self):
|
||||
container = utils.create_test_container()
|
||||
res = self.dbapi.get_container_by_name(self.context,
|
||||
container.name)
|
||||
self.assertEqual(container.id, res.id)
|
||||
self.assertEqual(container.uuid, res.uuid)
|
||||
|
||||
def test_get_container_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.get_container_by_id, self.context, 99)
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.get_container_by_uuid,
|
||||
self.context,
|
||||
uuidutils.generate_uuid())
|
||||
|
||||
def test_get_container_list(self):
|
||||
uuids = []
|
||||
for i in range(1, 6):
|
||||
container = utils.create_test_container(
|
||||
uuid=uuidutils.generate_uuid())
|
||||
uuids.append(six.text_type(container['uuid']))
|
||||
res = self.dbapi.get_container_list(self.context)
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(sorted(uuids), sorted(res_uuids))
|
||||
|
||||
def test_get_container_list_sorted(self):
|
||||
uuids = []
|
||||
for _ in range(5):
|
||||
container = utils.create_test_container(
|
||||
uuid=uuidutils.generate_uuid())
|
||||
uuids.append(six.text_type(container.uuid))
|
||||
res = self.dbapi.get_container_list(self.context, sort_key='uuid')
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(sorted(uuids), res_uuids)
|
||||
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.get_container_list,
|
||||
self.context,
|
||||
sort_key='foo')
|
||||
|
||||
def test_get_container_list_with_filters(self):
|
||||
container1 = utils.create_test_container(
|
||||
name='container-one',
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
bay_uuid=uuidutils.generate_uuid())
|
||||
container2 = utils.create_test_container(
|
||||
name='container-two',
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
bay_uuid=uuidutils.generate_uuid())
|
||||
|
||||
res = self.dbapi.get_container_list(self.context,
|
||||
filters={'name': 'container-one'})
|
||||
self.assertEqual([container1.id], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_container_list(self.context,
|
||||
filters={'name': 'container-two'})
|
||||
self.assertEqual([container2.id], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_container_list(self.context,
|
||||
filters={'name': 'bad-container'})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_container_list(
|
||||
self.context,
|
||||
filters={'bay_uuid': container1.bay_uuid})
|
||||
self.assertEqual([container1.id], [r.id for r in res])
|
||||
|
||||
def test_destroy_container(self):
|
||||
container = utils.create_test_container()
|
||||
self.dbapi.destroy_container(container.id)
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.get_container_by_id,
|
||||
self.context, container.id)
|
||||
|
||||
def test_destroy_container_by_uuid(self):
|
||||
container = utils.create_test_container()
|
||||
self.dbapi.destroy_container(container.uuid)
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.get_container_by_uuid,
|
||||
self.context, container.uuid)
|
||||
|
||||
def test_destroy_container_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.destroy_container,
|
||||
uuidutils.generate_uuid())
|
||||
|
||||
def test_update_container(self):
|
||||
container = utils.create_test_container()
|
||||
old_image = container.image
|
||||
new_image = 'new-image'
|
||||
self.assertNotEqual(old_image, new_image)
|
||||
|
||||
res = self.dbapi.update_container(container.id,
|
||||
{'image': new_image})
|
||||
self.assertEqual(new_image, res.image)
|
||||
|
||||
def test_update_container_not_found(self):
|
||||
container_uuid = uuidutils.generate_uuid()
|
||||
new_image = 'new-image'
|
||||
self.assertRaises(exception.ContainerNotFound,
|
||||
self.dbapi.update_container,
|
||||
container_uuid, {'image': new_image})
|
||||
|
||||
def test_update_container_uuid(self):
|
||||
container = utils.create_test_container()
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.update_container, container.id,
|
||||
{'uuid': ''})
|
@ -121,39 +121,6 @@ def create_test_bay(**kw):
|
||||
return dbapi.create_bay(bay)
|
||||
|
||||
|
||||
def get_test_container(**kw):
|
||||
return {
|
||||
'id': kw.get('id', 42),
|
||||
'uuid': kw.get('uuid', 'ea8e2a25-2901-438d-8157-de7ffd68d051'),
|
||||
'name': kw.get('name', 'container1'),
|
||||
'project_id': kw.get('project_id', 'fake_project'),
|
||||
'user_id': kw.get('user_id', 'fake_user'),
|
||||
'image': kw.get('image', 'ubuntu'),
|
||||
'created_at': kw.get('created_at'),
|
||||
'updated_at': kw.get('updated_at'),
|
||||
'command': kw.get('command', 'fake_command'),
|
||||
'bay_uuid': kw.get('bay_uuid', 'fff114da-3bfa-4a0f-a123-c0dffad9718e'),
|
||||
'status': kw.get('state', 'Running'),
|
||||
'memory': kw.get('memory', '512m'),
|
||||
'environment': kw.get('environment', {'key1': 'val1', 'key2': 'val2'}),
|
||||
}
|
||||
|
||||
|
||||
def create_test_container(**kw):
|
||||
"""Create test container entry in DB and return Container DB object.
|
||||
|
||||
Function to be used to create test Container objects in the database.
|
||||
:param kw: kwargs with overriding values for container's attributes.
|
||||
:returns: Test Container DB object.
|
||||
"""
|
||||
container = get_test_container(**kw)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kw:
|
||||
del container['id']
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_container(container)
|
||||
|
||||
|
||||
def get_test_rc(**kw):
|
||||
return {
|
||||
'id': kw.get('id', 42),
|
||||
|
@ -1,141 +0,0 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from magnum import objects
|
||||
from magnum.tests.unit.db import base
|
||||
from magnum.tests.unit.db import utils
|
||||
|
||||
|
||||
class TestContainerObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestContainerObject, self).setUp()
|
||||
self.fake_container = utils.get_test_container()
|
||||
|
||||
def test_get_by_id(self):
|
||||
container_id = self.fake_container['id']
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_id',
|
||||
autospec=True) as mock_get_container:
|
||||
mock_get_container.return_value = self.fake_container
|
||||
container = objects.Container.get_by_id(self.context,
|
||||
container_id)
|
||||
mock_get_container.assert_called_once_with(self.context,
|
||||
container_id)
|
||||
self.assertEqual(self.context, container._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
uuid = self.fake_container['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_uuid',
|
||||
autospec=True) as mock_get_container:
|
||||
mock_get_container.return_value = self.fake_container
|
||||
container = objects.Container.get_by_uuid(self.context, uuid)
|
||||
mock_get_container.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, container._context)
|
||||
|
||||
def test_get_by_name(self):
|
||||
name = self.fake_container['name']
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_name',
|
||||
autospec=True) as mock_get_container:
|
||||
mock_get_container.return_value = self.fake_container
|
||||
container = objects.Container.get_by_name(self.context, name)
|
||||
mock_get_container.assert_called_once_with(self.context, name)
|
||||
self.assertEqual(self.context, container._context)
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'get_container_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_container]
|
||||
containers = objects.Container.list(self.context)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
self.assertThat(containers, HasLength(1))
|
||||
self.assertIsInstance(containers[0], objects.Container)
|
||||
self.assertEqual(self.context, containers[0]._context)
|
||||
|
||||
def test_list_with_filters(self):
|
||||
with mock.patch.object(self.dbapi, 'get_container_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_container]
|
||||
containers = objects.Container.list(self.context,
|
||||
filters={'bay_uuid': 'uuid'})
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
self.assertThat(containers, HasLength(1))
|
||||
self.assertIsInstance(containers[0], objects.Container)
|
||||
self.assertEqual(self.context, containers[0]._context)
|
||||
mock_get_list.assert_called_once_with(self.context,
|
||||
filters={'bay_uuid': 'uuid'},
|
||||
limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_container',
|
||||
autospec=True) as mock_create_container:
|
||||
mock_create_container.return_value = self.fake_container
|
||||
container = objects.Container(self.context, **self.fake_container)
|
||||
container.create()
|
||||
mock_create_container.assert_called_once_with(self.fake_container)
|
||||
self.assertEqual(self.context, container._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_container['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_uuid',
|
||||
autospec=True) as mock_get_container:
|
||||
mock_get_container.return_value = self.fake_container
|
||||
with mock.patch.object(self.dbapi, 'destroy_container',
|
||||
autospec=True) as mock_destroy_container:
|
||||
container = objects.Container.get_by_uuid(self.context, uuid)
|
||||
container.destroy()
|
||||
mock_get_container.assert_called_once_with(self.context, uuid)
|
||||
mock_destroy_container.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, container._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_container['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_uuid',
|
||||
autospec=True) as mock_get_container:
|
||||
mock_get_container.return_value = self.fake_container
|
||||
with mock.patch.object(self.dbapi, 'update_container',
|
||||
autospec=True) as mock_update_container:
|
||||
container = objects.Container.get_by_uuid(self.context, uuid)
|
||||
container.image = 'container.img'
|
||||
container.memory = '512m'
|
||||
container.environment = {"key1": "val", "key2": "val2"}
|
||||
container.save()
|
||||
|
||||
mock_get_container.assert_called_once_with(self.context, uuid)
|
||||
mock_update_container.assert_called_once_with(
|
||||
uuid, {'image': 'container.img', 'memory': '512m',
|
||||
'environment': {"key1": "val", "key2": "val2"}})
|
||||
self.assertEqual(self.context, container._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_container['uuid']
|
||||
new_uuid = uuidutils.generate_uuid()
|
||||
returns = [dict(self.fake_container, uuid=uuid),
|
||||
dict(self.fake_container, uuid=new_uuid)]
|
||||
expected = [mock.call(self.context, uuid),
|
||||
mock.call(self.context, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_container_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_container:
|
||||
container = objects.Container.get_by_uuid(self.context, uuid)
|
||||
self.assertEqual(uuid, container.uuid)
|
||||
container.refresh()
|
||||
self.assertEqual(new_uuid, container.uuid)
|
||||
self.assertEqual(expected, mock_get_container.call_args_list)
|
||||
self.assertEqual(self.context, container._context)
|
@ -365,7 +365,6 @@ object_data = {
|
||||
'Bay': '1.5-a3b9292ef5d35175b93ca46ba3baec2d',
|
||||
'BayModel': '1.14-ae175b4aaba2c60df37cac63ef734853',
|
||||
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
|
||||
'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6',
|
||||
'MyObj': '1.0-b43567e512438205e32f4e95ca616697',
|
||||
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
|
||||
'ReplicationController': '1.0-a471c2429c212ed91833cfcf0f934eab',
|
||||
|
@ -154,30 +154,6 @@ def get_test_magnum_service_object(context, **kw):
|
||||
return magnum_service
|
||||
|
||||
|
||||
def create_test_container(context, **kw):
|
||||
"""Create and return a test container object.
|
||||
|
||||
Create a container in the DB and return a container object with
|
||||
appropriate attributes.
|
||||
"""
|
||||
container = get_test_container(context, **kw)
|
||||
container.create()
|
||||
return container
|
||||
|
||||
|
||||
def get_test_container(context, **kw):
|
||||
"""Return a test container object with appropriate attributes.
|
||||
|
||||
NOTE: The object leaves the attributes marked as changed, such
|
||||
that a create() could be used to commit it to the DB.
|
||||
"""
|
||||
db_container = db_utils.get_test_container(**kw)
|
||||
container = objects.Container(context)
|
||||
for key in db_container:
|
||||
setattr(container, key, db_container[key])
|
||||
return container
|
||||
|
||||
|
||||
def datetime_or_none(dt):
|
||||
"""Validate a datetime or None value."""
|
||||
if dt is None:
|
||||
|
Loading…
Reference in New Issue
Block a user