Remove pod object

Following on from removing the k8s specific APIs in
I1f6f04a35dfbb39f217487fea104ded035b75569 the objects associated with
these APIs need removal.

Remove the pod object, drop the db table and remove references to the
pod object.

Change-Id: I8c2499ccb97aae39d80868ce02fbef292d762c10
Partially-Implements: blueprint delete-container-endpoint
changes/45/320445/3
Tom Cammann 7 years ago
parent 988593e11f
commit d20e5ef715
  1. 7
      etc/magnum/policy.json
  2. 20
      magnum/conductor/api.py
  3. 126
      magnum/conductor/handlers/k8s_conductor.py
  4. 80
      magnum/db/api.py
  5. 28
      magnum/db/sqlalchemy/alembic/versions/ef08a5e057bd_remove_pod.py
  6. 100
      magnum/db/sqlalchemy/api.py
  7. 21
      magnum/db/sqlalchemy/models.py
  8. 3
      magnum/objects/__init__.py
  9. 122
      magnum/objects/pod.py
  10. 7
      magnum/tests/fake_policy.py
  11. 6
      magnum/tests/unit/api/controllers/v1/test_bay.py
  12. 202
      magnum/tests/unit/conductor/handlers/test_k8s_conductor.py
  13. 28
      magnum/tests/unit/conductor/test_rpcapi.py
  14. 7
      magnum/tests/unit/conductor/test_utils.py
  15. 54
      magnum/tests/unit/db/sqlalchemy/test_types.py
  16. 16
      magnum/tests/unit/db/test_bay.py
  17. 183
      magnum/tests/unit/db/test_pod.py
  18. 33
      magnum/tests/unit/db/utils.py
  19. 1
      magnum/tests/unit/objects/test_objects.py
  20. 100
      magnum/tests/unit/objects/test_pod.py
  21. 27
      magnum/tests/unit/objects/utils.py

@ -20,13 +20,6 @@
"baymodel:update": "rule:default",
"baymodel:publish": "rule:admin_or_owner",
"pod:create": "rule:default",
"pod:delete": "rule:default",
"pod:detail": "rule:default",
"pod:get": "rule:default",
"pod:get_all": "rule:default",
"pod:update": "rule:default",
"rc:create": "rule:default",
"rc:delete": "rule:default",
"rc:detail": "rule:default",

@ -61,26 +61,6 @@ class API(rpc_service.API):
return self._call('service_show', service_ident=service_ident,
bay_ident=bay_ident)
# Pod Operations
def pod_create(self, pod):
return self._call('pod_create', pod=pod)
def pod_list(self, context, bay_ident):
return self._call('pod_list', bay_ident=bay_ident)
def pod_update(self, pod_ident, bay_ident, manifest):
return self._call('pod_update', pod_ident=pod_ident,
bay_ident=bay_ident, manifest=manifest)
def pod_delete(self, pod_ident, bay_ident):
return self._call('pod_delete', pod_ident=pod_ident,
bay_ident=bay_ident)
def pod_show(self, context, pod_ident, bay_ident):
return self._call('pod_show', pod_ident=pod_ident,
bay_ident=bay_ident)
# ReplicationController Operations
def rc_create(self, rc):

