Refactor secret functional tests using models and behaviors
Updated the functional tests to use models and behaviors for secrets. * Adding simple HATEOS-compatible rest client * Fixing model de/serialization * Modifying all tests to conform to the new client * Adding option to load tempest config from local etc * Incorporating review feedback Change-Id: I497b4f7bf10a9f47ca399b569d964762505466c9
This commit is contained in:
parent
c13df4f2be
commit
018404b82e
@ -13,29 +13,21 @@ 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 oslotest.base as oslotest
|
||||
import os
|
||||
|
||||
import oslotest.base as oslotest
|
||||
from tempest import auth
|
||||
from tempest import clients
|
||||
from tempest.common import rest_client
|
||||
from tempest import clients as tempest_clients
|
||||
from tempest import config
|
||||
|
||||
from functionaltests.common import client
|
||||
|
||||
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'
|
||||
# Use local tempest conf if one is available.
|
||||
# This usually means we're running tests outside of devstack
|
||||
if os.path.exists('./etc/dev_tempest.conf'):
|
||||
CONF.set_config_path('./etc/dev_tempest.conf')
|
||||
|
||||
|
||||
class TestCase(oslotest.BaseTestCase):
|
||||
@ -45,9 +37,9 @@ class TestCase(oslotest.BaseTestCase):
|
||||
|
||||
credentials = BarbicanCredentials()
|
||||
|
||||
mgr = clients.Manager(credentials=credentials)
|
||||
mgr = tempest_clients.Manager(credentials=credentials)
|
||||
auth_provider = mgr.get_auth_provider(credentials)
|
||||
self.client = BarbicanClient(auth_provider)
|
||||
self.client = client.BarbicanClient(auth_provider)
|
||||
|
||||
|
||||
class BarbicanCredentials(auth.KeystoneV2Credentials):
|
||||
|
@ -12,21 +12,17 @@
|
||||
# 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)
|
||||
url_without_version = self.client.get_base_url(include_version=False)
|
||||
resp = self.client.get(url_without_version)
|
||||
body = resp.json()
|
||||
|
||||
self.assertEqual(resp.status, 200)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(body.get('v1'), 'current')
|
||||
self.assertGreater(len(body.get('build')), 1)
|
||||
|
0
functionaltests/api/v1/behaviors/__init__.py
Normal file
0
functionaltests/api/v1/behaviors/__init__.py
Normal file
37
functionaltests/api/v1/behaviors/base_behaviors.py
Normal file
37
functionaltests/api/v1/behaviors/base_behaviors.py
Normal file
@ -0,0 +1,37 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
class BaseBehaviors(object):
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.created_entities = []
|
||||
|
||||
def get_id_from_href(self, href):
|
||||
"""Returns the id from reference.
|
||||
|
||||
The id must be the last item in the href.
|
||||
|
||||
:param href: The href containing the id.
|
||||
:returns the id portion of the href
|
||||
"""
|
||||
|
||||
item_id = None
|
||||
if href and len(href) > 0:
|
||||
base, item_id = os.path.split(href)
|
||||
return item_id
|
68
functionaltests/api/v1/behaviors/secret_behaviors.py
Normal file
68
functionaltests/api/v1/behaviors/secret_behaviors.py
Normal file
@ -0,0 +1,68 @@
|
||||
"""
|
||||
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 functionaltests.api.v1.behaviors.base_behaviors import BaseBehaviors
|
||||
from functionaltests.api.v1.models import secret_models
|
||||
|
||||
|
||||
class SecretBehaviors(BaseBehaviors):
|
||||
|
||||
def create_secret(self, model):
|
||||
"""Create a secret from the data in the model.
|
||||
|
||||
:param model: The metadata used to create the secret
|
||||
:return: A tuple containing the response from the create
|
||||
and the href to the newly created secret
|
||||
"""
|
||||
resp = self.client.post('secrets', request_model=model)
|
||||
|
||||
returned_data = resp.json()
|
||||
secret_ref = returned_data.get('secret_ref')
|
||||
if secret_ref:
|
||||
self.created_entities.append(secret_ref)
|
||||
return resp, secret_ref
|
||||
|
||||
def update_secret_payload(self, secret_ref, payload, content_type):
|
||||
"""Updates a secret's payload data
|
||||
|
||||
:param secret_ref: HATEOS ref of the secret to be deleted
|
||||
:return: A request response object
|
||||
"""
|
||||
headers = {'Content-Type': content_type}
|
||||
return self.client.put(secret_ref, data=payload, extra_headers=headers)
|
||||
|
||||
def get_secret_metadata(self, secret_ref):
|
||||
"""Retrieves a secret's metadata
|
||||
|
||||
:param secret_ref: HATEOS ref of the secret to be deleted
|
||||
:return: A request response object
|
||||
"""
|
||||
return self.client.get(
|
||||
secret_ref, response_model_type=secret_models.SecretModel)
|
||||
|
||||
def delete_secret(self, secret_ref):
|
||||
"""Delete a secret.
|
||||
|
||||
:param secret_ref: HATEOS ref of the secret to be deleted
|
||||
:return: A request response object
|
||||
"""
|
||||
resp = self.client.delete(secret_ref)
|
||||
self.created_entities.remove(secret_ref)
|
||||
return resp
|
||||
|
||||
def delete_all_created_secrets(self):
|
||||
"""Delete all of the secrets that we have created."""
|
||||
for secret_ref in self.created_entities:
|
||||
self.delete_secret(secret_ref)
|
0
functionaltests/api/v1/models/__init__.py
Normal file
0
functionaltests/api/v1/models/__init__.py
Normal file
84
functionaltests/api/v1/models/base_models.py
Normal file
84
functionaltests/api/v1/models/base_models.py
Normal file
@ -0,0 +1,84 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
import json
|
||||
|
||||
from tempest.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseModel(object):
|
||||
"""Base class for models.
|
||||
|
||||
To allow simple (de)serialization we will use __dict__ to create
|
||||
"""
|
||||
|
||||
def obj_to_json(self):
|
||||
"""Convert this object to a JSON string.
|
||||
|
||||
:return A string of JSON containing the fields in this object
|
||||
"""
|
||||
|
||||
return json.dumps(self.obj_to_dict())
|
||||
|
||||
def obj_to_dict(self):
|
||||
"""Create a dict of the values for this model object.
|
||||
|
||||
If there are fields that are not set in this object then those
|
||||
will NOT have entries in the returned dict.
|
||||
|
||||
:return A dict representing this model
|
||||
"""
|
||||
|
||||
the_dict = self.__dict__
|
||||
retval = self._remove_empty_fields_from_dict(the_dict)
|
||||
return retval
|
||||
|
||||
def _remove_empty_fields_from_dict(self, dictionary):
|
||||
"""Remove k,v pairs with empty values from a dictionary.
|
||||
|
||||
:param dictionary: a dictionary of stuff
|
||||
:return: the same dictionary where all k,v pairs with empty values
|
||||
have been removed.
|
||||
"""
|
||||
|
||||
# Dumping the keys to a list as we'll be changing the dict size
|
||||
empty_keys = [k for k, v in dictionary.iteritems() if not v]
|
||||
for k in empty_keys:
|
||||
del dictionary[k]
|
||||
return dictionary
|
||||
|
||||
@classmethod
|
||||
def json_to_obj(cls, serialized_str):
|
||||
"""Create a model from a JSON string.
|
||||
|
||||
:param serialized_str: the JSON string
|
||||
:return a secret object
|
||||
"""
|
||||
try:
|
||||
json_dict = json.loads(serialized_str)
|
||||
return cls.dict_to_obj(json_dict)
|
||||
except TypeError as e:
|
||||
LOG.error('Couldn\'t deserialize input: %s', e)
|
||||
|
||||
@classmethod
|
||||
def dict_to_obj(cls, input_dict):
|
||||
"""Create an object from a dict.
|
||||
|
||||
:param input_dict: A dict of fields.
|
||||
:return a model object build from the passed in dict.
|
||||
"""
|
||||
return cls(**input_dict)
|
38
functionaltests/api/v1/models/order_models.py
Normal file
38
functionaltests/api/v1/models/order_models.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""
|
||||
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 functionaltests.api.v1.models.base_models import BaseModel
|
||||
from functionaltests.api.v1.models.secret_models import SecretModel
|
||||
|
||||
|
||||
class OrderModel(BaseModel):
|
||||
|
||||
def __init__(self, status=None, secret_ref=None, updated=None,
|
||||
created=None, secret=None, order_ref=None):
|
||||
super(OrderModel, self).__init__()
|
||||
self.status = status
|
||||
self.secret_ref = secret_ref
|
||||
self.updated = updated
|
||||
self.created = created
|
||||
self.secret = secret
|
||||
self.order_ref = order_ref
|
||||
|
||||
@classmethod
|
||||
def dict_to_obj(cls, input_dict):
|
||||
secret_metadata = input_dict.get('secret')
|
||||
if secret_metadata:
|
||||
input_dict['secret'] = SecretModel.dict_to_obj(secret_metadata)
|
||||
|
||||
return super(OrderModel, cls).dict_to_obj(input_dict)
|
40
functionaltests/api/v1/models/secret_models.py
Normal file
40
functionaltests/api/v1/models/secret_models.py
Normal file
@ -0,0 +1,40 @@
|
||||
"""
|
||||
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 functionaltests.api.v1.models.base_models import BaseModel
|
||||
|
||||
|
||||
class SecretModel(BaseModel):
|
||||
|
||||
def __init__(self, name=None, expiration=None, algorithm=None,
|
||||
secret_ref=None, bit_length=None, mode=None,
|
||||
payload_content_type=None, payload=None, content_types=None,
|
||||
payload_content_encoding=None, status=None, updated=None,
|
||||
created=None):
|
||||
super(SecretModel, self).__init__()
|
||||
|
||||
self.name = name
|
||||
self.expiration = expiration
|
||||
self.algorithm = algorithm
|
||||
self.bit_length = bit_length
|
||||
self.mode = mode
|
||||
self.payload_content_type = payload_content_type
|
||||
self.payload = payload
|
||||
self.content_types = content_types
|
||||
self.payload_content_encoding = payload_content_encoding
|
||||
self.secret_ref = secret_ref
|
||||
self.status = status
|
||||
self.updated = updated
|
||||
self.created = created
|
@ -15,6 +15,8 @@
|
||||
import json
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import secret_behaviors
|
||||
from functionaltests.api.v1.models import secret_models
|
||||
|
||||
create_secret_data = {
|
||||
"name": "AES key",
|
||||
@ -63,45 +65,39 @@ create_container_data = {
|
||||
|
||||
class ConsumersTestCase(base.TestCase):
|
||||
|
||||
def _create_a_secret(self):
|
||||
secret_model = secret_models.SecretModel(**create_secret_data)
|
||||
resp, secret_ref = self.secret_behaviors.create_secret(secret_model)
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
self.assertIsNotNone(secret_ref)
|
||||
|
||||
return secret_ref
|
||||
|
||||
def setUp(self):
|
||||
super(ConsumersTestCase, self).setUp()
|
||||
self.secret_behaviors = secret_behaviors.SecretBehaviors(self.client)
|
||||
|
||||
# Set up two secrets
|
||||
secret_json_data = json.dumps(create_secret_data)
|
||||
resp, body = self.client.post(
|
||||
'/secrets',
|
||||
secret_json_data,
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(resp.status, 201)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
secret_ref_1 = returned_data['secret_ref']
|
||||
self.assertIsNotNone(secret_ref_1)
|
||||
resp, body = self.client.post(
|
||||
'/secrets',
|
||||
secret_json_data,
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(resp.status, 201)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
secret_ref_2 = returned_data['secret_ref']
|
||||
self.assertIsNotNone(secret_ref_2)
|
||||
secret_ref_1 = self._create_a_secret()
|
||||
secret_ref_2 = self._create_a_secret()
|
||||
|
||||
# Create a container with our secrets
|
||||
create_container_data['secret_refs'][0]['secret_ref'] = secret_ref_1
|
||||
create_container_data['secret_refs'][1]['secret_ref'] = secret_ref_2
|
||||
container_json_data = json.dumps(create_container_data)
|
||||
resp, body = self.client.post(
|
||||
'/containers', container_json_data,
|
||||
headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 201)
|
||||
resp = self.client.post(
|
||||
'containers', container_json_data)
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
container_ref = returned_data['container_ref']
|
||||
self.assertIsNotNone(container_ref)
|
||||
self.container_id = container_ref.split('/')[-1]
|
||||
|
||||
def tearDown(self):
|
||||
self.secret_behaviors.delete_all_created_secrets()
|
||||
super(ConsumersTestCase, self).tearDown()
|
||||
|
||||
def test_create_consumer(self):
|
||||
"""Covers consumer creation.
|
||||
|
||||
@ -109,12 +105,12 @@ class ConsumersTestCase(base.TestCase):
|
||||
single POST.
|
||||
"""
|
||||
json_data = json.dumps(create_consumer_data)
|
||||
resp, body = self.client.post(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.post(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertIn(create_consumer_data, consumer_data)
|
||||
@ -128,23 +124,23 @@ class ConsumersTestCase(base.TestCase):
|
||||
json_data = json.dumps(create_consumer_data_for_delete)
|
||||
|
||||
# Register the consumer once
|
||||
resp, body = self.client.post(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.post(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertIn(create_consumer_data_for_delete, consumer_data)
|
||||
|
||||
# Delete the consumer
|
||||
resp, body = self.client.delete(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
body=json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.delete(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertNotIn(create_consumer_data_for_delete, consumer_data)
|
||||
@ -154,34 +150,34 @@ class ConsumersTestCase(base.TestCase):
|
||||
json_data = json.dumps(create_consumer_data_for_recreate)
|
||||
|
||||
# Register the consumer once
|
||||
resp, body = self.client.post(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.post(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertIn(create_consumer_data_for_recreate, consumer_data)
|
||||
|
||||
# Delete the consumer
|
||||
resp, body = self.client.delete(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
body=json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.delete(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertNotIn(create_consumer_data_for_recreate, consumer_data)
|
||||
|
||||
# Register the consumer again
|
||||
resp, body = self.client.post(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.post(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertIn(create_consumer_data_for_recreate, consumer_data)
|
||||
@ -191,23 +187,23 @@ class ConsumersTestCase(base.TestCase):
|
||||
json_data = json.dumps(create_consumer_data_for_idempotency)
|
||||
|
||||
# Register the consumer once
|
||||
resp, body = self.client.post(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.post(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertIn(create_consumer_data_for_idempotency, consumer_data)
|
||||
|
||||
# Register the consumer again, without deleting it first
|
||||
resp, body = self.client.post(
|
||||
'/containers/{0}/consumers'.format(self.container_id),
|
||||
json_data, headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 200)
|
||||
resp = self.client.post(
|
||||
'containers/{0}/consumers'.format(self.container_id),
|
||||
json_data)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
returned_data = resp.json()
|
||||
consumer_data = returned_data['consumers']
|
||||
self.assertIsNotNone(consumer_data)
|
||||
self.assertIn(create_consumer_data_for_idempotency, consumer_data)
|
||||
|
@ -15,9 +15,9 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from tempest import exceptions
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import secret_behaviors
|
||||
from functionaltests.api.v1.models import secret_models
|
||||
|
||||
create_secret_data = {
|
||||
"name": "AES key",
|
||||
@ -47,64 +47,47 @@ create_container_data = {
|
||||
class ContainersTestCase(base.TestCase):
|
||||
|
||||
def _create_a_secret(self):
|
||||
secret_json_data = json.dumps(create_secret_data)
|
||||
resp, body = self.client.post(
|
||||
'/secrets',
|
||||
secret_json_data,
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(resp.status, 201)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
secret_ref = returned_data.get('secret_ref')
|
||||
secret_model = secret_models.SecretModel(**create_secret_data)
|
||||
resp, secret_ref = self.secret_behaviors.create_secret(secret_model)
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
self.assertIsNotNone(secret_ref)
|
||||
|
||||
return secret_ref
|
||||
|
||||
def _get_a_secret(self, secret_id):
|
||||
resp, body = self.client.get(
|
||||
'/secrets/{0}'.format(secret_id),
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(resp.status, 200)
|
||||
return json.loads(body)
|
||||
resp = self.client.get('secrets/{0}'.format(secret_id))
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
return resp.json()
|
||||
|
||||
def _create_a_container(self):
|
||||
json_data = json.dumps(create_container_data)
|
||||
resp, body = self.client.post(
|
||||
'/containers', json_data,
|
||||
headers={'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 201)
|
||||
resp = self.client.post('containers/', json_data)
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
container_ref = returned_data['container_ref']
|
||||
body = resp.json()
|
||||
container_ref = body.get('container_ref')
|
||||
self.assertIsNotNone(container_ref)
|
||||
return container_ref
|
||||
|
||||
def _get_a_container(self, container_id):
|
||||
resp, body = self.client.get(
|
||||
'/containers/{0}'.format(container_id),
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(resp.status, 200)
|
||||
self.assertIsNotNone(body)
|
||||
return json.loads(body)
|
||||
resp = self.client.get('containers/{0}'.format(container_id))
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertIsNotNone(resp.content)
|
||||
return resp.json()
|
||||
|
||||
def _get_container_list(self):
|
||||
resp, body = self.client.get(
|
||||
'/containers/',
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
self.assertEqual(resp.status, 200)
|
||||
return json.loads(body)
|
||||
resp = self.client.get('containers/')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
return resp.json()
|
||||
|
||||
def _delete_a_container(self, container_id):
|
||||
resp, body = self.client.delete(
|
||||
'/containers/{0}'.format(container_id), headers={}
|
||||
)
|
||||
self.assertEqual(resp.status, 204)
|
||||
resp = self.client.delete('containers/{0}'.format(container_id))
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
|
||||
def setUp(self):
|
||||
super(ContainersTestCase, self).setUp()
|
||||
self.secret_behaviors = secret_behaviors.SecretBehaviors(self.client)
|
||||
|
||||
# Set up two secrets
|
||||
secret_ref_1 = self._create_a_secret()
|
||||
secret_ref_2 = self._create_a_secret()
|
||||
@ -115,6 +98,10 @@ class ContainersTestCase(base.TestCase):
|
||||
self.secret_id_1 = secret_ref_1.split('/')[-1]
|
||||
self.secret_id_2 = secret_ref_2.split('/')[-1]
|
||||
|
||||
def tearDown(self):
|
||||
self.secret_behaviors.delete_all_created_secrets()
|
||||
super(ContainersTestCase, self).tearDown()
|
||||
|
||||
def test_create_container(self):
|
||||
"""Covers container creation.
|
||||
|
||||
@ -138,11 +125,8 @@ class ContainersTestCase(base.TestCase):
|
||||
self._delete_a_container(container_id)
|
||||
|
||||
# Verify container is gone
|
||||
self.assertRaises(
|
||||
exceptions.NotFound, self.client.get,
|
||||
'/containers/{0}'.format(container_id),
|
||||
headers={'content-type': 'application/json'}
|
||||
)
|
||||
resp = self.client.get(container_ref)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
# Verify the Secrets from the container still exist
|
||||
self._get_a_secret(self.secret_id_1)
|
||||
|
@ -12,9 +12,8 @@
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import json
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.models import order_models
|
||||
|
||||
|
||||
create_order_data = {
|
||||
@ -36,12 +35,9 @@ class OrdersTestCase(base.TestCase):
|
||||
All of the data needed to create the order is provided in a
|
||||
single POST.
|
||||
"""
|
||||
json_data = json.dumps(create_order_data)
|
||||
resp, body = self.client.post(
|
||||
'/orders', json_data, headers={
|
||||
'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 202)
|
||||
model = order_models.OrderModel(**create_order_data)
|
||||
resp = self.client.post('orders/', request_model=model)
|
||||
self.assertEqual(resp.status_code, 202)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
order_ref = returned_data['order_ref']
|
||||
self.assertIsNotNone(order_ref)
|
||||
body = resp.json()
|
||||
self.assertIsNotNone(body.get('order_ref'))
|
||||
|
@ -12,10 +12,11 @@
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import json
|
||||
import os
|
||||
import testtools
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import secret_behaviors
|
||||
from functionaltests.api.v1.models import secret_models
|
||||
|
||||
|
||||
one_phase_create_data = {
|
||||
@ -35,61 +36,71 @@ two_phase_create_data = {
|
||||
"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 setUp(self):
|
||||
super(SecretsTestCase, self).setUp()
|
||||
self.behaviors = secret_behaviors.SecretBehaviors(self.client)
|
||||
self.one_phase_secret_model = secret_models.SecretModel(
|
||||
**one_phase_create_data)
|
||||
self.two_phase_secret_model = secret_models.SecretModel(
|
||||
**two_phase_create_data)
|
||||
|
||||
def tearDown(self):
|
||||
self.behaviors.delete_all_created_secrets()
|
||||
super(SecretsTestCase, self).tearDown()
|
||||
|
||||
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.
|
||||
Verify that a secret gets created with the correct http
|
||||
response code and a secret reference.
|
||||
"""
|
||||
json_data = json.dumps(one_phase_create_data)
|
||||
resp, body = self.client.post(
|
||||
'/secrets', json_data, headers={
|
||||
'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 201)
|
||||
resp, secret_ref = self.behaviors.create_secret(
|
||||
self.one_phase_secret_model)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
secret_ref = returned_data['secret_ref']
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
self.assertIsNotNone(secret_ref)
|
||||
|
||||
def test_get_created_secret_metadata(self):
|
||||
"""Covers retrieval of a created secret's metadata."""
|
||||
|
||||
create_model = self.one_phase_secret_model
|
||||
resp, secret_ref = self.behaviors.create_secret(create_model)
|
||||
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
self.assertIsNotNone(secret_ref)
|
||||
|
||||
get_resp = self.behaviors.get_secret_metadata(secret_ref)
|
||||
get_model = get_resp.model
|
||||
|
||||
self.assertEqual(get_resp.status_code, 200)
|
||||
self.assertEqual(get_model.name, create_model.name)
|
||||
self.assertEqual(get_model.algorithm, create_model.algorithm)
|
||||
self.assertEqual(get_model.bit_length, create_model.bit_length)
|
||||
self.assertEqual(get_model.mode, create_model.mode)
|
||||
|
||||
@testtools.skip('Skip until we can fix two-step creation')
|
||||
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.
|
||||
Verify that a secret gets created with the correct http
|
||||
response code and a secret reference.
|
||||
"""
|
||||
# phase 1 - POST secret without payload
|
||||
json_data = json.dumps(two_phase_create_data)
|
||||
resp, body = self.client.post(
|
||||
'/secrets', json_data, headers={
|
||||
'content-type': 'application/json'})
|
||||
self.assertEqual(resp.status, 201)
|
||||
|
||||
returned_data = json.loads(body)
|
||||
secret_ref = returned_data['secret_ref']
|
||||
# Phase 1
|
||||
resp, secret_ref = self.behaviors.create_secret(
|
||||
self.two_phase_secret_model)
|
||||
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
self.assertIsNotNone(secret_ref)
|
||||
|
||||
secret_id = os.path.split(secret_ref)[1]
|
||||
self.assertIsNotNone(secret_id)
|
||||
# Phase 2
|
||||
update_resp = self.behaviors.update_secret_payload(
|
||||
secret_ref, 'YmFt', 'text/plain')
|
||||
|
||||
# phase 2 - provide (PUT) the secret payload
|
||||
json_data = json.dumps(two_phase_payload_data)
|
||||
resp, body = self.client.post(
|
||||
'/secrets/{0}'.format(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)
|
||||
self.assertEqual(update_resp.status_code, 204)
|
||||
|
0
functionaltests/common/__init__.py
Normal file
0
functionaltests/common/__init__.py
Normal file
139
functionaltests/common/client.py
Normal file
139
functionaltests/common/client.py
Normal file
@ -0,0 +1,139 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
import os
|
||||
|
||||
import requests
|
||||
from requests import auth
|
||||
from tempest.common.utils import misc as misc_utils
|
||||
from tempest.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BarbicanClientAuth(auth.AuthBase):
|
||||
"""Implementation of Requests Auth for Barbican http calls."""
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
credentials = auth_provider.fill_credentials()
|
||||
|
||||
self.username = credentials.username
|
||||
self.password = credentials.password
|
||||
self.project_id = credentials.tenant_id
|
||||
self.project_name = credentials.tenant_name
|
||||
self.token = auth_provider.get_token()
|
||||
|
||||
def __call__(self, r):
|
||||
r.headers['X-Project-Id'] = self.project_id
|
||||
r.headers['X-Auth-Token'] = self.token
|
||||
return r
|
||||
|
||||
|
||||
class BarbicanClient(object):
|
||||
|
||||
def __init__(self, auth_provider, api_version='v1'):
|
||||
self._auth = BarbicanClientAuth(auth_provider)
|
||||
self._auth_provider = auth_provider
|
||||
self.timeout = 10
|
||||
self.api_version = api_version
|
||||
self.default_headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
def stringify_request(self, request_kwargs, response):
|
||||
format_kwargs = {
|
||||
'code': response.status_code,
|
||||
'method': request_kwargs.get('method'),
|
||||
'url': request_kwargs.get('url'),
|
||||
'headers': response.request.headers,
|
||||
'body': request_kwargs.get('data'),
|
||||
'response_body': response.content
|
||||
}
|
||||
return ('{code} {method} {url}\n'
|
||||
'Request Headers: {headers}\n'
|
||||
'Request Body: {body}\n'
|
||||
'Response: {response_body}\n').format(**format_kwargs)
|
||||
|
||||
def log_request(self, request_kwargs, response):
|
||||
test_name = misc_utils.find_test_caller()
|
||||
str_request = self.stringify_request(request_kwargs, response)
|
||||
LOG.info('Request (%s)\n %s', test_name, str_request)
|
||||
|
||||
def attempt_to_deserialize(self, response, model_type):
|
||||
if model_type and hasattr(model_type, 'json_to_obj'):
|
||||
return model_type.json_to_obj(response.content)
|
||||
|
||||
def attempt_to_serialize(self, model):
|
||||
if model and hasattr(model, 'obj_to_json'):
|
||||
return model.obj_to_json()
|
||||
|
||||
def get_base_url(self, include_version=True):
|
||||
filters = {
|
||||
'service': 'keystore',
|
||||
'api_version': self.api_version if include_version else ''
|
||||
}
|
||||
|
||||
return self._auth_provider.base_url(filters)
|
||||
|
||||
def request(self, method, url, data=None, extra_headers=None,
|
||||
use_auth=True, response_model_type=None, request_model=None):
|
||||
"""Prepares and sends http request through Requests."""
|
||||
if 'http' not in url:
|
||||
url = os.path.join(self.get_base_url(), url)
|
||||
|
||||
# Duplicate Base headers and add extras (if needed)
|
||||
headers = {}
|
||||
headers.update(self.default_headers)
|
||||
if extra_headers:
|
||||
headers.update(extra_headers)
|
||||
|
||||
# Attempt to serialize model if required
|
||||
if request_model:
|
||||
data = self.attempt_to_serialize(request_model)
|
||||
|
||||
# Prepare call arguments
|
||||
call_kwargs = {
|
||||
'method': method,
|
||||
'url': url,
|
||||
'headers': headers,
|
||||
'data': data,
|
||||
'timeout': self.timeout
|
||||
}
|
||||
if use_auth:
|
||||
call_kwargs['auth'] = self._auth
|
||||
|
||||
response = requests.request(**call_kwargs)
|
||||
|
||||
# Attempt to deserialize the response
|
||||
response.model = self.attempt_to_deserialize(response,
|
||||
response_model_type)
|
||||
self.log_request(call_kwargs, response)
|
||||
return response
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""Proxies the request method specifically for http GET methods."""
|
||||
return self.request('GET', *args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
"""Proxies the request method specifically for http POST methods."""
|
||||
return self.request('POST', *args, **kwargs)
|
||||
|
||||
def put(self, *args, **kwargs):
|
||||
"""Proxies the request method specifically for http PUT methods."""
|
||||
return self.request('PUT', *args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""Proxies the request method specifically for http DELETE methods."""
|
||||
return self.request('DELETE', *args, **kwargs)
|
@ -6,6 +6,7 @@ oslotest>=1.1.0.0a1
|
||||
testrepository>=0.0.18
|
||||
testtools>=0.9.34
|
||||
fixtures>=0.3.14
|
||||
requests>=1.2.1,!=2.4.0
|
||||
|
||||
# Documentation build requirements
|
||||
sphinx>=1.1.2,<1.2
|
||||
|
Loading…
Reference in New Issue
Block a user