From efa6a39f6e24f6c9a8fd8e8b3a978e7b9314288c Mon Sep 17 00:00:00 2001 From: Steve Heyman Date: Mon, 21 Jul 2014 14:36:49 -0500 Subject: [PATCH] Add tempest tests to run in devstack gate Added tests for secret create (single and two-phase), order create, and versioning to run in the devstack gate. These should both provide a base for additional tempest-based tests and also ensure that future CRs won't regress basic Barbican functionality. Added skip to version tests until blueprint fix-version-api has been implemented. Change-Id: Ib1d6bb3a2251c21b9d7d647687d0ac30ad52e4c9 --- .testr.conf | 2 +- functionaltests/api/__init__.py | 0 functionaltests/api/base.py | 60 +++++++++++++++++ functionaltests/api/test_versions.py | 32 +++++++++ functionaltests/api/v1/__init__.py | 0 functionaltests/api/v1/test_orders.py | 46 +++++++++++++ functionaltests/api/v1/test_secrets.py | 93 ++++++++++++++++++++++++++ 7 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 functionaltests/api/__init__.py create mode 100644 functionaltests/api/base.py create mode 100644 functionaltests/api/test_versions.py create mode 100644 functionaltests/api/v1/__init__.py create mode 100644 functionaltests/api/v1/test_orders.py create mode 100644 functionaltests/api/v1/test_secrets.py diff --git a/.testr.conf b/.testr.conf index 0d13a20e0..f45a334eb 100644 --- a/.testr.conf +++ b/.testr.conf @@ -1,5 +1,5 @@ [DEFAULT] -test_command=${PYTHON:-python} -m subunit.run discover -t . $LISTOPT $IDOPTION +test_command=${PYTHON:-python} -m subunit.run discover -s ./barbican -t . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list diff --git a/functionaltests/api/__init__.py b/functionaltests/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/functionaltests/api/base.py b/functionaltests/api/base.py new file mode 100644 index 000000000..9fd748b1e --- /dev/null +++ b/functionaltests/api/base.py @@ -0,0 +1,60 @@ +""" +Copyright 2014 Rackspace + +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 auth +from tempest import clients +from tempest.common import rest_client +from tempest import config +import testtools + +CONF = config.CONF + + +class BarbicanClient(rest_client.RestClient): + + def __init__(self, auth_provider): + super(BarbicanClient, self).__init__(auth_provider) + + # get the project id (aka tenant id) which we need in the API tests + # to build the correct URI. + credentials = auth_provider.fill_credentials() + self.project_id = credentials.tenant_id + + self.service = 'keystore' + self.endpoint_url = 'publicURL' + + +class TestCase(testtools.TestCase): + + def setUp(self): + super(TestCase, self).setUp() + + credentials = BarbicanCredentials() + + mgr = clients.Manager(credentials=credentials) + auth_provider = mgr.get_auth_provider(credentials) + self.client = BarbicanClient(auth_provider) + + +class BarbicanCredentials(auth.KeystoneV2Credentials): + + def __init__(self): + credentials = dict( + username=CONF.identity.admin_username, + password=CONF.identity.admin_password, + tenant_name=CONF.identity.admin_tenant_name + ) + + super(BarbicanCredentials, self).__init__(**credentials) diff --git a/functionaltests/api/test_versions.py b/functionaltests/api/test_versions.py new file mode 100644 index 000000000..da3582c5f --- /dev/null +++ b/functionaltests/api/test_versions.py @@ -0,0 +1,32 @@ +# Copyright (c) 2014 Rackspace, 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 +import testtools + +from functionaltests.api import base + + +class VersionDiscoveryTestCase(base.TestCase): + + @testtools.skipIf(True, 'Skip until blueprint fix-version-api is complete') + def test_get_root_discovers_v1(self): + """Covers retrieving version data for Barbican. + """ + resp, body = self.client.get(' ') + body = json.loads(body) + + self.assertEqual(resp.status, 200) + self.assertEqual(body.get('v1'), 'current') + self.assertGreater(len(body.get('build')), 1) diff --git a/functionaltests/api/v1/__init__.py b/functionaltests/api/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/functionaltests/api/v1/test_orders.py b/functionaltests/api/v1/test_orders.py new file mode 100644 index 000000000..bae6b979f --- /dev/null +++ b/functionaltests/api/v1/test_orders.py @@ -0,0 +1,46 @@ +# Copyright (c) 2014 Rackspace, 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 functionaltests.api import base + + +create_order_data = { + "secret": { + "name": "secretname", + "algorithm": "AES", + "bit_length": 256, + "mode": "cbc", + "payload_content_type": "application/octet-stream", + } +} + + +class OrdersTestCase(base.TestCase): + + def test_create_order(self): + """Covers order creation. All of the data needed to + create the order is provided in a single POST. + """ + json_data = json.dumps(create_order_data) + project_id = self.client.project_id + resp, body = self.client.post( + '{0}/orders'.format(project_id), json_data, headers={ + 'content-type': 'application/json'}) + self.assertEqual(resp.status, 202) + + returned_data = json.loads(body) + order_ref = returned_data['order_ref'] + self.assertIsNotNone(order_ref) diff --git a/functionaltests/api/v1/test_secrets.py b/functionaltests/api/v1/test_secrets.py new file mode 100644 index 000000000..1573bbcdd --- /dev/null +++ b/functionaltests/api/v1/test_secrets.py @@ -0,0 +1,93 @@ +# Copyright (c) 2014 Rackspace, 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 +import os + +from functionaltests.api import base + + +one_phase_create_data = { + "name": "AES key", + "expiration": "2018-02-28T19:14:44.180394", + "algorithm": "aes", + "bit_length": 256, + "mode": "cbc", + "payload": "gF6+lLoF3ohA9aPRpt+6bQ==", + "payload_content_type": "application/octet-stream", + "payload_content_encoding": "base64", +} + +two_phase_create_data = { + "name": "AES key", + "expiration": "2018-02-28T19:14:44.180394", + "algorithm": "aes", + "bit_length": 256, + "mode": "cbc", +} + +two_phase_payload_data = { + "payload": "gF6+lLoF3ohA9aPRpt+6bQ==", + "payload_content_type": "application/octet-stream", + "payload_content_encoding": "base64", +} + + +class SecretsTestCase(base.TestCase): + + def test_create_secret_single_phase(self): + """Covers single phase secret creation. All of the data needed to + create the secret, including payload, is provided in a single POST. + """ + json_data = json.dumps(one_phase_create_data) + project_id = self.client.project_id + resp, body = self.client.post( + '{0}/secrets'.format(project_id), json_data, headers={ + 'content-type': 'application/json'}) + self.assertEqual(resp.status, 201) + + returned_data = json.loads(body) + secret_ref = returned_data['secret_ref'] + self.assertIsNotNone(secret_ref) + + def test_create_secret_two_phase(self): + """Covers two phase secret creation. The first call, a POST, provides + the metadata about the secret - everything except the payload. A + subsequent call (PUT) provides the payload. + """ + # phase 1 - POST secret without payload + json_data = json.dumps(two_phase_create_data) + project_id = self.client.project_id + resp, body = self.client.post( + '{0}/secrets'.format(project_id), json_data, headers={ + 'content-type': 'application/json'}) + self.assertEqual(resp.status, 201) + + returned_data = json.loads(body) + secret_ref = returned_data['secret_ref'] + self.assertIsNotNone(secret_ref) + + secret_id = os.path.split(secret_ref)[1] + self.assertIsNotNone(secret_id) + + # phase 2 - provide (PUT) the secret payload + json_data = json.dumps(two_phase_payload_data) + resp, body = self.client.post( + '{0}/secrets/{1}'.format(project_id, secret_id), json_data, + headers={'content-type': 'application/json'}) + self.assertEqual(resp.status, 200) + + returned_data = json.loads(body) + secret_ref = returned_data['secret_ref'] + self.assertIsNotNone(secret_ref)