@ -191,132 +191,6 @@ class Handler(object):
return services
# Pod Operations
def pod_create(self, context, pod):
LOG.debug("pod_create")
bay = conductor_utils.retrieve_bay(context, pod.bay_uuid)
self.k8s_api = k8s.create_k8s_api(context, bay)
manifest = k8s_manifest.parse(pod.manifest)
try:
resp = self.k8s_api.create_namespaced_pod(body=manifest,
namespace='default')
except rest.ApiException as err:
pod.status = 'failed'
raise exception.KubernetesAPIFailed(err=err)
if resp is None:
raise exception.PodCreationFailed(bay_uuid=pod.bay_uuid)
pod['uuid'] = resp.metadata.uid
pod['name'] = resp.metadata.name
pod['images'] = [c.image for c in resp.spec.containers]
pod['labels'] = ast.literal_eval(resp.metadata.labels)
pod['status'] = resp.status.phase
pod['host'] = resp.spec.node_name
return pod
def pod_update(self, context, pod_ident, bay_ident, manifest):
LOG.debug("pod_update %s", pod_ident)
bay = conductor_utils.retrieve_bay(context, bay_ident)
self.k8s_api = k8s.create_k8s_api(context, bay)
if uuidutils.is_uuid_like(pod_ident):
pod = objects.Pod.get_by_uuid(context, pod_ident,
bay.uuid, self.k8s_api)
else:
pod = objects.Pod.get_by_name(context, pod_ident,
bay.uuid, self.k8s_api)
pod_ident = pod.name
try:
resp = self.k8s_api.replace_namespaced_pod(name=str(pod_ident),
body=manifest,
namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
if resp is None:
raise exception.PodNotFound(pod=pod.uuid)
pod['uuid'] = resp.metadata.uid
pod['name'] = resp.metadata.name
pod['project_id'] = context.project_id
pod['user_id'] = context.user_id
pod['bay_uuid'] = bay.uuid
pod['images'] = [c.image for c in resp.spec.containers]
if not resp.metadata.labels:
pod['labels'] = {}
else:
pod['labels'] = ast.literal_eval(resp.metadata.labels)
pod['status'] = resp.status.phase
pod['host'] = resp.spec.node_name
return pod
def pod_delete(self, context, pod_ident, bay_ident):
LOG.debug("pod_delete %s", pod_ident)
bay = conductor_utils.retrieve_bay(context, bay_ident)
self.k8s_api = k8s.create_k8s_api(context, bay)
if uuidutils.is_uuid_like(pod_ident):
pod = objects.Pod.get_by_uuid(context, pod_ident,
bay.uuid, self.k8s_api)
pod_name = pod.name
else:
pod_name = pod_ident
if conductor_utils.object_has_stack(context, bay.uuid):
try:
self.k8s_api.delete_namespaced_pod(name=str(pod_name), body={},
namespace='default')
except rest.ApiException as err:
if err.status == 404:
pass
else:
raise exception.KubernetesAPIFailed(err=err)
def pod_show(self, context, pod_ident, bay_ident):
LOG.debug("pod_show %s", pod_ident)
bay = conductor_utils.retrieve_bay(context, bay_ident)
self.k8s_api = k8s.create_k8s_api(context, bay)
if uuidutils.is_uuid_like(pod_ident):
pod = objects.Pod.get_by_uuid(context, pod_ident,
bay.uuid, self.k8s_api)
else:
pod = objects.Pod.get_by_name(context, pod_ident,
bay.uuid, self.k8s_api)
return pod
def pod_list(self, context, bay_ident):
bay = conductor_utils.retrieve_bay(context, bay_ident)
self.k8s_api = k8s.create_k8s_api(context, bay)
try:
resp = self.k8s_api.list_namespaced_pod(namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
if resp is None:
raise exception.PodListNotFound(bay_uuid=bay.uuid)
pods = []
for pod_entry in resp.items:
pod = {}
pod['uuid'] = pod_entry.metadata.uid
pod['name'] = pod_entry.metadata.name
pod['project_id'] = context.project_id
pod['user_id'] = context.user_id
pod['bay_uuid'] = bay.uuid
pod['images'] = [c.image for c in pod_entry.spec.containers]
if not pod_entry.metadata.labels:
pod['labels'] = {}
else:
pod['labels'] = ast.literal_eval(pod_entry.metadata.labels)
pod['status'] = pod_entry.status.phase
pod['host'] = pod_entry.spec.node_name
pod_obj = objects.Pod(context, **pod)
pods.append(pod_obj)
return pods
# Replication Controller Operations
def rc_create(self, context, rc):
LOG.debug("rc_create")

@ -288,86 +288,6 @@ class Connection(object):
:raises: ContainerNotFound
"""
@abc.abstractmethod
def get_pod_list(self, context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
"""Get matching pods.
Return a list of the specified columns for all pods that match the
specified filters.
:param context: The security context
:param filters: Filters to apply. Defaults to None.
:param limit: Maximum number of pods 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_pod(self, values):
"""Create a new pod.
:param values: A dict containing several items used to identify
and track the pod, and several dicts which are passed
into the Drivers when managing this pod. For example:
::
{
'uuid': uuidutils.generate_uuid(),
'name': 'example',
'type': 'virt'
}
:returns: A pod.
"""
@abc.abstractmethod
def get_pod_by_id(self, context, pod_id):
"""Return a pod.
:param context: The security context
:param pod_id: The id of a pod.
:returns: A pod.
"""
@abc.abstractmethod
def get_pod_by_uuid(self, context, pod_uuid):
"""Return a pod.
:param context: The security context
:param pod_uuid: The uuid of a pod.
:returns: A pod.
"""
@abc.abstractmethod
def get_pod_by_name(self, pod_name):
"""Return a pod.
:param pod_name: The name of a pod.
:returns: A pod.
"""
@abc.abstractmethod
def destroy_pod(self, pod_id):
"""Destroy a pod and all associated interfaces.
:param pod_id: The id or uuid of a pod.
"""
@abc.abstractmethod
def update_pod(self, pod_id, values):
"""Update properties of a pod.
:param pod_id: The id or uuid of a pod.
:returns: A pod.
:raises: PodNotFound
"""
@abc.abstractmethod
def get_service_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 pod object
Revision ID: ef08a5e057bd
Revises: e647f5931da8
Create Date: 2016-05-24 13:52:39.782156
"""
# revision identifiers, used by Alembic.
revision = 'ef08a5e057bd'
down_revision = 'e647f5931da8'
from alembic import op
def upgrade():
op.drop_table('pod')

@ -193,11 +193,6 @@ class Connection(api.Connection):
def destroy_bay(self, bay_id):
def destroy_bay_resources(session, bay_uuid):
"""Checks whether the bay does not have resources."""
query = model_query(models.Pod, session=session)
query = self._add_pods_filters(query, {'bay_uuid': bay_uuid})
if query.count() != 0:
query.delete()
query = model_query(models.Service, session=session)
query = self._add_services_filters(query, {'bay_uuid': bay_uuid})
if query.count() != 0:
@ -477,101 +472,6 @@ class Connection(api.Connection):
ref.update(values)
return ref
def _add_pods_filters(self, query, filters):
if filters is None:
filters = {}
if 'bay_uuid' in filters:
query = query.filter_by(bay_uuid=filters['bay_uuid'])
if 'name' in filters:
query = query.filter_by(name=filters['name'])
if 'status' in filters:
query = query.filter_by(status=filters['status'])
return query
def get_pod_list(self, context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
query = model_query(models.Pod)
query = self._add_tenant_filters(context, query)
query = self._add_pods_filters(query, filters)
return _paginate_query(models.Pod, limit, marker,
sort_key, sort_dir, query)
def create_pod(self, values):
# ensure defaults are present for new pods
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
pod = models.Pod()
pod.update(values)
try:
pod.save()
except db_exc.DBDuplicateEntry:
raise exception.PodAlreadyExists(uuid=values['uuid'])
return pod
def get_pod_by_id(self, context, pod_id):
query = model_query(models.Pod)
query = self._add_tenant_filters(context, query)
query = query.filter_by(id=pod_id)
try:
return query.one()
except NoResultFound:
raise exception.PodNotFound(pod=pod_id)
def get_pod_by_uuid(self, context, pod_uuid):
query = model_query(models.Pod)
query = self._add_tenant_filters(context, query)
query = query.filter_by(uuid=pod_uuid)
try:
return query.one()
except NoResultFound:
raise exception.PodNotFound(pod=pod_uuid)
def get_pod_by_name(self, pod_name):
query = model_query(models.Pod).filter_by(name=pod_name)
try:
return query.one()
except MultipleResultsFound:
raise exception.Conflict('Multiple pods exist with same name.'
' Please use the pod uuid instead.')
except NoResultFound:
raise exception.PodNotFound(pod=pod_name)
def destroy_pod(self, pod_id):
session = get_session()
with session.begin():
query = model_query(models.Pod, session=session)
query = add_identity_filter(query, pod_id)
count = query.delete()
if count != 1:
raise exception.PodNotFound(pod_id)
def update_pod(self, pod_id, values):
# NOTE(dtantsur): this can lead to very strange errors
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Pod.")
raise exception.InvalidParameterValue(err=msg)
return self._do_update_pod(pod_id, values)
def _do_update_pod(self, pod_id, values):
session = get_session()
with session.begin():
query = model_query(models.Pod, session=session)
query = add_identity_filter(query, pod_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.PodNotFound(pod=pod_id)
if 'provision_state' in values:
values['provision_updated_at'] = timeutils.utcnow()
ref.update(values)
return ref
def _add_services_filters(self, query, filters):
if filters is None:
filters = {}

@ -194,27 +194,6 @@ class Container(Base):
environment = Column(JSONEncodedDict)
class Pod(Base):
"""Represents a pod."""
__tablename__ = 'pod'
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_pod0uuid'),
table_args()
)
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
name = Column(String(255))
desc = Column(String(255))
bay_uuid = Column(String(36))
images = Column(JSONEncodedList)
labels = Column(JSONEncodedDict)
status = Column(String(255))
project_id = Column(String(255))
user_id = Column(String(255))
host = Column(String(255))
class Service(Base):
"""Represents a software service."""

@ -17,7 +17,6 @@ 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 pod
from magnum.objects import replicationcontroller as rc
from magnum.objects import service
from magnum.objects import x509keypair
@ -27,7 +26,6 @@ Container = container.Container
Bay = bay.Bay
BayModel = baymodel.BayModel
MagnumService = magnum_service.MagnumService
Pod = pod.Pod
ReplicationController = rc.ReplicationController
Service = service.Service
X509KeyPair = x509keypair.X509KeyPair
@ -36,7 +34,6 @@ __all__ = (Bay,
BayModel,
Container,
MagnumService,
Pod,
ReplicationController,
Service,
X509KeyPair,

@ -1,122 +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.
import ast
from k8sclient.client import rest
from oslo_versionedobjects import fields
from magnum.common import exception
from magnum.db import api as dbapi
from magnum.objects import base
@base.MagnumObjectRegistry.register
class Pod(base.MagnumPersistentObject, base.MagnumObject,
base.MagnumObjectDictCompat):
# Version 1.0: Initial version
# Version 1.1: Remove unused Pod object API 'list_by_bay_uuid'
VERSION = '1.1'
dbapi = dbapi.get_instance()
fields = {
'id': fields.IntegerField(),
'uuid': fields.StringField(nullable=True),
'name': fields.StringField(nullable=True),
'desc': fields.StringField(nullable=True),
'project_id': fields.StringField(nullable=True),
'user_id': fields.StringField(nullable=True),
'bay_uuid': fields.StringField(nullable=True),
'images': fields.ListOfStringsField(nullable=True),
'labels': fields.DictOfStringsField(nullable=True),
'status': fields.StringField(nullable=True),
'manifest_url': fields.StringField(nullable=True),
'manifest': fields.StringField(nullable=True),
'host': fields.StringField(nullable=True),
}
@base.remotable_classmethod
def get_by_uuid(cls, context, uuid, bay_uuid, k8s_api):
"""Find a pod based on pod uuid and the uuid for a bay.
:param context: Security context
:param uuid: the uuid of a pod.
:param bay_uuid: the UUID of the Bay
:param k8s_api: k8s API object
:returns: a :class:`Pod` object.
"""
try:
resp = k8s_api.list_namespaced_pod(namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
if resp is None:
raise exception.PodListNotFound(bay_uuid=bay_uuid)
pod = {}
for pod_entry in resp.items:
if pod_entry.metadata.uid == uuid:
pod['uuid'] = pod_entry.metadata.uid
pod['name'] = pod_entry.metadata.name
pod['project_id'] = context.project_id
pod['user_id'] = context.user_id
pod['bay_uuid'] = bay_uuid
pod['images'] = [c.image for c in pod_entry.spec.containers]
if not pod_entry.metadata.labels:
pod['labels'] = {}
else:
pod['labels'] = ast.literal_eval(pod_entry.metadata.labels)
pod['status'] = pod_entry.status.phase
pod['host'] = pod_entry.spec.node_name
pod_obj = Pod(context, **pod)
return pod_obj
raise exception.PodNotFound(pod=uuid)
@base.remotable_classmethod
def get_by_name(cls, context, name, bay_uuid, k8s_api):
"""Find a pod based on pod name and the uuid for a bay.
:param context: Security context
:param name: the name of a pod.
:param bay_uuid: the UUID of the Bay
:param k8s_api: k8s API object
:returns: a :class:`Pod` object.
"""
try:
resp = k8s_api.read_namespaced_pod(name=name,
namespace='default')
except rest.ApiException as err:
raise exception.KubernetesAPIFailed(err=err)
if resp is None:
raise exception.PodNotFound(pod=name)
pod = {}
pod['uuid'] = resp.metadata.uid
pod['name'] = resp.metadata.name
pod['project_id'] = context.project_id
pod['user_id'] = context.user_id
pod['bay_uuid'] = bay_uuid
pod['images'] = [c.image for c in resp.spec.containers]
if not resp.metadata.labels:
pod['labels'] = {}
else:
pod['labels'] = ast.literal_eval(resp.metadata.labels)
pod['status'] = resp.status.phase
pod['host'] = resp.spec.node_name
pod_obj = Pod(context, **pod)
return pod_obj

@ -34,13 +34,6 @@ policy_data = """
"baymodel:get_all": "",
"baymodel:update": "",
"pod:create": "",
"pod:delete": "",
"pod:detail": "",
"pod:get": "",
"pod:get_all": "",
"pod:update": "",
"rc:create": "",
"rc:delete": "",
"rc:detail": "",

@ -647,12 +647,6 @@ class TestDelete(api_base.FunctionalTest):
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['errors'])
def test_delete_bay_with_pods(self):
obj_utils.create_test_pod(self.context, bay_uuid=self.bay.uuid)
response = self.delete('/bays/%s' % self.bay.uuid,
expect_errors=True)
self.assertEqual(204, response.status_int)
def test_delete_bay_with_services(self):
obj_utils.create_test_service(self.context, bay_uuid=self.bay.uuid)
response = self.delete('/bays/%s' % self.bay.uuid,

@ -27,9 +27,6 @@ class TestK8sConductor(base.TestCase):
super(TestK8sConductor, self).setUp()
self.kube_handler = k8s_conductor.Handler()
def mock_pod(self):
return objects.Pod({})
def mock_service(self):
return objects.Service({})
@ -42,140 +39,6 @@ class TestK8sConductor(base.TestCase):
def mock_baymodel(self):
return objects.BayModel({})
@patch('magnum.conductor.utils.retrieve_bay')
@patch('ast.literal_eval')
def test_pod_create_with_success(self, mock_ast, mock_retrieve):
expected_pod = mock.MagicMock()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
expected_pod.bay_uuid = 'test-bay-uuid'
manifest = {"key": "value"}
expected_pod.manifest = '{"key": "value"}'
mock_ast.return_value = {}
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
self.kube_handler.pod_create(self.context, expected_pod)
(mock_kube_api.return_value.create_namespaced_pod
.assert_called_once_with(body=manifest, namespace='default'))
@patch('magnum.conductor.utils.retrieve_bay')
def test_pod_create_with_fail(self, mock_retrieve):
expected_pod = mock.MagicMock()
manifest = {"key": "value"}
expected_pod.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
err = rest.ApiException(status=500)
mock_kube_api.return_value.create_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_create,
self.context, expected_pod)
(mock_kube_api.return_value
.create_namespaced_pod
.assert_called_once_with(body=manifest,
namespace='default'))
@patch('magnum.conductor.utils.retrieve_bay')
def test_pod_create_fail_on_existing_pod(self, retrieve_bay):
expected_pod = mock.MagicMock()
expected_pod.manifest = '{"key": "value"}'
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
err = rest.ApiException(status=409)
mock_kube_api.return_value.create_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_create,
self.context, expected_pod)
self.assertEqual('failed', expected_pod.status)
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('magnum.objects.Bay.get_by_name')
def test_pod_delete_with_success(self,
mock_bay_get_by_name,
mock_pod_get_by_uuid,
mock_object_has_stack):
mock_bay = mock.MagicMock()
mock_bay_get_by_name.return_value = mock_bay
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
mock_pod.uuid = 'test-uuid'
mock_pod.bay_uuid = 'test-bay-uuid'
mock_pod_get_by_uuid.return_value = mock_pod
mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
self.kube_handler.pod_delete(self.context,
mock_pod.name,
mock_pod.bay_uuid)
(mock_kube_api.return_value.delete_namespaced_pod
.assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('magnum.objects.Bay.get_by_name')
def test_pod_delete_with_failure(self, mock_bay_get_by_name,
mock_pod_get_by_uuid,
mock_object_has_stack):
mock_bay = mock.MagicMock()
mock_bay_get_by_name.return_value = mock_bay
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
mock_pod.uuid = 'test-uuid'
mock_pod_get_by_uuid.return_value = mock_pod
mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
err = rest.ApiException(status=500)
mock_kube_api.return_value.delete_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_delete,
self.context, mock_pod.name,
mock_pod.bay_uuid)
(mock_kube_api.return_value.delete_namespaced_pod
.assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
@patch('magnum.conductor.utils.object_has_stack')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('magnum.objects.Bay.get_by_name')
def test_pod_delete_succeeds_when_not_found(
self, mock_bay_get_by_name,
mock_pod_get_by_uuid,
mock_object_has_stack):
mock_bay = mock.MagicMock()
mock_bay_get_by_name.return_value = mock_bay
mock_pod = mock.MagicMock()
mock_pod.name = 'test-pod'
mock_pod.uuid = 'test-uuid'
mock_pod_get_by_uuid.return_value = mock_pod
mock_object_has_stack.return_value = True
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
err = rest.ApiException(status=404)
mock_kube_api.return_value.delete_namespaced_pod.side_effect = err
self.kube_handler.pod_delete(self.context, mock_pod.name,
mock_pod.bay_uuid)
(mock_kube_api.return_value.delete_namespaced_pod
.assert_called_once_with(
name=mock_pod.name, body={}, namespace='default'))
@patch('magnum.objects.Bay.get_by_name')
@patch('magnum.conductor.k8s_api.create_k8s_api')
@patch('ast.literal_eval')
@ -567,68 +430,3 @@ class TestK8sConductor(base.TestCase):
.assert_called_once_with(body=expected_service.manifest,
name=service_name,
namespace='default'))
@patch('magnum.objects.Pod.get_by_name')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('magnum.objects.Bay.get_by_name')
@patch('ast.literal_eval')
def test_pod_update_with_success(self, mock_ast,
mock_bay_get_by_name,
mock_pod_get_by_uuid,
mock_pod_get_by_name):
mock_bay = mock.MagicMock()
mock_bay_get_by_name.return_value = mock_bay
expected_pod = mock.MagicMock()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
expected_pod.bay_uuid = 'test-bay-uuid'
expected_pod.manifest = '{"key": "value"}'
mock_ast.return_value = {}
mock_pod_get_by_uuid.return_value = expected_pod
mock_pod_get_by_name.return_value = expected_pod
name_pod = expected_pod.name
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
self.kube_handler.pod_update(self.context, expected_pod.name,
expected_pod.bay_uuid,
expected_pod.manifest)
(mock_kube_api.return_value.replace_namespaced_pod
.assert_called_once_with(
body=expected_pod.manifest, name=name_pod,
namespace='default'))
@patch('magnum.objects.Pod.get_by_name')
@patch('magnum.objects.Pod.get_by_uuid')
@patch('magnum.objects.Bay.get_by_name')
def test_pod_update_with_failure(self, mock_bay_get_by_name,
mock_pod_get_by_uuid,
mock_pod_get_by_name):
mock_bay = mock.MagicMock()
mock_bay_get_by_name.return_value = mock_bay
expected_pod = mock.MagicMock()
expected_pod.uuid = 'test-uuid'
expected_pod.name = 'test-name'
expected_pod.bay_uuid = 'test-bay-uuid'
mock_pod_get_by_uuid.return_value = expected_pod
mock_pod_get_by_name.return_value = expected_pod
expected_pod.manifest = '{"key": "value"}'
name_pod = expected_pod.name
with patch('magnum.conductor.k8s_api.create_k8s_api') as \
mock_kube_api:
err = rest.ApiException(status=404)
mock_kube_api.return_value.replace_namespaced_pod.side_effect = err
self.assertRaises(exception.KubernetesAPIFailed,
self.kube_handler.pod_update,
self.context, expected_pod.name,
expected_pod.bay_uuid,
expected_pod.manifest)
(mock_kube_api.return_value.replace_namespaced_pod
.assert_called_once_with(
body=expected_pod.manifest, name=name_pod,
namespace='default'))

