Merge "Add Functional Tests for Quota Enforcement"
This commit is contained in:
commit
71f892335a
@ -142,7 +142,7 @@ class ContainerConsumersController(controllers.ACLMixin):
|
||||
except exception.NotFound:
|
||||
controllers.containers.container_not_found()
|
||||
|
||||
self.quota_enforcer.enforce(external_project_id)
|
||||
self.quota_enforcer.enforce(project)
|
||||
|
||||
new_consumer = models.ContainerConsumerMetadatum(self.container_id,
|
||||
project.id,
|
||||
|
@ -181,7 +181,7 @@ class ContainersController(controllers.ACLMixin):
|
||||
if ctxt: # in authenticated pipleline case, always use auth token user
|
||||
data['creator_id'] = ctxt.user
|
||||
|
||||
self.quota_enforcer.enforce(external_project_id)
|
||||
self.quota_enforcer.enforce(project)
|
||||
|
||||
LOG.debug('Start on_post...%s', data)
|
||||
|
||||
|
@ -216,7 +216,7 @@ class OrdersController(controllers.ACLMixin):
|
||||
external_project_id,
|
||||
container_ref, pecan.request)
|
||||
|
||||
self.quota_enforcer.enforce(external_project_id)
|
||||
self.quota_enforcer.enforce(project)
|
||||
|
||||
new_order = models.Order()
|
||||
new_order.meta = body.get('meta')
|
||||
|
@ -311,7 +311,7 @@ class SecretsController(controllers.ACLMixin):
|
||||
data = api.load_body(pecan.request, validator=self.validator)
|
||||
project = res.get_or_create_project(external_project_id)
|
||||
|
||||
self.quota_enforcer.enforce(external_project_id)
|
||||
self.quota_enforcer.enforce(project)
|
||||
|
||||
transport_key_needed = data.get('transport_key_needed',
|
||||
'false').lower() == 'true'
|
||||
|
@ -200,14 +200,14 @@ class QuotaEnforcer(object):
|
||||
self.resource_type = resource_type
|
||||
self.resource_repo = resource_repo
|
||||
|
||||
def enforce(self, external_project_id):
|
||||
def enforce(self, project):
|
||||
"""Enforce the quota limit for the resource
|
||||
|
||||
:param external_project_id: ID of project requesting to create
|
||||
:param project: the project object corresponding to the sender
|
||||
:raises QuotaReached: exception raised if quota forbids request
|
||||
:return: None
|
||||
"""
|
||||
quotas = self.quota_driver.get_effective_quotas(external_project_id)
|
||||
quotas = self.quota_driver.get_effective_quotas(project.external_id)
|
||||
quota = quotas[self.resource_type]
|
||||
|
||||
reached = False
|
||||
@ -217,12 +217,12 @@ class QuotaEnforcer(object):
|
||||
elif self.quota_driver.is_disabled_value(quota):
|
||||
reached = True
|
||||
else:
|
||||
count = self.resource_repo.get_count(external_project_id)
|
||||
count = self.resource_repo.get_count(project.id)
|
||||
if count >= quota:
|
||||
reached = True
|
||||
|
||||
if reached:
|
||||
raise exception.QuotaReached(
|
||||
external_project_id=external_project_id,
|
||||
external_project_id=project.external_id,
|
||||
resource_type=self.resource_type,
|
||||
quota=quota)
|
||||
|
@ -778,6 +778,12 @@ class WhenCreatingConsumersUsingConsumersResource(FunctionalTest):
|
||||
self.project_repo.get.return_value = self.project
|
||||
self.setup_project_repository_mock(self.project_repo)
|
||||
|
||||
# Set up mocked quota enforcer
|
||||
self.quota_patch = mock.patch(
|
||||
'barbican.common.quota.QuotaEnforcer.enforce', return_value=None)
|
||||
self.quota_patch.start()
|
||||
self.addCleanup(self.quota_patch.stop)
|
||||
|
||||
# Set up mocked container
|
||||
self.container = create_container(
|
||||
id_ref='id1',
|
||||
|
@ -203,7 +203,7 @@ class DummyRepoForTestingQuotaEnforcement(object):
|
||||
def __init__(self, get_count_return_value):
|
||||
self.get_count_return_value = get_count_return_value
|
||||
|
||||
def get_count(self, external_project_id):
|
||||
def get_count(self, internal_project_id):
|
||||
return self.get_count_return_value
|
||||
|
||||
|
||||
@ -219,7 +219,7 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
|
||||
def test_should_pass_default_unlimited(self):
|
||||
test_repo = DummyRepoForTestingQuotaEnforcement(0)
|
||||
quota_enforcer = quota.QuotaEnforcer('secrets', test_repo)
|
||||
quota_enforcer.enforce(self.project.external_id)
|
||||
quota_enforcer.enforce(self.project)
|
||||
|
||||
def test_should_raise_disabled_value(self):
|
||||
test_repo = DummyRepoForTestingQuotaEnforcement(0)
|
||||
@ -232,7 +232,7 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
|
||||
exception = self.assertRaises(
|
||||
excep.QuotaReached,
|
||||
quota_enforcer.enforce,
|
||||
self.project.external_id
|
||||
self.project
|
||||
)
|
||||
self.assertIn('Quota reached for project', exception.message)
|
||||
self.assertIn('my_keystone_id', exception.message)
|
||||
@ -247,7 +247,7 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
|
||||
'transport_keys': 5}
|
||||
self.quota_driver.set_project_quotas(self.project.external_id,
|
||||
five_project_quotas)
|
||||
quota_enforcer.enforce(self.project.external_id)
|
||||
quota_enforcer.enforce(self.project)
|
||||
|
||||
def test_should_raise_equal_limit(self):
|
||||
test_repo = DummyRepoForTestingQuotaEnforcement(5)
|
||||
@ -260,7 +260,7 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
|
||||
exception = self.assertRaises(
|
||||
excep.QuotaReached,
|
||||
quota_enforcer.enforce,
|
||||
self.project.external_id
|
||||
self.project
|
||||
)
|
||||
self.assertIn('Quota reached for project', exception.message)
|
||||
self.assertIn('my_keystone_id', exception.message)
|
||||
@ -278,7 +278,7 @@ class WhenTestingQuotaEnforcingFunctions(utils.BaseTestCase):
|
||||
exception = self.assertRaises(
|
||||
excep.QuotaReached,
|
||||
quota_enforcer.enforce,
|
||||
self.project.external_id
|
||||
self.project
|
||||
)
|
||||
self.assertIn('Quota reached for project', exception.message)
|
||||
self.assertIn('my_keystone_id', exception.message)
|
||||
|
@ -20,12 +20,14 @@ from functionaltests.api.v1.models import consumer_model
|
||||
class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
|
||||
def create_consumer(self, model, container_ref, extra_headers=None,
|
||||
use_auth=True):
|
||||
user_name=None, admin=None, use_auth=True):
|
||||
"""Register a consumer to a container.
|
||||
|
||||
:param model: The metadata for the consumer
|
||||
:param container_ref: Full reference to a container
|
||||
:param extra_headers: Any additional headers to pass to the request
|
||||
:param user_name: The user name used to create the consumer
|
||||
:param admin: The user with permission to delete the consumer
|
||||
:param use_auth: Boolean for whether to send authentication headers
|
||||
|
||||
:return: A tuple containing the response from the create
|
||||
@ -35,18 +37,25 @@ class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
url = '{0}/consumers'.format(container_ref)
|
||||
|
||||
resp = self.client.post(url, request_model=model,
|
||||
extra_headers=extra_headers, use_auth=use_auth)
|
||||
extra_headers=extra_headers,
|
||||
user_name=user_name, use_auth=use_auth)
|
||||
|
||||
if resp.status_code == 401 and not use_auth:
|
||||
return resp, None
|
||||
|
||||
if resp.status_code == 200:
|
||||
if admin is None:
|
||||
admin = user_name
|
||||
self.created_entities.append((container_ref, model, admin))
|
||||
|
||||
returned_data = self.get_json(resp)
|
||||
consumer_data = returned_data.get('consumers')
|
||||
|
||||
return resp, consumer_data
|
||||
|
||||
def get_consumers(self, container_ref, limit=10, offset=0,
|
||||
extra_headers=None, use_auth=True):
|
||||
extra_headers=None,
|
||||
user_name=None, use_auth=True):
|
||||
"""Gets a list of consumers on a container.
|
||||
|
||||
:param container_ref: Full reference to a container
|
||||
@ -54,6 +63,7 @@ class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
:param offset: represents how many records to skip before retrieving
|
||||
the list
|
||||
:param extra_headers: Any additional headers to pass to the request
|
||||
:param user_name: The user name used to get the consumer
|
||||
:param use_auth: Boolean for whether to send authentication headers
|
||||
|
||||
:return: The response from the get and refs to the next/previous list
|
||||
@ -64,7 +74,7 @@ class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
|
||||
params = {'limit': limit, 'offset': offset}
|
||||
resp = self.client.get(url, params=params, extra_headers=extra_headers,
|
||||
use_auth=use_auth)
|
||||
user_name=user_name, use_auth=use_auth)
|
||||
|
||||
if resp.status_code == 401 and not use_auth:
|
||||
return resp, None, None, None
|
||||
@ -77,12 +87,13 @@ class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
return resp, consumers, next_ref, prev_ref
|
||||
|
||||
def delete_consumer(self, model, container_ref, extra_headers=None,
|
||||
use_auth=True):
|
||||
user_name=None, use_auth=True):
|
||||
"""Deletes a consumer from a container.
|
||||
|
||||
:param model: The metadata for the consumer
|
||||
:param container_ref: Full reference to a container
|
||||
:param extra_headers: Any additional headers to pass to the request
|
||||
:param user_name: The user name used to delete the consumer
|
||||
:param use_auth: Boolean for whether to send authentication headers
|
||||
|
||||
:return: The response from the delete
|
||||
@ -91,6 +102,7 @@ class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
|
||||
resp = self.client.delete(url, request_model=model,
|
||||
extra_headers=extra_headers,
|
||||
user_name=user_name,
|
||||
use_auth=use_auth)
|
||||
|
||||
if resp.status_code == 401 and not use_auth:
|
||||
@ -100,3 +112,9 @@ class ConsumerBehaviors(base_behaviors.BaseBehaviors):
|
||||
consumer_data = returned_data['consumers']
|
||||
|
||||
return resp, consumer_data
|
||||
|
||||
def delete_all_created_consumers(self):
|
||||
"""Delete all of the consumers that we have created."""
|
||||
entities = list(self.created_entities)
|
||||
for (container_ref, model, admin) in entities:
|
||||
self.delete_consumer(model, container_ref, user_name=admin)
|
||||
|
@ -45,6 +45,8 @@ class OrderBehaviors(base_behaviors.BaseBehaviors):
|
||||
|
||||
# remember this order and its admin for our housekeeping cleanup
|
||||
if order_ref:
|
||||
if admin is None:
|
||||
admin = user_name
|
||||
self.created_entities.append((order_ref, admin))
|
||||
|
||||
return resp, order_ref
|
||||
|
@ -13,6 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from testtools import testcase
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import quota_behaviors
|
||||
from functionaltests.api.v1.models import quota_models
|
||||
@ -100,6 +102,7 @@ class QuotasTestCase(base.TestCase):
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
|
||||
@testcase.attr('no_parallel')
|
||||
class ProjectQuotasPagingTestCase(base.PagingTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
232
functionaltests/api/v1/functional/test_quotas_enforce.py
Normal file
232
functionaltests/api/v1/functional/test_quotas_enforce.py
Normal file
@ -0,0 +1,232 @@
|
||||
# Copyright (c) 2015 Cisco Systems
|
||||
#
|
||||
# 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 testtools import testcase
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.v1.behaviors import consumer_behaviors
|
||||
from functionaltests.api.v1.behaviors import container_behaviors
|
||||
from functionaltests.api.v1.behaviors import order_behaviors
|
||||
from functionaltests.api.v1.behaviors import quota_behaviors
|
||||
from functionaltests.api.v1.behaviors import secret_behaviors
|
||||
from functionaltests.api.v1.models import consumer_model
|
||||
from functionaltests.api.v1.models import container_models
|
||||
from functionaltests.api.v1.models import order_models
|
||||
from functionaltests.api.v1.models import quota_models
|
||||
from functionaltests.api.v1.models import secret_models
|
||||
from functionaltests.common import config
|
||||
|
||||
|
||||
CONF = config.get_config()
|
||||
admin_b = CONF.rbac_users.admin_b
|
||||
service_admin = CONF.identity.service_admin
|
||||
|
||||
|
||||
@testcase.attr('no_parallel')
|
||||
class QuotaEnforcementTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(QuotaEnforcementTestCase, self).setUp()
|
||||
self.quota_behaviors = quota_behaviors.QuotaBehaviors(self.client)
|
||||
self.secret_behaviors = secret_behaviors.SecretBehaviors(self.client)
|
||||
self.container_behaviors = container_behaviors.ContainerBehaviors(
|
||||
self.client)
|
||||
self.order_behaviors = order_behaviors.OrderBehaviors(self.client)
|
||||
self.consumer_behaviors = consumer_behaviors.ConsumerBehaviors(
|
||||
self.client)
|
||||
|
||||
self.secret_data = self.get_default_secret_data()
|
||||
self.quota_data = self.get_default_quota_data()
|
||||
self.project_id = self.quota_behaviors.get_project_id_from_name(
|
||||
admin_b)
|
||||
self.order_secrets = []
|
||||
|
||||
def tearDown(self):
|
||||
self.quota_behaviors.delete_all_created_quotas()
|
||||
self.consumer_behaviors.delete_all_created_consumers()
|
||||
self.container_behaviors.delete_all_created_containers()
|
||||
self.secret_behaviors.delete_all_created_secrets()
|
||||
for secret_ref in self.order_secrets:
|
||||
resp = self.secret_behaviors.delete_secret(
|
||||
secret_ref, user_name=admin_b)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.order_behaviors.delete_all_created_orders()
|
||||
super(QuotaEnforcementTestCase, self).tearDown()
|
||||
|
||||
def test_secrets_unlimited(self):
|
||||
self.set_quotas('secrets', -1)
|
||||
self.create_secrets(count=5)
|
||||
|
||||
def test_secrets_disabled(self):
|
||||
self.set_quotas('secrets', 0)
|
||||
self.create_secrets(expected_return=403)
|
||||
|
||||
def test_secrets_limited_one(self):
|
||||
self.set_quotas('secrets', 1)
|
||||
self.create_secrets(count=1)
|
||||
self.create_secrets(expected_return=403)
|
||||
|
||||
def test_secrets_limited_five(self):
|
||||
self.set_quotas('secrets', 5)
|
||||
self.create_secrets(count=5)
|
||||
self.create_secrets(expected_return=403)
|
||||
|
||||
def test_containers_unlimited(self):
|
||||
self.set_quotas('containers', -1)
|
||||
self.create_containers(count=5)
|
||||
|
||||
def test_containers_disabled(self):
|
||||
self.set_quotas('containers', 0)
|
||||
self.create_containers(expected_return=403)
|
||||
|
||||
def test_containers_limited_one(self):
|
||||
self.set_quotas('containers', 1)
|
||||
self.create_containers(count=1)
|
||||
self.create_containers(expected_return=403)
|
||||
|
||||
def test_containers_limited_five(self):
|
||||
self.set_quotas('containers', 5)
|
||||
self.create_containers(count=5)
|
||||
self.create_containers(expected_return=403)
|
||||
|
||||
def test_orders_unlimited(self):
|
||||
self.set_quotas('orders', -1)
|
||||
self.create_orders(count=5)
|
||||
|
||||
def test_orders_disabled(self):
|
||||
self.set_quotas('orders', 0)
|
||||
self.create_orders(expected_return=403)
|
||||
|
||||
def test_orders_limited_one(self):
|
||||
self.set_quotas('orders', 1)
|
||||
self.create_orders(count=1)
|
||||
self.create_orders(expected_return=403)
|
||||
|
||||
def test_orders_limited_five(self):
|
||||
self.set_quotas('orders', 5)
|
||||
self.create_orders(count=5)
|
||||
self.create_orders(expected_return=403)
|
||||
|
||||
def test_consumers_unlimited(self):
|
||||
self.set_quotas('consumers', -1)
|
||||
self.create_consumers(count=5)
|
||||
|
||||
def test_consumers_disabled(self):
|
||||
self.set_quotas('consumers', 0)
|
||||
self.create_consumers(expected_return=403)
|
||||
|
||||
def test_consumers_limited_one(self):
|
||||
self.set_quotas('consumers', 1)
|
||||
self.create_consumers(count=1)
|
||||
self.create_consumers(expected_return=403)
|
||||
|
||||
def test_consumers_limited_five(self):
|
||||
self.set_quotas('consumers', 5)
|
||||
self.create_consumers(count=5)
|
||||
self.create_consumers(expected_return=403)
|
||||
|
||||
# ----------------------- Helper Functions ---------------------------
|
||||
|
||||
def get_default_quota_data(self):
|
||||
return {"project_quotas":
|
||||
{"secrets": -1,
|
||||
"orders": -1,
|
||||
"containers": -1,
|
||||
"consumers": -1}}
|
||||
|
||||
def set_quotas(self, resource, quota):
|
||||
"""Utility function to set resource quotas"""
|
||||
self.quota_data["project_quotas"][resource] = quota
|
||||
request_model = quota_models.ProjectQuotaRequestModel(
|
||||
**self.quota_data)
|
||||
resp = self.quota_behaviors.set_project_quotas(self.project_id,
|
||||
request_model,
|
||||
user_name=service_admin)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
|
||||
def get_default_secret_data(self):
|
||||
return {
|
||||
"name": "AES key",
|
||||
"expiration": "2050-02-28T19:14:44.180394",
|
||||
"algorithm": "aes",
|
||||
"bit_length": 256,
|
||||
"mode": "cbc",
|
||||
"payload": "Z0Y2K2xMb0Yzb2hBOWFQUnB0KzZiUT09",
|
||||
"payload_content_type": "application/octet-stream",
|
||||
"payload_content_encoding": "base64",
|
||||
}
|
||||
|
||||
def create_secrets(self, count=1, expected_return=201):
|
||||
"""Utility function to create secrets"""
|
||||
secret_ref = None
|
||||
for _ in range(count):
|
||||
test_model = secret_models.SecretModel(**self.secret_data)
|
||||
resp, secret_ref = self.secret_behaviors.create_secret(
|
||||
test_model, user_name=admin_b)
|
||||
self.assertEqual(expected_return, resp.status_code)
|
||||
return secret_ref
|
||||
|
||||
def get_container_req(self, secret_ref):
|
||||
return {"name": "test_container",
|
||||
"type": "generic",
|
||||
"secret_refs": [{'name': 'secret1', 'secret_ref': secret_ref}]}
|
||||
|
||||
def create_containers(self, count=1, expected_return=201):
|
||||
"""Utility function to create containers"""
|
||||
container_ref = None
|
||||
for _ in range(count):
|
||||
secret_ref = self.create_secrets()
|
||||
test_model = container_models.ContainerModel(
|
||||
**self.get_container_req(secret_ref))
|
||||
resp, container_ref = self.container_behaviors.create_container(
|
||||
test_model, user_name=admin_b)
|
||||
self.assertEqual(expected_return, resp.status_code)
|
||||
return container_ref
|
||||
|
||||
def get_default_order_data(self):
|
||||
return {'type': 'key',
|
||||
"meta": {
|
||||
"name": "barbican functional test order name",
|
||||
"algorithm": "aes",
|
||||
"bit_length": 256,
|
||||
"mode": "cbc"}}
|
||||
|
||||
def create_orders(self, count=1, expected_return=202):
|
||||
"""Utility function to create orders"""
|
||||
for _ in range(count):
|
||||
order_data = self.get_default_order_data()
|
||||
test_model = order_models.OrderModel(**order_data)
|
||||
resp, order_ref = self.order_behaviors.create_order(
|
||||
test_model, user_name=admin_b)
|
||||
self.assertEqual(expected_return, resp.status_code)
|
||||
if resp.status_code == 202:
|
||||
order_resp = self.order_behaviors.get_order(
|
||||
order_ref, user_name=admin_b)
|
||||
self.assertEqual(order_resp.status_code, 200)
|
||||
self.order_secrets.append(order_resp.model.secret_ref)
|
||||
|
||||
def get_default_consumer_data(self):
|
||||
return {"name": "consumer_name",
|
||||
"URL": "consumer_url"}
|
||||
|
||||
def create_consumers(self, count=1, expected_return=200):
|
||||
"""Utility function to create consumers"""
|
||||
for _ in range(count):
|
||||
container_ref = self.create_containers()
|
||||
model = consumer_model.ConsumerModel(
|
||||
**self.get_default_consumer_data())
|
||||
resp, consumer_dat = self.consumer_behaviors.create_consumer(
|
||||
model, container_ref, user_name=admin_b)
|
||||
self.assertEqual(expected_return, resp.status_code)
|
@ -35,7 +35,7 @@ retval=$?
|
||||
testr slowest
|
||||
|
||||
# run the tests in parallel
|
||||
SKIP=^\(\?\!\.\*ProjectQuotasPagingTestCase\)
|
||||
SKIP=^\(\?\!\.\*\(ProjectQuotasPagingTestCase\|QuotaEnforcementTestCase\)\)
|
||||
testr init
|
||||
testr run $SKIP --parallel --subunit | subunit-trace --no-failure-debug -f
|
||||
retval=$?
|
||||
|
Loading…
x
Reference in New Issue
Block a user