New test cases for quota tests suite
1) test_admin_sets_quota_for_a_project
Admin client sets quotas for a primary project
2) test_primary_fails_to_set_quota
Primary user fails to set quotas with: "403 Forbidden"
3) test_admin_sets_invalid_quota_values
Admin user tries to set quotas that with invalid
values. Expected: "400 BadRequest"
4) test_alt_reaches_zones_quota
Alt user tries to create a zones up untill
"413 RateLimitExceeded" is raised
Change-Id: I47e8371e55f07afe0dbea95f804f3833f42fa1ef
diff --git a/designate_tempest_plugin/services/dns/v2/json/quotas_client.py b/designate_tempest_plugin/services/dns/v2/json/quotas_client.py
index f9ee10a..97398c0 100644
--- a/designate_tempest_plugin/services/dns/v2/json/quotas_client.py
+++ b/designate_tempest_plugin/services/dns/v2/json/quotas_client.py
@@ -88,3 +88,25 @@
self.expected_success(204, resp.status)
return resp, body
+
+ @base.handle_errors
+ def set_quotas(self, project_id, quotas, params=None, headers=None):
+ """Set a specific quota to given project ID
+
+ :param project_id: Set the quotas of this project id.
+ :param quotas: Dictionary of quotas to set (POST payload)
+ :param params: A Python dict that represents the query paramaters to
+ include in the request URI.
+ :param headers (dict): The headers to use for the request.
+ :return: Serialized quota as a dictionary.
+ """
+ if headers is None:
+ headers = {'content-type': 'application/json'}
+ if 'content-type' not in [header.lower() for header in headers]:
+ headers['content-type'] = 'application/json'
+
+ resp, body = self._update_request(
+ "quotas", project_id,
+ data=quotas, params=params, headers=headers)
+ self.expected_success(200, resp.status)
+ return resp, body
diff --git a/designate_tempest_plugin/tests/api/v2/test_quotas.py b/designate_tempest_plugin/tests/api/v2/test_quotas.py
index 9adf62e..db870af 100644
--- a/designate_tempest_plugin/tests/api/v2/test_quotas.py
+++ b/designate_tempest_plugin/tests/api/v2/test_quotas.py
@@ -15,6 +15,7 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import data_utils as tempest_data_utils
from designate_tempest_plugin.tests import base
from designate_tempest_plugin import data_utils as dns_data_utils
@@ -52,6 +53,7 @@
cls.admin_client = cls.os_admin.dns_v2.QuotasClient()
cls.quotas_client = cls.os_primary.dns_v2.QuotasClient()
cls.alt_client = cls.os_alt.dns_v2.QuotasClient()
+ cls.alt_zone_client = cls.os_alt.dns_v2.ZonesClient()
def _store_quotas(self, project_id, cleanup=True):
"""Remember current quotas and reset them after the test"""
@@ -172,11 +174,11 @@
if not CONF.dns_feature_enabled.api_v2_quotas_verify_project:
raise self.skipException("Project ID in quotas "
"is not being verified.")
-
+ original_quotas = self.quotas_client.show_quotas(
+ project_id=self.quotas_client.project_id)[1]
project_id = 'project-that-does-not-exist'
LOG.info("Updating quotas for non-existing %s ", project_id)
-
quotas = dns_data_utils.rand_quotas()
request = quotas.copy()
with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_project', 400):
@@ -184,3 +186,75 @@
project_id=project_id,
headers=self.all_projects_header,
**request)
+
+ LOG.info("Make sure that the quotas weren't changed")
+ _, client_body = self.quotas_client.show_quotas(
+ project_id=self.quotas_client.project_id)
+ self.assertExpected(original_quotas, client_body, [])
+
+ @decorators.idempotent_id('ae82a0ba-da60-11eb-bf12-74e5f9e2a801')
+ def test_admin_sets_quota_for_a_project(self):
+
+ primary_project_id = self.quotas_client.project_id
+ http_headers_to_use = [
+ {'X-Auth-All-Projects': True},
+ {'x-auth-sudo-project-id': primary_project_id}]
+
+ for http_header in http_headers_to_use:
+ LOG.info('As Admin user set Zones quota for a Primary user and {} '
+ 'HTTP header'.format(http_header))
+ quotas = dns_data_utils.rand_quotas()
+ self.admin_client.set_quotas(
+ project_id=primary_project_id,
+ quotas=quotas, headers=http_header)
+ self.addCleanup(self.admin_client.delete_quotas,
+ project_id=primary_project_id)
+
+ LOG.info("As Admin fetch the quotas for a Primary user")
+ body = self.admin_client.show_quotas(
+ project_id=primary_project_id, headers=http_header)[1]
+ LOG.info('Ensuring that the "set" and "shown" quotas are same')
+ self.assertExpected(quotas, body, [])
+
+ @decorators.idempotent_id('40b9d7ac-da5f-11eb-bf12-74e5f9e2a801')
+ def test_primary_fails_to_set_quota(self):
+
+ primary_project_id = self.quotas_client.project_id
+ LOG.info('Try to set quota as Primary user')
+ self.assertRaises(
+ lib_exc.Forbidden, self.quotas_client.set_quotas,
+ project_id=primary_project_id,
+ quotas=dns_data_utils.rand_quotas())
+
+ LOG.info('Try to set quota as Primary user using '
+ '"x-auth-sudo-project-id" HTTP header')
+ self.assertRaises(
+ lib_exc.Forbidden, self.quotas_client.set_quotas,
+ project_id=self.quotas_client.project_id,
+ quotas=dns_data_utils.rand_quotas(),
+ headers={'x-auth-sudo-project-id': primary_project_id})
+
+ LOG.info('Try to set quota as Primary user using '
+ '"x-auth-all-projects" HTTP header')
+ self.assertRaises(
+ lib_exc.Forbidden, self.quotas_client.set_quotas,
+ project_id=self.quotas_client.project_id,
+ quotas=dns_data_utils.rand_quotas(),
+ headers={'x-auth-all-projects': True})
+
+ @decorators.idempotent_id('a6ce5b46-dcce-11eb-903e-74e5f9e2a801')
+ @decorators.skip_because(bug="1934596")
+ def test_admin_sets_invalid_quota_values(self):
+
+ primary_project_id = self.quotas_client.project_id
+ http_header = {'X-Auth-All-Projects': True}
+
+ for item in ['zones', 'zone_records',
+ 'zone_recordsets', 'recordset_records']:
+ quota = dns_data_utils.rand_quotas()
+ quota[item] = tempest_data_utils.rand_name()
+ self.assertRaises(
+ lib_exc.BadRequest, self.admin_client.set_quotas,
+ project_id=primary_project_id,
+ quotas=quota,
+ headers=http_header)
diff --git a/designate_tempest_plugin/tests/scenario/v2/test_quotas.py b/designate_tempest_plugin/tests/scenario/v2/test_quotas.py
new file mode 100644
index 0000000..0c20b3d
--- /dev/null
+++ b/designate_tempest_plugin/tests/scenario/v2/test_quotas.py
@@ -0,0 +1,92 @@
+# Copyright 2021 Red Hat.
+#
+# 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 tempest import config
+from tempest.lib import decorators
+
+from designate_tempest_plugin.tests import base
+from designate_tempest_plugin import data_utils as dns_data_utils
+
+LOG = logging.getLogger(__name__)
+
+
+CONF = config.CONF
+
+
+class QuotasV2Test(base.BaseDnsV2Test):
+
+ credentials = ['primary', 'admin', 'system_admin', 'alt']
+
+ @classmethod
+ def setup_credentials(cls):
+ # Do not create network resources for these test.
+ cls.set_network_resources()
+ super(QuotasV2Test, cls).setup_credentials()
+
+ @classmethod
+ def skip_checks(cls):
+ super(QuotasV2Test, cls).skip_checks()
+
+ if not CONF.dns_feature_enabled.api_v2_quotas:
+ skip_msg = ("%s skipped as designate V2 Quotas API is not "
+ "available" % cls.__name__)
+ raise cls.skipException(skip_msg)
+
+ @classmethod
+ def setup_clients(cls):
+ super(QuotasV2Test, cls).setup_clients()
+ if CONF.enforce_scope.designate:
+ cls.admin_client = cls.os_system_admin.dns_v2.QuotasClient()
+ else:
+ cls.admin_client = cls.os_admin.dns_v2.QuotasClient()
+ cls.quotas_client = cls.os_primary.dns_v2.QuotasClient()
+ cls.alt_client = cls.os_alt.dns_v2.QuotasClient()
+ cls.alt_zone_client = cls.os_alt.dns_v2.ZonesClient()
+
+ @decorators.idempotent_id('6987953a-dccf-11eb-903e-74e5f9e2a801')
+ def test_alt_reaches_zones_quota(self):
+
+ alt_project_id = self.alt_client.project_id
+ http_header = {'x-auth-sudo-project-id': alt_project_id}
+ limit_zones_quota = 3
+
+ LOG.info('As Admin user set Zones quota for Alt user '
+ 'to:{} '.format(limit_zones_quota))
+ quotas = dns_data_utils.rand_quotas()
+ quotas['zones'] = limit_zones_quota
+ self.admin_client.set_quotas(
+ project_id=alt_project_id, quotas=quotas, headers=http_header)
+ self.addCleanup(
+ self.admin_client.delete_quotas, project_id=alt_project_id)
+
+ LOG.info('As Alt user try to create zones, up untill'
+ ' "zones" quota (status code 413) is reached')
+ attempt_number = 0
+ while attempt_number <= limit_zones_quota + 1:
+ attempt_number += 1
+ LOG.info('Attempt No:{} '.format(attempt_number))
+ try:
+ zone = self.alt_zone_client.create_zone()[1]
+ self.addCleanup(
+ self.wait_zone_delete, self.alt_zone_client, zone['id'])
+ except Exception as err:
+ raised_error = str(err).replace(' ', '')
+ if not "'code':413" and "'type':'over_quota'" in raised_error \
+ and attempt_number == limit_zones_quota + 1:
+ raise (
+ "Failed, expected status code 413 (type:over_quota) "
+ "was not raised or maybe it has been raised mistakenly"
+ "(bug) before the quota was actually reached."
+ " Test failed with: {} ".format(err))