@ -29,7 +29,6 @@ class RPCAPITestCase(base.DbTestCase):
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_pod = dbutils.get_test_pod(driver='fake-driver')
self.fake_rc = dbutils.get_test_rc(driver='fake-driver')
self.fake_service = dbutils.get_test_service(driver='fake-driver')
self.fake_x509keypair = dbutils.get_test_x509keypair(
@ -128,33 +127,6 @@ class RPCAPITestCase(base.DbTestCase):
service_ident=self.fake_service['uuid'],
bay_ident=self.fake_service['bay_uuid'])
def test_pod_create(self):
self._test_rpcapi('pod_create',
'call',
version='1.0',
pod=self.fake_pod)
def test_pod_update(self):
self._test_rpcapi('pod_update',
'call',
version='1.1',
pod_ident=self.fake_pod['uuid'],
bay_ident=self.fake_pod['bay_uuid'],
manifest={})
def test_pod_delete(self):
self._test_rpcapi('pod_delete',
'call',
version='1.0',
pod_ident=self.fake_pod['uuid'],
bay_ident=self.fake_pod['bay_uuid'])
self._test_rpcapi('pod_delete',
'call',
version='1.1',
pod_ident=self.fake_pod['uuid'],
bay_ident=self.fake_pod['bay_uuid'])
def test_rc_create(self):
self._test_rpcapi('rc_create',
'call',

@ -27,13 +27,6 @@ class TestConductorUtils(base.TestCase):
mock_bay_get_by_uuid.assert_called_once_with(expected_context,
expected_bay_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_pod(self,
mock_bay_get_by_uuid):
pod = objects.Pod({})
pod.bay_uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
self._test_retrieve_bay(pod.bay_uuid, mock_bay_get_by_uuid)
@patch('magnum.objects.Bay.get_by_uuid')
def test_retrieve_bay_from_service(self,
mock_bay_get_by_uuid):

@ -23,40 +23,44 @@ from magnum.tests.unit.db import base
class SqlAlchemyCustomTypesTestCase(base.DbTestCase):
def test_JSONEncodedDict_default_value(self):
# Create pod w/o labels
pod1_id = uuidutils.generate_uuid()
self.dbapi.create_pod({'uuid': pod1_id})
pod1 = sa_api.model_query(models.Pod).filter_by(uuid=pod1_id).one()
self.assertEqual({}, pod1.labels)
# Create pod with labels
pod2_id = uuidutils.generate_uuid()
self.dbapi.create_pod({'uuid': pod2_id, 'labels': {'bar': 'foo'}})
pod2 = sa_api.model_query(models.Pod).filter_by(uuid=pod2_id).one()
self.assertEqual('foo', pod2.labels['bar'])
# Create rc w/o labels
rc1_id = uuidutils.generate_uuid()
self.dbapi.create_rc({'uuid': rc1_id})
rc1 = sa_api.model_query(
models.ReplicationController).filter_by(uuid=rc1_id).one()
self.assertEqual({}, rc1.labels)
# Create rc with labels
rc2_id = uuidutils.generate_uuid()
self.dbapi.create_rc({'uuid': rc2_id, 'labels': {'bar': 'foo'}})
rc2 = sa_api.model_query(
models.ReplicationController).filter_by(uuid=rc2_id).one()
self.assertEqual('foo', rc2.labels['bar'])
def test_JSONEncodedDict_type_check(self):
self.assertRaises(db_exc.DBError,
self.dbapi.create_pod,
self.dbapi.create_rc,
{'labels':
['this is not a dict']})
def test_JSONEncodedList_default_value(self):
# Create pod w/o images
pod1_id = uuidutils.generate_uuid()
self.dbapi.create_pod({'uuid': pod1_id})
pod1 = sa_api.model_query(models.Pod).filter_by(uuid=pod1_id).one()
self.assertEqual([], pod1.images)
# Create pod with images
pod2_id = uuidutils.generate_uuid()
self.dbapi.create_pod({'uuid': pod2_id,
'images': ['myimage1', 'myimage2']})
pod2 = sa_api.model_query(models.Pod).filter_by(uuid=pod2_id).one()
self.assertEqual(['myimage1', 'myimage2'], pod2.images)
# Create rc w/o images
rc1_id = uuidutils.generate_uuid()
self.dbapi.create_rc({'uuid': rc1_id})
rc1 = sa_api.model_query(
models.ReplicationController).filter_by(uuid=rc1_id).one()
self.assertEqual([], rc1.images)
# Create rc with images
rc2_id = uuidutils.generate_uuid()
self.dbapi.create_rc({'uuid': rc2_id,
'images': ['myimage1', 'myimage2']})
rc2 = sa_api.model_query(
models.ReplicationController).filter_by(uuid=rc2_id).one()
self.assertEqual(['myimage1', 'myimage2'], rc2.images)
def test_JSONEncodedList_type_check(self):
self.assertRaises(db_exc.DBError,
self.dbapi.create_pod,
self.dbapi.create_rc,
{'images':
{'this is not a list': 'test'}})

@ -192,22 +192,6 @@ class DbBayTestCase(base.DbTestCase):
self.dbapi.destroy_bay,
'12345678-9999-0000-aaaa-123456789012')
def test_destroy_bay_that_has_pods(self):
bay = utils.create_test_bay()
pod = utils.create_test_pod(bay_uuid=bay.uuid)
self.assertEqual(bay.uuid, pod.bay_uuid)
self.dbapi.destroy_bay(bay.id)
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_id, self.context, pod.id)
def test_destroy_bay_that_has_pods_by_uuid(self):
bay = utils.create_test_bay()
pod = utils.create_test_pod(bay_uuid=bay.uuid)
self.assertEqual(bay.uuid, pod.bay_uuid)
self.dbapi.destroy_bay(bay.uuid)
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_id, self.context, pod.id)
def test_destroy_bay_that_has_services(self):
bay = utils.create_test_bay()
service = utils.create_test_service(bay_uuid=bay.uuid)

