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:
Tom Cammann 2016-06-02 11:13:49 +01:00 committed by Spyros Trigazis
parent d92c76f6d1
commit 40aa6550f1
24 changed files with 31 additions and 1788 deletions

View File

@ -27,13 +27,6 @@
"rc:get_all": "rule:default", "rc:get_all": "rule:default",
"rc:update": "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:create": "rule:admin_or_user",
"certificate:get": "rule:admin_or_user", "certificate:get": "rule:admin_or_user",

View File

@ -28,7 +28,6 @@ from magnum.common import short_id
from magnum.conductor.handlers import bay_conductor from magnum.conductor.handlers import bay_conductor
from magnum.conductor.handlers import ca_conductor from magnum.conductor.handlers import ca_conductor
from magnum.conductor.handlers import conductor_listener 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 indirection_api
from magnum.conductor.handlers import k8s_conductor from magnum.conductor.handlers import k8s_conductor
from magnum.i18n import _LI from magnum.i18n import _LI
@ -51,7 +50,6 @@ def main():
conductor_id = short_id.generate_id() conductor_id = short_id.generate_id()
endpoints = [ endpoints = [
indirection_api.Handler(), indirection_api.Handler(),
docker_conductor.Handler(),
k8s_conductor.Handler(), k8s_conductor.Handler(),
bay_conductor.Handler(), bay_conductor.Handler(),
conductor_listener.Handler(), conductor_listener.Handler(),

View File

@ -18,11 +18,9 @@ from docker import client
from docker import tls from docker import tls
from docker.utils import utils from docker.utils import utils
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import uuidutils
from magnum.conductor.handlers.common import cert_manager from magnum.conductor.handlers.common import cert_manager
from magnum.conductor import utils as conductor_utils from magnum.conductor import utils as conductor_utils
from magnum import objects
docker_opts = [ docker_opts = [
@ -76,15 +74,6 @@ def is_docker_api_version_atleast(docker, version):
return False 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 @contextlib.contextmanager
def docker_for_bay(context, bay): def docker_for_bay(context, bay):
baymodel = conductor_utils.retrieve_baymodel(context, bay) baymodel = conductor_utils.retrieve_baymodel(context, bay)

View File

@ -59,39 +59,6 @@ class API(rpc_service.API):
return self._call('rc_show', rc_ident=rc_ident, return self._call('rc_show', rc_ident=rc_ident,
bay_ident=bay_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 # CA operations
def sign_certificate(self, bay, certificate): def sign_certificate(self, bay, certificate):

View File

@ -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}

View File

@ -27,10 +27,10 @@ LOG = log.getLogger(__name__)
CONF = cfg.CONF CONF = cfg.CONF
CONF.import_opt('docker_remote_api_version', CONF.import_opt('docker_remote_api_version',
'magnum.conductor.handlers.docker_conductor', 'magnum.common.docker_utils',
group='docker') group='docker')
CONF.import_opt('default_timeout', CONF.import_opt('default_timeout',
'magnum.conductor.handlers.docker_conductor', 'magnum.common.docker_utils',
group='docker') group='docker')
COE_CLASS_PATH = { COE_CLASS_PATH = {

View File

@ -204,90 +204,6 @@ class Connection(object):
:raises: BayModelNotFound :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 @abc.abstractmethod
def get_rc_list(self, context, filters=None, limit=None, def get_rc_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None): marker=None, sort_key=None, sort_dir=None):

View File

@ -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')

View File

@ -198,11 +198,6 @@ class Connection(api.Connection):
if query.count() != 0: if query.count() != 0:
query.delete() 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() session = get_session()
with session.begin(): with session.begin():
query = model_query(models.Bay, session=session) query = model_query(models.Bay, session=session)
@ -370,103 +365,6 @@ class Connection(api.Connection):
ref.update(values) ref.update(values)
return ref 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): def _add_rcs_filters(self, query, filters):
if filters is None: if filters is None:
filters = {} filters = {}

View File

@ -179,27 +179,6 @@ class BayModel(Base):
master_lb_enabled = Column(Boolean, default=False) 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): class ReplicationController(Base):
"""Represents a pod replication controller.""" """Represents a pod replication controller."""

View File

@ -15,13 +15,11 @@
from magnum.objects import bay from magnum.objects import bay
from magnum.objects import baymodel from magnum.objects import baymodel
from magnum.objects import certificate from magnum.objects import certificate
from magnum.objects import container
from magnum.objects import magnum_service from magnum.objects import magnum_service
from magnum.objects import replicationcontroller as rc from magnum.objects import replicationcontroller as rc
from magnum.objects import x509keypair from magnum.objects import x509keypair
Container = container.Container
Bay = bay.Bay Bay = bay.Bay
BayModel = baymodel.BayModel BayModel = baymodel.BayModel
MagnumService = magnum_service.MagnumService MagnumService = magnum_service.MagnumService
@ -30,7 +28,6 @@ X509KeyPair = x509keypair.X509KeyPair
Certificate = certificate.Certificate Certificate = certificate.Certificate
__all__ = (Bay, __all__ = (Bay,
BayModel, BayModel,
Container,
MagnumService, MagnumService,
ReplicationController, ReplicationController,
X509KeyPair, X509KeyPair,

View File

@ -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]

View File

@ -25,7 +25,6 @@ import magnum.common.service
import magnum.common.x509.config import magnum.common.x509.config
import magnum.conductor.config import magnum.conductor.config
import magnum.conductor.handlers.bay_conductor import magnum.conductor.handlers.bay_conductor
import magnum.conductor.handlers.docker_conductor
import magnum.conductor.handlers.k8s_conductor import magnum.conductor.handlers.k8s_conductor
import magnum.db import magnum.db
import magnum.drivers.common.template_def import magnum.drivers.common.template_def

View File

@ -39,13 +39,6 @@ policy_data = """
"rc:detail": "", "rc:detail": "",
"rc:get": "", "rc:get": "",
"rc:get_all": "", "rc:get_all": "",
"rc:update": "", "rc:update": ""
"container:create": "",
"container:delete": "",
"container:detail": "",
"container:get": "",
"container:get_all": "",
"container:update": ""
} }
""" """

View File

@ -29,123 +29,6 @@ CONF.import_opt('default_timeout', 'magnum.common.docker_utils',
class TestDockerUtils(base.BaseTestCase): 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): def test_is_docker_api_version_atleast(self):

View File

@ -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)

View File

@ -28,7 +28,6 @@ class RPCAPITestCase(base.DbTestCase):
def setUp(self): def setUp(self):
super(RPCAPITestCase, self).setUp() super(RPCAPITestCase, self).setUp()
self.fake_bay = dbutils.get_test_bay(driver='fake-driver') 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_rc = dbutils.get_test_rc(driver='fake-driver')
self.fake_certificate = objects.Certificate.from_db_bay(self.fake_bay) self.fake_certificate = objects.Certificate.from_db_bay(self.fake_bay)
self.fake_certificate.csr = 'fake-csr' self.fake_certificate.csr = 'fake-csr'
@ -125,67 +124,6 @@ class RPCAPITestCase(base.DbTestCase):
rc_ident=self.fake_rc['uuid'], rc_ident=self.fake_rc['uuid'],
bay_ident=self.fake_rc['bay_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): def test_ping_conductor(self):
self._test_rpcapi('ping_conductor', self._test_rpcapi('ping_conductor',
'call', 'call',

View File

@ -35,13 +35,6 @@ class TestConductorUtils(base.TestCase):
self._test_retrieve_bay(rc.bay_uuid, self._test_retrieve_bay(rc.bay_uuid,
mock_bay_get_by_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') @patch('magnum.objects.BayModel.get_by_uuid')
def test_retrieve_baymodel(self, mock_baymodel_get_by_uuid): def test_retrieve_baymodel(self, mock_baymodel_get_by_uuid):
expected_context = 'context' expected_context = 'context'

View File

@ -210,24 +210,6 @@ class DbBayTestCase(base.DbTestCase):
self.dbapi.get_rc_by_id, self.dbapi.get_rc_by_id,
self.context, rc.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): def test_update_bay(self):
bay = utils.create_test_bay() bay = utils.create_test_bay()
old_nc = bay.node_count old_nc = bay.node_count

View File

@ -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': ''})

View File

@ -121,39 +121,6 @@ def create_test_bay(**kw):
return dbapi.create_bay(bay) 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): def get_test_rc(**kw):
return { return {
'id': kw.get('id', 42), 'id': kw.get('id', 42),

View File

@ -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)

View File

@ -365,7 +365,6 @@ object_data = {
'Bay': '1.5-a3b9292ef5d35175b93ca46ba3baec2d', 'Bay': '1.5-a3b9292ef5d35175b93ca46ba3baec2d',
'BayModel': '1.14-ae175b4aaba2c60df37cac63ef734853', 'BayModel': '1.14-ae175b4aaba2c60df37cac63ef734853',
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2', 'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6',
'MyObj': '1.0-b43567e512438205e32f4e95ca616697', 'MyObj': '1.0-b43567e512438205e32f4e95ca616697',
'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd', 'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
'ReplicationController': '1.0-a471c2429c212ed91833cfcf0f934eab', 'ReplicationController': '1.0-a471c2429c212ed91833cfcf0f934eab',

View File

@ -154,30 +154,6 @@ def get_test_magnum_service_object(context, **kw):
return magnum_service 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): def datetime_or_none(dt):
"""Validate a datetime or None value.""" """Validate a datetime or None value."""
if dt is None: if dt is None: