diff --git a/.zuul.yaml b/.zuul.yaml index d1c2785..91d86bb 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -48,6 +48,9 @@ api_v1: False ephemeral_storage_encryption: enabled: True + key_manager: + min_microversion: '1.0' + max_microversion: latest tox_envlist: all tempest_test_regex: barbican tempest_plugins: @@ -72,16 +75,25 @@ name: barbican-tempest-plugin-simple-crypto-yoga parent: barbican-tempest-plugin-simple-crypto override-checkout: stable/yoga + vars: µversion_v1_0 + devstack_local_conf: + test-config: + $TEMPEST_CONFIG: + key_manager: + min_microversion: '1.0' + max_microversion: '1.0' - job: name: barbican-tempest-plugin-simple-crypto-xena parent: barbican-tempest-plugin-simple-crypto override-checkout: stable/xena + vars: *microversion_v1_0 - job: name: barbican-tempest-plugin-simple-crypto-wallaby parent: barbican-tempest-plugin-simple-crypto override-checkout: stable/wallaby + vars: *microversion_v1_0 - job: name: barbican-tempest-plugin-simple-crypto-ipv6-only diff --git a/barbican_tempest_plugin/config.py b/barbican_tempest_plugin/config.py index 7b79cb5..da78d15 100644 --- a/barbican_tempest_plugin/config.py +++ b/barbican_tempest_plugin/config.py @@ -20,6 +20,32 @@ service_option = cfg.BoolOpt("barbican", help="Whether or not barbican is expected to be " "available") +key_manager_group = cfg.OptGroup( + name='key_manager', + title='Key Manager (Barbican) service options' +) + +KeyManagerOpts = [ + cfg.StrOpt('min_microversion', + default=None, + help="Lower version of the test target microversion range. " + "The format is 'X.Y', where 'X' and 'Y' are int values. " + "Tempest selects tests based on the range between " + "min_microversion and max_microversion. " + "If both values are not specified, Tempest avoids tests " + "which require a microversion. Valid values are string " + "with format 'X.Y' or string 'latest'"), + cfg.StrOpt('max_microversion', + default=None, + help="Upper version of the test target microversion range. " + "The format is 'X.Y', where 'X' and 'Y' are int values. " + "Tempest selects tests based on the range between " + "min_microversion and max_microversion. " + "If both values are not specified, Tempest avoids tests " + "which require a microversion. Valid values are string " + "with format 'X.Y' or string 'latest'") +] + barbican_tempest_group = cfg.OptGroup( name='barbican_tempest', title='Key Manager (Barbican) service options' diff --git a/barbican_tempest_plugin/plugin.py b/barbican_tempest_plugin/plugin.py index b829a05..4649e85 100644 --- a/barbican_tempest_plugin/plugin.py +++ b/barbican_tempest_plugin/plugin.py @@ -33,6 +33,10 @@ class BarbicanTempestPlugin(plugins.TempestPlugin): conf.register_opt(project_config.service_option, group='service_available') + conf.register_group(project_config.key_manager_group) + conf.register_opts(project_config.KeyManagerOpts, + project_config.key_manager_group) + conf.register_group(project_config.barbican_tempest_group) conf.register_opts(project_config.BarbicanGroupOpts, project_config.barbican_tempest_group) @@ -69,4 +73,13 @@ class BarbicanTempestPlugin(plugins.TempestPlugin): 'TransportKeyClient' ], } - return [v1_params] + v1_1_params = { + 'name': 'secret_v1_1', + 'service_version': 'secret.v1_1', + 'module_path': 'barbican_tempest_plugin.services.key_manager.v1_1', + 'client_names': [ + 'SecretConsumerClient', + 'VersionClient' + ], + } + return [v1_params, v1_1_params] diff --git a/barbican_tempest_plugin/services/key_manager/json/base.py b/barbican_tempest_plugin/services/key_manager/json/base.py index 97323a3..dedc0fd 100644 --- a/barbican_tempest_plugin/services/key_manager/json/base.py +++ b/barbican_tempest_plugin/services/key_manager/json/base.py @@ -13,14 +13,24 @@ from tempest.lib.common import rest_client _DEFAULT_SERVICE_TYPE = 'key-manager' +_MICROVERSION_HEADER = 'OpenStack-API-Version' class BarbicanTempestClient(rest_client.RestClient): + _microversion = None + def __init__(self, *args, **kwargs): kwargs['service'] = _DEFAULT_SERVICE_TYPE super().__init__(*args, **kwargs) + def get_headers(self, accept_type=None, send_type=None): + headers = super().get_headers(accept_type, send_type) + if self._microversion: + headers[_MICROVERSION_HEADER] = \ + f'{_DEFAULT_SERVICE_TYPE} {self._microversion}' + return headers + @classmethod def ref_to_uuid(cls, href): return href.split('/')[-1] diff --git a/barbican_tempest_plugin/services/key_manager/v1_1/__init__.py b/barbican_tempest_plugin/services/key_manager/v1_1/__init__.py new file mode 100644 index 0000000..1ee6b52 --- /dev/null +++ b/barbican_tempest_plugin/services/key_manager/v1_1/__init__.py @@ -0,0 +1,23 @@ +# Copyright (c) 2022 Red Hat Inc. +# +# 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 barbican_tempest_plugin.services.key_manager.v1_1.secret_consumer_client \ + import SecretConsumerClient # noqa: F401 +from barbican_tempest_plugin.services.key_manager.v1_1.version_client \ + import VersionClient # noqa: F401 + +__all__ = [ + 'SecretConsumerClient' + 'VersionClient' +] diff --git a/barbican_tempest_plugin/services/key_manager/v1_1/secret_consumer_client.py b/barbican_tempest_plugin/services/key_manager/v1_1/secret_consumer_client.py new file mode 100644 index 0000000..84d7c25 --- /dev/null +++ b/barbican_tempest_plugin/services/key_manager/v1_1/secret_consumer_client.py @@ -0,0 +1,53 @@ +# Copyright (c) 2022 Red Hat Inc. +# +# 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 json + +from urllib import parse as urllib + +from tempest import config + +from barbican_tempest_plugin.services.key_manager.json import base + + +CONF = config.CONF + + +class SecretConsumerClient(base.BarbicanTempestClient): + + _microversion = '1.1' + + def list_consumers_in_secret(self, secret_id, **kwargs): + uri = "/v1/secrets/%s/consumers" % secret_id + if kwargs: + uri += "?%s" % urllib.urlencode(kwargs) + + response, body = self.get(uri) + self.expected_success(200, response.status) + return json.loads(body.decode("utf-8")) + + def add_consumer_to_secret(self, secret_id, **kwargs): + uri = "/v1/secrets/%s/consumers" % secret_id + + response, body = self.post(uri, json.dumps(kwargs)) + self.expected_success(200, response.status) + return json.loads(body.decode("utf-8")) + + def delete_consumer_from_secret(self, secret_id, **kwargs): + uri = "/v1/secrets/%s/consumers" % secret_id + + response, body = self.delete(uri, body=json.dumps(kwargs)) + self.expected_success(200, response.status) + return json.loads(body.decode("utf-8")) diff --git a/barbican_tempest_plugin/services/key_manager/v1_1/version_client.py b/barbican_tempest_plugin/services/key_manager/v1_1/version_client.py new file mode 100644 index 0000000..248d4f0 --- /dev/null +++ b/barbican_tempest_plugin/services/key_manager/v1_1/version_client.py @@ -0,0 +1,23 @@ +# Copyright (c) 2022 Red Hat Inc. +# +# 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 tempest import config + +from barbican_tempest_plugin.services.key_manager.json import base + + +CONF = config.CONF + + +class VersionClient(base.BarbicanTempestClient): + pass diff --git a/barbican_tempest_plugin/tests/api/base.py b/barbican_tempest_plugin/tests/api/base.py index 2599480..566f363 100644 --- a/barbican_tempest_plugin/tests/api/base.py +++ b/barbican_tempest_plugin/tests/api/base.py @@ -16,6 +16,7 @@ import functools from tempest import config +from tempest.lib.common import api_version_utils from tempest import test from barbican_tempest_plugin import clients @@ -56,7 +57,8 @@ def creates(resource): return decorator -class BaseKeyManagerTest(test.BaseTestCase): +class BaseKeyManagerTest(test.BaseTestCase, + api_version_utils.BaseMicroversionTest): """Base class for all api tests.""" # Why do I have to be an admin to create secrets? No idea... @@ -64,6 +66,15 @@ class BaseKeyManagerTest(test.BaseTestCase): client_manager = clients.Clients created_objects = {} + @classmethod + def skip_checks(cls): + super().skip_checks() + api_version_utils.check_skip_with_microversion( + cls.min_microversion, + cls.max_microversion, + CONF.key_manager.min_microversion, + CONF.key_manager.max_microversion) + @classmethod def setup_clients(cls): super(BaseKeyManagerTest, cls).setup_clients() @@ -76,9 +87,11 @@ class BaseKeyManagerTest(test.BaseTestCase): ) cls.order_client = os.secret_v1.OrderClient(service='key-manager') cls.secret_client = os.secret_v1.SecretClient(service='key-manager') + cls.secret_consumer_client = os.secret_v1_1.SecretConsumerClient() cls.secret_metadata_client = os.secret_v1.SecretMetadataClient( service='key-manager' ) + cls.version_client = os.secret_v1_1.VersionClient() os = getattr(cls, 'os_roles_%s' % cls.credentials[1][0]) cls.quota_client = os.secret_v1.QuotaClient(service='key-manager') diff --git a/barbican_tempest_plugin/tests/api/test_secret_consumers.py b/barbican_tempest_plugin/tests/api/test_secret_consumers.py new file mode 100644 index 0000000..8288cfd --- /dev/null +++ b/barbican_tempest_plugin/tests/api/test_secret_consumers.py @@ -0,0 +1,101 @@ +# Copyright (c) 2022 Red Hat Inc. +# +# 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 tempest.lib import decorators + +from barbican_tempest_plugin.tests.api import base + + +class SecretConsumersTest(base.BaseKeyManagerTest): + """Secret Consumers API tests.""" + + min_microversion = '1.1' + + @decorators.idempotent_id('07a47f8b-e454-4dd0-afb6-bfa12677cd8e') + def test_add_delete_consumers_in_secret(self): + # Create a secret to test against + sec = self.create_secret(name='secret_1') + secret_id = self.secret_consumer_client.ref_to_uuid(sec['secret_ref']) + + # Confirm that the secret has no consumers + body = self.secret_consumer_client.list_consumers_in_secret(secret_id) + self.assertEqual(0, body.get('total')) + self.assertEmpty(body.get('consumers')) + + # Add some consumers to the secret + body = self.secret_consumer_client.add_consumer_to_secret( + secret_id, + service="service1", + resource_id="resource_id1", + resource_type="resource_type1" + ) + self.assertEqual( + secret_id, + self.secret_consumer_client.ref_to_uuid(body.get('secret_ref')) + ) + self.assertEqual(1, len(body.get('consumers'))) + body = self.secret_consumer_client.add_consumer_to_secret( + secret_id, + service="service2", + resource_id="resource_id2", + resource_type="resource_type2" + ) + self.assertEqual( + secret_id, + self.secret_consumer_client.ref_to_uuid(body.get('secret_ref')) + ) + self.assertEqual(2, len(body.get('consumers'))) + + # Confirm that the consumers are in the secret + body = self.secret_consumer_client.list_consumers_in_secret(secret_id) + self.assertEqual(2, body.get('total')) + self.assertEqual(2, len(body.get('consumers'))) + for consumer in body.get('consumers'): + self.assertIn(consumer.get('service'), ("service1", "service2")) + self.assertIn(consumer.get('resource_id'), + ("resource_id1", "resource_id2")) + self.assertIn(consumer.get('resource_type'), + ("resource_type1", "resource_type2")) + + # Remove the consumers from the secret + body = self.secret_consumer_client.delete_consumer_from_secret( + secret_id, + service="service1", + resource_id="resource_id1", + resource_type="resource_type1" + ) + self.assertEqual( + secret_id, + self.secret_consumer_client.ref_to_uuid(body.get('secret_ref')) + ) + self.assertEqual(1, len(body.get('consumers'))) + body = self.secret_consumer_client.delete_consumer_from_secret( + secret_id, + service="service2", + resource_id="resource_id2", + resource_type="resource_type2" + ) + self.assertEqual( + secret_id, + self.secret_consumer_client.ref_to_uuid(body.get('secret_ref')) + ) + self.assertEqual(0, len(body.get('consumers'))) + + # Confirm that the secret has no consumers + body = self.secret_consumer_client.list_consumers_in_secret(secret_id) + self.assertEqual(0, body.get('total')) + self.assertEqual(0, len(body.get('consumers'))) + + # Clean up the secret + self.delete_secret(secret_id) diff --git a/barbican_tempest_plugin/tests/scenario/barbican_manager.py b/barbican_tempest_plugin/tests/scenario/barbican_manager.py index 6ebf6fd..b6f24a9 100644 --- a/barbican_tempest_plugin/tests/scenario/barbican_manager.py +++ b/barbican_tempest_plugin/tests/scenario/barbican_manager.py @@ -106,9 +106,11 @@ class BarbicanScenarioTest(mgr.ScenarioTest): ) cls.order_client = os.secret_v1.OrderClient(service='key-manager') cls.secret_client = os.secret_v1.SecretClient(service='key-manager') + cls.secret_consumer_client = os.secret_v1_1.SecretConsumerClient() cls.secret_metadata_client = os.secret_v1.SecretMetadataClient( service='key-manager' ) + cls.secret_consumer_client = os.secret_v1_1.VersionClient() if CONF.compute_feature_enabled.attach_encrypted_volume: cls.admin_encryption_types_client =\