@ -1,183 +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 Pods 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 as utils
class DbPodTestCase(base.DbTestCase):
def setUp(self):
# This method creates a pod for every test and
# replaces a test for creating a pod.
super(DbPodTestCase, self).setUp()
self.bay = utils.create_test_bay()
self.pod = utils.create_test_pod(bay_uuid=self.bay.uuid)
def test_create_pod_duplicated_uuid(self):
self.assertRaises(exception.PodAlreadyExists,
utils.create_test_pod,
uuid=self.pod.uuid,
bay_uuid=self.bay.uuid)
def test_get_pod_by_id(self):
res = self.dbapi.get_pod_by_id(self.context, self.pod.id)
self.assertEqual(self.pod.id, res.id)
self.assertEqual(self.pod.uuid, res.uuid)
def test_get_pod_by_uuid(self):
res = self.dbapi.get_pod_by_uuid(self.context, self.pod.uuid)
self.assertEqual(self.pod.id, res.id)
self.assertEqual(self.pod.uuid, res.uuid)
def test_get_pod_by_name(self):
res = self.dbapi.get_pod_by_name(self.pod.name)
self.assertEqual(self.pod.id, res.id)
self.assertEqual(self.pod.uuid, res.uuid)
def test_get_pod_by_name_multiple_pods(self):
utils.create_test_pod(bay_uuid=self.bay.uuid,
uuid=uuidutils.generate_uuid())
self.assertRaises(exception.Conflict, self.dbapi.get_pod_by_name,
self.pod.name)
def test_get_pod_by_name_not_found(self):
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_name,
'not_found')
def test_get_pod_that_does_not_exist(self):
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_id, self.context, 999)
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_uuid,
self.context,
uuidutils.generate_uuid())
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_name,
'bad-name')
def test_get_pod_list(self):
uuids = [self.pod.uuid]
for i in range(1, 6):
pod = utils.create_test_pod(uuid=uuidutils.generate_uuid(),
bay_uuid=self.bay.uuid)
uuids.append(six.text_type(pod.uuid))
res = self.dbapi.get_pod_list(self.context)
res_uuids = [r.uuid for r in res]
self.assertEqual(sorted(uuids), sorted(res_uuids))
def test_get_pod_list_sorted(self):
uuids = [self.pod.uuid]
for _ in range(5):
pod = utils.create_test_pod(uuid=uuidutils.generate_uuid())
uuids.append(six.text_type(pod.uuid))
res = self.dbapi.get_pod_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_pod_list,
self.context,
sort_key='foo')
def test_get_pod_list_with_filters(self):
bay1 = utils.get_test_bay(id=11, uuid=uuidutils.generate_uuid())
bay2 = utils.get_test_bay(id=12, uuid=uuidutils.generate_uuid())
self.dbapi.create_bay(bay1)
self.dbapi.create_bay(bay2)
pod1 = utils.create_test_pod(
name='pod-one',
uuid=uuidutils.generate_uuid(),
bay_uuid=bay1['uuid'],
status='status1')
pod2 = utils.create_test_pod(
name='pod-two',
uuid=uuidutils.generate_uuid(),
bay_uuid=bay2['uuid'],
status='status2')
res = self.dbapi.get_pod_list(self.context,
filters={'bay_uuid': bay1['uuid']})
self.assertEqual([pod1.id], [r.id for r in res])
res = self.dbapi.get_pod_list(self.context,
filters={'bay_uuid': bay2['uuid']})
self.assertEqual([pod2.id], [r.id for r in res])
res = self.dbapi.get_pod_list(self.context,
filters={'name': 'pod-one'})
self.assertEqual([pod1.id], [r.id for r in res])
res = self.dbapi.get_pod_list(self.context,
filters={'name': 'bad-pod'})
self.assertEqual([], [r.id for r in res])
res = self.dbapi.get_pod_list(self.context,
filters={'status': 'status1'})
self.assertEqual([pod1.id], [r.id for r in res])
res = self.dbapi.get_pod_list(self.context,
filters={'status': 'status2'})
self.assertEqual([pod2.id], [r.id for r in res])
def test_get_pod_list_bay_not_exist(self):
res = self.dbapi.get_pod_list(self.context,
{'bay_uuid': self.bay.uuid})
self.assertEqual(1, len(res))
res = self.dbapi.get_pod_list(self.context, filters={
'bay_uuid': uuidutils.generate_uuid()})
self.assertEqual(0, len(res))
def test_destroy_pod(self):
self.dbapi.destroy_pod(self.pod.id)
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_id, self.context, self.pod.id)
def test_destroy_pod_by_uuid(self):
self.assertIsNotNone(self.dbapi.get_pod_by_uuid(self.context,
self.pod.uuid))
self.dbapi.destroy_pod(self.pod.uuid)
self.assertRaises(exception.PodNotFound,
self.dbapi.get_pod_by_uuid,
self.context, self.pod.uuid)
def test_destroy_pod_that_does_not_exist(self):
self.assertRaises(exception.PodNotFound,
self.dbapi.destroy_pod,
uuidutils.generate_uuid())
def test_update_pod(self):
old_name = self.pod.name
new_name = 'new-pod'
self.assertNotEqual(old_name, new_name)
res = self.dbapi.update_pod(self.pod.id, {'name': new_name})
self.assertEqual(new_name, res.name)
def test_update_pod_not_found(self):
pod_uuid = uuidutils.generate_uuid()
self.assertRaises(exception.PodNotFound, self.dbapi.update_pod,
pod_uuid, {'status': 'Running'})
def test_update_pod_uuid(self):
self.assertRaises(exception.InvalidParameterValue,
self.dbapi.update_pod, self.pod.id,
{'uuid': ''})

