Add capsule method list
Part of blueprint introduce-compose Change-Id: I574a0cb9630c4f789c620e2d498373cd927976ba Signed-off-by: Kevin Zhao <kevin.zhao@arm.com>
This commit is contained in:
parent
24cd5b021d
commit
d5983c4d28
@ -5,8 +5,8 @@ kind: capsule
|
||||
metadata:
|
||||
name: capsule-example
|
||||
labels:
|
||||
- app: web
|
||||
- nihao: baibai
|
||||
app: web
|
||||
nihao: baibai
|
||||
restart_policy: always
|
||||
spec:
|
||||
containers:
|
||||
|
@ -13,7 +13,9 @@
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import strutils
|
||||
import pecan
|
||||
import six
|
||||
|
||||
from zun.api.controllers import base
|
||||
from zun.api.controllers.experimental import collection
|
||||
@ -76,6 +78,70 @@ class CapsuleController(base.Controller):
|
||||
|
||||
}
|
||||
|
||||
@pecan.expose('json')
|
||||
@exception.wrap_pecan_controller_exception
|
||||
def get_all(self, **kwargs):
|
||||
'''Retrieve a list of capsules.'''
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, "capsule:get_all",
|
||||
action="capsule:get_all")
|
||||
return self._get_capsules_collection(**kwargs)
|
||||
|
||||
def _get_capsules_collection(self, **kwargs):
|
||||
context = pecan.request.context
|
||||
all_tenants = kwargs.get('all_tenants')
|
||||
if all_tenants:
|
||||
try:
|
||||
all_tenants = strutils.bool_from_string(all_tenants, True)
|
||||
except ValueError as err:
|
||||
raise exception.InvalidInput(six.text_type(err))
|
||||
else:
|
||||
# If no value, it's considered to disable all_tenants
|
||||
all_tenants = False
|
||||
if all_tenants:
|
||||
context.all_tenants = True
|
||||
compute_api = pecan.request.compute_api
|
||||
limit = api_utils.validate_limit(kwargs.get('limit'))
|
||||
sort_dir = api_utils.validate_sort_dir(kwargs.get('sort_dir', 'asc'))
|
||||
sort_key = kwargs.get('sort_key', 'id')
|
||||
resource_url = kwargs.get('resource_url')
|
||||
expand = kwargs.get('expand')
|
||||
filters = None
|
||||
marker_obj = None
|
||||
marker = kwargs.get('marker')
|
||||
if marker:
|
||||
marker_obj = objects.Capsule.get_by_uuid(context,
|
||||
marker)
|
||||
capsules = objects.Capsule.list(context,
|
||||
limit,
|
||||
marker_obj,
|
||||
sort_key,
|
||||
sort_dir,
|
||||
filters=filters)
|
||||
|
||||
# Sync status for container inside capsule
|
||||
for i, capsule in enumerate(capsules):
|
||||
try:
|
||||
containers_list = capsule.containers_uuids
|
||||
if containers_list is not None:
|
||||
# Capsule is depending on infra container status
|
||||
uuid = containers_list[0]
|
||||
container = utils.get_container(uuid)
|
||||
container = compute_api.container_show(context, container)
|
||||
capsule.status = container.status
|
||||
capsule.save(context)
|
||||
except Exception as e:
|
||||
LOG.exception(("Error while list capsule %(uuid)s: "
|
||||
"%(e)s."),
|
||||
{'uuid': capsule.uuid, 'e': e})
|
||||
capsules[i].status = consts.UNKNOWN
|
||||
|
||||
return CapsuleCollection.convert_with_links(capsules, limit,
|
||||
url=resource_url,
|
||||
expand=expand,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
@pecan.expose('json')
|
||||
@api_utils.enforce_content_types(['application/json'])
|
||||
@exception.wrap_pecan_controller_exception
|
||||
|
@ -0,0 +1,37 @@
|
||||
# Copyright 2017 Arm Limited.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""change the properties of meta_labels
|
||||
|
||||
Revision ID: fc27c7415d9c
|
||||
Revises: bcd6410d645e
|
||||
Create Date: 2017-09-07 10:56:07.489031
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fc27c7415d9c'
|
||||
down_revision = 'bcd6410d645e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
|
||||
import zun
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.alter_column('capsule', 'meta_labels',
|
||||
type_=zun.db.sqlalchemy.models.JSONEncodedDict()
|
||||
)
|
@ -340,7 +340,7 @@ class Capsule(Base):
|
||||
|
||||
status = Column(String(20))
|
||||
status_reason = Column(Text, nullable=True)
|
||||
meta_labels = Column(JSONEncodedList)
|
||||
meta_labels = Column(JSONEncodedDict)
|
||||
meta_name = Column(String(255))
|
||||
spec = Column(JSONEncodedDict)
|
||||
containers_uuids = Column(JSONEncodedList)
|
||||
|
@ -23,7 +23,8 @@ from zun.objects import fields as z_fields
|
||||
class Capsule(base.ZunPersistentObject, base.ZunObject):
|
||||
# Version 1.0: Initial version
|
||||
# Version 1.1: Add host to capsule
|
||||
VERSION = '1.1'
|
||||
# Version 1.2: Change the properties of meta_labels
|
||||
VERSION = '1.2'
|
||||
|
||||
fields = {
|
||||
'capsule_version': fields.StringField(nullable=True),
|
||||
@ -48,7 +49,7 @@ class Capsule(base.ZunPersistentObject, base.ZunObject):
|
||||
|
||||
'spec': z_fields.JsonField(nullable=True),
|
||||
'meta_name': fields.StringField(nullable=True),
|
||||
'meta_labels': z_fields.JsonField(nullable=True),
|
||||
'meta_labels': fields.DictOfStringsField(nullable=True),
|
||||
'containers': fields.ListOfObjectsField('Container', nullable=True),
|
||||
'containers_uuids': fields.ListOfStringsField(nullable=True),
|
||||
'host': fields.StringField(nullable=True),
|
||||
|
@ -12,9 +12,11 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
from mock import patch
|
||||
from oslo_utils import uuidutils
|
||||
from webtest.app import AppError
|
||||
from zun.common import consts
|
||||
from zun.common import exception
|
||||
from zun import objects
|
||||
from zun.tests.unit.api import base as api_base
|
||||
@ -34,20 +36,19 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
'"image": "test", "drivers": "cinder", "volumeType": '
|
||||
'"type1", "driverOptions": "options", '
|
||||
'"size": "5GB"}]}, '
|
||||
'"metadata": {"labels": [{"foo0": "bar0"}, '
|
||||
'{"foo1": "bar1"}], '
|
||||
'"metadata": {"labels": {"foo0": "bar0", "foo1": "bar1"}, '
|
||||
'"name": "capsule-example"}}}')
|
||||
response = self.post('/capsules/',
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
return_value = response.json
|
||||
expected_meta_name = "capsule-example"
|
||||
expected_meta_label = [{"foo0": "bar0"}, {"foo1": "bar1"}]
|
||||
expected_meta_labels = {"foo0": "bar0", "foo1": "bar1"}
|
||||
expected_container_num = 2
|
||||
self.assertEqual(len(return_value["containers_uuids"]),
|
||||
expected_container_num)
|
||||
self.assertEqual(return_value["meta_name"], expected_meta_name)
|
||||
self.assertEqual(return_value["meta_labels"], expected_meta_label)
|
||||
self.assertEqual(return_value["meta_labels"], expected_meta_labels)
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertTrue(mock_capsule_create.called)
|
||||
|
||||
@ -63,22 +64,21 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
'"image": "test1", "labels": {"app1": "web1"}, '
|
||||
'"image_driver": "docker", "resources": '
|
||||
'{"allocation": {"cpu": 1, "memory": 1024}}}]}, '
|
||||
'"metadata": {"labels": [{"foo0": "bar0"}, '
|
||||
'{"foo1": "bar1"}], '
|
||||
'"metadata": {"labels": {"foo0": "bar0", "foo1": "bar1"}, '
|
||||
'"name": "capsule-example"}}}')
|
||||
response = self.post('/capsules/',
|
||||
params=params,
|
||||
content_type='application/json')
|
||||
return_value = response.json
|
||||
expected_meta_name = "capsule-example"
|
||||
expected_meta_label = [{"foo0": "bar0"}, {"foo1": "bar1"}]
|
||||
expected_meta_labels = {"foo0": "bar0", "foo1": "bar1"}
|
||||
expected_container_num = 3
|
||||
self.assertEqual(len(return_value["containers_uuids"]),
|
||||
expected_container_num)
|
||||
self.assertEqual(return_value["meta_name"],
|
||||
expected_meta_name)
|
||||
self.assertEqual(return_value["meta_labels"],
|
||||
expected_meta_label)
|
||||
expected_meta_labels)
|
||||
self.assertEqual(202, response.status_int)
|
||||
self.assertTrue(mock_capsule_create.called)
|
||||
|
||||
@ -92,7 +92,7 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
'"image": "test1", "labels": {"app0": "web0"}, '
|
||||
'"image_driver": "docker", "resources": '
|
||||
'{"allocation": {"cpu": 1, "memory": 1024}}}]}, '
|
||||
'"metadata": {"labels": [{"foo0": "bar0"}], '
|
||||
'"metadata": {"labels": {"foo0": "bar0"}, '
|
||||
'"name": "capsule-example"}}}')
|
||||
mock_check_template.side_effect = exception.InvalidCapsuleTemplate(
|
||||
"kind fields need to be set as capsule or Capsule")
|
||||
@ -106,7 +106,7 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
mock_capsule_create):
|
||||
params = ('{"spec": {"kind": "capsule",'
|
||||
'"spec": {container:[]}, '
|
||||
'"metadata": {"labels": [{"foo0": "bar0"}], '
|
||||
'"metadata": {"labels": {"foo0": "bar0"}, '
|
||||
'"name": "capsule-example"}}}')
|
||||
mock_check_template.side_effect = exception.InvalidCapsuleTemplate(
|
||||
"Capsule need to have one container at least")
|
||||
@ -120,7 +120,7 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
mock_capsule_create):
|
||||
params = ('{"spec": {"kind": "capsule",'
|
||||
'"spec": {}, '
|
||||
'"metadata": {"labels": [{"foo0": "bar0"}], '
|
||||
'"metadata": {"labels": {"foo0": "bar0"}, '
|
||||
'"name": "capsule-example"}}}')
|
||||
mock_check_template.side_effect = exception.InvalidCapsuleTemplate(
|
||||
"Capsule need to have one container at least")
|
||||
@ -135,7 +135,7 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
params = ('{"spec": {"kind": "capsule",'
|
||||
'"spec": {container:[{"environment": '
|
||||
'{"ROOT_PASSWORD": "foo1"}]}, '
|
||||
'"metadata": {"labels": [{"foo0": "bar0"}], '
|
||||
'"metadata": {"labels": {"foo0": "bar0"}, '
|
||||
'"name": "capsule-example"}}}')
|
||||
mock_check_template.side_effect = exception.InvalidCapsuleTemplate(
|
||||
"Container image is needed")
|
||||
@ -278,3 +278,129 @@ class TestCapsuleController(api_base.FunctionalTest):
|
||||
self.assertEqual(204, response.status_int)
|
||||
context = mock_capsule_save.call_args[0][0]
|
||||
self.assertIs(False, context.all_tenants)
|
||||
|
||||
@patch('zun.compute.api.API.container_show')
|
||||
@patch('zun.objects.Capsule.list')
|
||||
@patch('zun.objects.Container.get_by_uuid')
|
||||
def test_get_all_capsules(self, mock_container_get_by_uuid,
|
||||
mock_capsule_list,
|
||||
mock_container_show):
|
||||
test_container = utils.get_test_container()
|
||||
test_container_obj = objects.Container(self.context,
|
||||
**test_container)
|
||||
mock_container_get_by_uuid.return_value = test_container_obj
|
||||
|
||||
test_capsule = utils.get_test_capsule()
|
||||
test_capsule_obj = objects.Capsule(self.context, **test_capsule)
|
||||
mock_capsule_list.return_value = [test_capsule_obj]
|
||||
mock_container_show.return_value = test_container_obj
|
||||
|
||||
response = self.app.get('/capsules/')
|
||||
|
||||
mock_capsule_list.assert_called_once_with(mock.ANY,
|
||||
1000, None, 'id', 'asc',
|
||||
filters=None)
|
||||
context = mock_capsule_list.call_args[0][0]
|
||||
self.assertIs(False, context.all_tenants)
|
||||
self.assertEqual(200, response.status_int)
|
||||
actual_capsules = response.json['capsules']
|
||||
self.assertEqual(1, len(actual_capsules))
|
||||
self.assertEqual(test_capsule['uuid'],
|
||||
actual_capsules[0].get('uuid'))
|
||||
|
||||
@patch('zun.compute.api.API.container_show')
|
||||
@patch('zun.objects.Capsule.list')
|
||||
@patch('zun.objects.Container.get_by_uuid')
|
||||
def test_get_all_capsules_all_tenants(self,
|
||||
mock_container_get_by_uuid,
|
||||
mock_capsule_list,
|
||||
mock_container_show):
|
||||
test_container = utils.get_test_container()
|
||||
test_container_obj = objects.Container(self.context,
|
||||
**test_container)
|
||||
mock_container_get_by_uuid.return_value = test_container_obj
|
||||
|
||||
test_capsule = utils.get_test_capsule()
|
||||
test_capsule_obj = objects.Capsule(self.context, **test_capsule)
|
||||
mock_capsule_list.return_value = [test_capsule_obj]
|
||||
mock_container_show.return_value = test_container_obj
|
||||
|
||||
response = self.app.get('/capsules/?all_tenants=1')
|
||||
|
||||
mock_capsule_list.assert_called_once_with(mock.ANY,
|
||||
1000, None, 'id', 'asc',
|
||||
filters=None)
|
||||
context = mock_capsule_list.call_args[0][0]
|
||||
self.assertIs(True, context.all_tenants)
|
||||
self.assertEqual(200, response.status_int)
|
||||
actual_capsules = response.json['capsules']
|
||||
self.assertEqual(1, len(actual_capsules))
|
||||
self.assertEqual(test_capsule['uuid'],
|
||||
actual_capsules[0].get('uuid'))
|
||||
|
||||
@patch('zun.compute.api.API.container_show')
|
||||
@patch('zun.objects.Capsule.list')
|
||||
@patch('zun.objects.Container.get_by_uuid')
|
||||
def test_get_all_capsules_with_exception(self,
|
||||
mock_container_get_by_uuid,
|
||||
mock_capsule_list,
|
||||
mock_container_show):
|
||||
test_container = utils.get_test_container()
|
||||
test_container_obj = objects.Container(self.context,
|
||||
**test_container)
|
||||
mock_container_get_by_uuid.return_value = test_container_obj
|
||||
|
||||
test_capsule = utils.get_test_capsule()
|
||||
test_capsule_obj = objects.Capsule(self.context, **test_capsule)
|
||||
mock_capsule_list.return_value = [test_capsule_obj]
|
||||
mock_container_show.side_effect = Exception
|
||||
|
||||
response = self.app.get('/capsules/')
|
||||
|
||||
mock_capsule_list.assert_called_once_with(mock.ANY,
|
||||
1000, None, 'id', 'asc',
|
||||
filters=None)
|
||||
context = mock_capsule_list.call_args[0][0]
|
||||
self.assertIs(False, context.all_tenants)
|
||||
self.assertEqual(200, response.status_int)
|
||||
actual_capsules = response.json['capsules']
|
||||
self.assertEqual(1, len(actual_capsules))
|
||||
self.assertEqual(test_capsule['uuid'],
|
||||
actual_capsules[0].get('uuid'))
|
||||
self.assertEqual(consts.UNKNOWN,
|
||||
actual_capsules[0].get('status'))
|
||||
|
||||
@patch('zun.compute.api.API.container_show')
|
||||
@patch('zun.objects.Capsule.list')
|
||||
@patch('zun.objects.Capsule.save')
|
||||
@patch('zun.objects.Container.get_by_uuid')
|
||||
def test_get_all_capsules_with_pagination_marker(
|
||||
self,
|
||||
mock_container_get_by_uuid,
|
||||
mock_capsule_save,
|
||||
mock_capsule_list,
|
||||
mock_container_show):
|
||||
test_container = utils.get_test_container()
|
||||
test_container_obj = objects.Container(self.context,
|
||||
**test_container)
|
||||
mock_container_get_by_uuid.return_value = test_container_obj
|
||||
capsule_list = []
|
||||
for id_ in range(4):
|
||||
test_capsule = utils.create_test_capsule(
|
||||
id=id_, uuid=uuidutils.generate_uuid(),
|
||||
name='capsule' + str(id_), context=self.context)
|
||||
capsule_list.append(objects.Capsule(self.context,
|
||||
**test_capsule))
|
||||
mock_capsule_list.return_value = capsule_list[-1:]
|
||||
mock_container_show.return_value = capsule_list[-1]
|
||||
mock_capsule_save.return_value = True
|
||||
|
||||
response = self.app.get('/capsules/?limit=3&marker=%s'
|
||||
% capsule_list[2].uuid)
|
||||
|
||||
self.assertEqual(200, response.status_int)
|
||||
actual_capsules = response.json['capsules']
|
||||
|
||||
self.assertEqual(1, len(actual_capsules))
|
||||
self.assertEqual(actual_capsules[-1].get('uuid'),
|
||||
actual_capsules[0].get('uuid'))
|
||||
|
@ -379,3 +379,18 @@ def get_test_capsule(**kwargs):
|
||||
'6219e0fb-2935-4db2-a3c7-86a2ac3ac84e']),
|
||||
'host': kwargs.get('host', 'localhost'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_capsule(**kwargs):
|
||||
"""Create test capsule entry in DB and return Capsule DB object.
|
||||
|
||||
Function to be used to create test Capsule objects in the database.
|
||||
:param kwargs: kwargs with overriding values for capsule's attributes.
|
||||
:returns: Test Capsule DB object.
|
||||
"""
|
||||
capsule = get_test_capsule(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if CONF.db_type == 'sql' and 'id' not in kwargs:
|
||||
del capsule['id']
|
||||
dbapi = db_api._get_dbdriver_instance()
|
||||
return dbapi.create_capsule(kwargs['context'], capsule)
|
||||
|
@ -354,7 +354,7 @@ object_data = {
|
||||
'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24',
|
||||
'ZunService': '1.1-b1549134bfd5271daec417ca8cabc77e',
|
||||
'ComputeNode': '1.7-9b700eb146e9978d84e9ccc5849d90e2',
|
||||
'Capsule': '1.1-bbf2165650900e7d79d1c12a12464b59',
|
||||
'Capsule': '1.2-1f8b3716ef272c9d9cb55390f6a7cdc3',
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user