From 31aa2fdfb255c3fd3a6d678a98718f4b4b13789d Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Sun, 6 Jan 2019 19:25:32 +0000 Subject: [PATCH] Support private registry - objects layer Change-Id: I1ac40bfc67d49c60a026cc8bdf006aeb16eafa87 Partial-Bug: #1702830 --- zun/objects/__init__.py | 3 + zun/objects/registry.py | 144 ++++++++++++++++++++++++ zun/tests/unit/objects/test_objects.py | 1 + zun/tests/unit/objects/test_registry.py | 122 ++++++++++++++++++++ 4 files changed, 270 insertions(+) create mode 100644 zun/objects/registry.py create mode 100644 zun/tests/unit/objects/test_registry.py diff --git a/zun/objects/__init__.py b/zun/objects/__init__.py index ac0b4f7a0..81a8cc9ec 100644 --- a/zun/objects/__init__.py +++ b/zun/objects/__init__.py @@ -23,6 +23,7 @@ from zun.objects import pci_device from zun.objects import pci_device_pool from zun.objects import quota from zun.objects import quota_class +from zun.objects import registry from zun.objects import resource_class from zun.objects import resource_provider from zun.objects import volume @@ -51,6 +52,7 @@ ContainerPCIRequests = container_pci_requests.ContainerPCIRequests ContainerAction = container_action.ContainerAction ContainerActionEvent = container_action.ContainerActionEvent ExecInstance = exec_instance.ExecInstance +Registry = registry.Registry __all__ = ( 'Container', @@ -73,4 +75,5 @@ __all__ = ( 'ContainerAction', 'ContainerActionEvent', 'ExecInstance', + 'Registry', ) diff --git a/zun/objects/registry.py b/zun/objects/registry.py new file mode 100644 index 000000000..c46129599 --- /dev/null +++ b/zun/objects/registry.py @@ -0,0 +1,144 @@ +# 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_log import log as logging +from oslo_versionedobjects import fields + +from zun.db import api as dbapi +from zun.objects import base + + +LOG = logging.getLogger(__name__) + + +@base.ZunObjectRegistry.register +class Registry(base.ZunPersistentObject, base.ZunObject): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'id': fields.IntegerField(), + 'uuid': fields.UUIDField(nullable=True), + 'name': fields.StringField(nullable=True), + 'project_id': fields.StringField(nullable=True), + 'user_id': fields.StringField(nullable=True), + 'domain': fields.StringField(nullable=True), + 'username': fields.StringField(nullable=True), + 'password': fields.StringField(nullable=True), + } + + @staticmethod + def _from_db_object(registry, db_registry): + """Converts a database entity to a formal object.""" + for field in registry.fields: + setattr(registry, field, db_registry[field]) + + registry.obj_reset_changes() + return registry + + @staticmethod + def _from_db_object_list(db_objects, cls, context): + """Converts a list of database entities to a list of formal objects.""" + return [Registry._from_db_object(cls(context), obj) + for obj in db_objects] + + @base.remotable_classmethod + def get_by_uuid(cls, context, uuid): + """Find a registry based on uuid and return a :class:`Registry` object. + + :param uuid: the uuid of a registry. + :param context: Security context + :returns: a :class:`Registry` object. + """ + db_registry = dbapi.get_registry_by_uuid(context, uuid) + registry = Registry._from_db_object(cls(context), db_registry) + return registry + + @base.remotable_classmethod + def get_by_name(cls, context, name): + """Find a registry based on name and return a Registry object. + + :param name: the logical name of a registry. + :param context: Security context + :returns: a :class:`Registry` object. + """ + db_registry = dbapi.get_registry_by_name(context, name) + registry = Registry._from_db_object(cls(context), db_registry) + return registry + + @base.remotable_classmethod + def list(cls, context, limit=None, marker=None, + sort_key=None, sort_dir=None, filters=None): + """Return a list of Registry 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 registries. + :returns: a list of :class:`Registry` object. + + """ + db_registries = dbapi.list_registries( + context, limit=limit, marker=marker, sort_key=sort_key, + sort_dir=sort_dir, filters=filters) + return Registry._from_db_object_list(db_registries, cls, context) + + @base.remotable + def create(self, context): + """Create a Registry 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.: Registry(context) + + """ + values = self.obj_get_changes() + db_registry = dbapi.create_registry(context, values) + self._from_db_object(self, db_registry) + + @base.remotable + def destroy(self, context=None): + """Delete the Registry 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.: Registry(context) + """ + dbapi.destroy_registry(context, self.uuid) + self.obj_reset_changes() + + @base.remotable + def save(self, context=None): + """Save updates to this Registry. + + 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.: Registry(context) + """ + updates = self.obj_get_changes() + dbapi.update_registry(context, self.uuid, updates) + + self.obj_reset_changes() diff --git a/zun/tests/unit/objects/test_objects.py b/zun/tests/unit/objects/test_objects.py index ec893bf71..46d7d1e91 100644 --- a/zun/tests/unit/objects/test_objects.py +++ b/zun/tests/unit/objects/test_objects.py @@ -368,6 +368,7 @@ object_data = { 'ContainerActionEvent': '1.0-2974d0a6f5d4821fd4e223a88c10181a', 'Network': '1.1-26e8d37a54e5fc905ede657744a221d9', 'ExecInstance': '1.0-59464e7b96db847c0abb1e96d3cec30a', + 'Registry': '1.0-21ed56234497120755c60deba7c9e1dc', } diff --git a/zun/tests/unit/objects/test_registry.py b/zun/tests/unit/objects/test_registry.py new file mode 100644 index 000000000..a364c4e44 --- /dev/null +++ b/zun/tests/unit/objects/test_registry.py @@ -0,0 +1,122 @@ +# 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 testtools.matchers import HasLength + +from zun import objects +from zun.tests.unit.db import base +from zun.tests.unit.db import utils + + +class TestRegistryObject(base.DbTestCase): + + def setUp(self): + super(TestRegistryObject, self).setUp() + self.fake_cpuset = utils.get_cpuset_dict() + self.fake_registry = utils.get_test_registry( + cpuset=self.fake_cpuset, cpu_policy='dedicated') + + def test_get_by_uuid(self): + uuid = self.fake_registry['uuid'] + with mock.patch.object(self.dbapi, 'get_registry_by_uuid', + autospec=True) as mock_get_registry: + mock_get_registry.return_value = self.fake_registry + registry = objects.Registry.get_by_uuid(self.context, uuid) + mock_get_registry.assert_called_once_with(self.context, uuid) + self.assertEqual(self.context, registry._context) + + def test_get_by_name(self): + name = self.fake_registry['name'] + with mock.patch.object(self.dbapi, 'get_registry_by_name', + autospec=True) as mock_get_registry: + mock_get_registry.return_value = self.fake_registry + registry = objects.Registry.get_by_name(self.context, name) + mock_get_registry.assert_called_once_with(self.context, name) + self.assertEqual(self.context, registry._context) + + def test_list(self): + with mock.patch.object(self.dbapi, 'list_registries', + autospec=True) as mock_get_list: + mock_get_list.return_value = [self.fake_registry] + registries = objects.Registry.list(self.context) + self.assertEqual(1, mock_get_list.call_count) + self.assertThat(registries, HasLength(1)) + self.assertIsInstance(registries[0], objects.Registry) + self.assertEqual(self.context, registries[0]._context) + + def test_list_with_filters(self): + with mock.patch.object(self.dbapi, 'list_registries', + autospec=True) as mock_get_list: + mock_get_list.return_value = [self.fake_registry] + filt = {'username': 'fake_username'} + registries = objects.Registry.list(self.context, + filters=filt) + self.assertEqual(1, mock_get_list.call_count) + self.assertThat(registries, HasLength(1)) + self.assertIsInstance(registries[0], objects.Registry) + self.assertEqual(self.context, registries[0]._context) + mock_get_list.assert_called_once_with(self.context, + filters=filt, + limit=None, marker=None, + sort_key=None, sort_dir=None) + + def test_create(self): + with mock.patch.object(self.dbapi, 'create_registry', + autospec=True) as mock_create_registry: + mock_create_registry.return_value = self.fake_registry + registry_dict = dict(self.fake_registry) + registry = objects.Registry(self.context, **registry_dict) + registry.create(self.context) + mock_create_registry.assert_called_once_with(self.context, + self.fake_registry) + self.assertEqual(self.context, registry._context) + + def test_destroy(self): + uuid = self.fake_registry['uuid'] + with mock.patch.object(self.dbapi, 'get_registry_by_uuid', + autospec=True) as mock_get_registry: + mock_get_registry.return_value = self.fake_registry + with mock.patch.object(self.dbapi, 'destroy_registry', + autospec=True) as mock_destroy_registry: + registry = objects.Registry.get_by_uuid(self.context, uuid) + registry.destroy() + mock_get_registry.assert_called_once_with(self.context, uuid) + mock_destroy_registry.assert_called_once_with(None, uuid) + self.assertEqual(self.context, registry._context) + + def test_save(self): + uuid = self.fake_registry['uuid'] + with mock.patch.object(self.dbapi, 'get_registry_by_uuid', + autospec=True) as mock_get_registry: + mock_get_registry.return_value = self.fake_registry + with mock.patch.object(self.dbapi, 'update_registry', + autospec=True) as mock_update_registry: + registry = objects.Registry.get_by_uuid(self.context, uuid) + registry.domain = 'testdomain.io' + registry.username = 'testuesrname' + registry.password = 'testpassword' + registry.name = 'testname' + registry.save() + + mock_get_registry.assert_called_once_with(self.context, uuid) + mock_update_registry.assert_called_once_with( + None, uuid, + {'domain': 'testdomain.io', + 'username': 'testuesrname', + 'password': 'testpassword', + 'name': 'testname'}) + self.assertEqual(self.context, registry._context)