@ -109,39 +109,6 @@ def create_test_bay(**kw):
return dbapi.create_bay(bay)
def get_test_pod(**kw):
return {
'id': kw.get('id', 42),
'uuid': kw.get('uuid', '10a47dd1-4874-4298-91cf-eff046dbdb8d'),
'name': kw.get('name', 'pod1'),
'project_id': kw.get('project_id', 'fake_project'),
'user_id': kw.get('user_id', 'fake_user'),
'desc': kw.get('desc', 'test pod'),
'bay_uuid': kw.get('bay_uuid', '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'),
'images': kw.get('images', ['MyImage']),
'labels': kw.get('labels', {'name': 'foo'}),
'status': kw.get('status', 'Running'),
'host': kw.get('host', '10.0.0.3'),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at'),
}
def create_test_pod(**kw):
"""Create test pod entry in DB and return Pod DB object.
Function to be used to create test Pod objects in the database.
:param kw: kwargs with overriding values for pod's attributes.
:returns: Test Pod DB object.
"""
pod = get_test_pod(**kw)
# Let DB generate ID if it isn't specified explicitly
if 'id' not in kw:
del pod['id']
dbapi = db_api.get_instance()
return dbapi.create_pod(pod)
def get_test_service(**kw):
return {
'id': kw.get('id', 42),

@ -428,7 +428,6 @@ object_data = {
'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2',
'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6',
'MyObj': '1.0-b43567e512438205e32f4e95ca616697',
'Pod': '1.1-39f221ad1dad0eb7f7bee3569d42fa7e',
'ReplicationController': '1.0-a471c2429c212ed91833cfcf0f934eab',
'Service': '1.0-f4a1c5a4618708824a553568c1ada0ea',
'X509KeyPair': '1.1-4aecc268e23e32b8a762d43ba1a4b159',

@ -1,100 +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,