From 9359e9c1c74a8c351a79c2268c2ffdb50ed960d5 Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Mon, 2 Jul 2018 20:53:13 +0000 Subject: [PATCH] Implement basic Enforcer context manager This commit builds the foundation for a context manager that can be used for usage enforcement. Subsequent patches will incorporate keystoneauth and perform calls to keystone to verify project limits and registered limits, incorporating them into the usage check. Change-Id: I7c78c1979444a2a83189e2035a4ab72890abc5ab --- oslo_limit/limit.py | 39 +++++++++++++++++++++ oslo_limit/tests/test_limit.py | 63 ++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/oslo_limit/limit.py b/oslo_limit/limit.py index b449bee..283f5da 100644 --- a/oslo_limit/limit.py +++ b/oslo_limit/limit.py @@ -44,3 +44,42 @@ class ProjectClaim(object): self.resource_name = resource_name self.project_id = project_id self.quantity = quantity + + +class Enforcer(object): + + def __init__(self, claim, callback=None, verify=True): + """Context manager for checking usage against resource claims. + + :param claim: An object containing information about the claim. + :type claim: ``oslo_limit.limit.ProjectClaim`` + :param callback: A callable function that accepts a project_id string + as a parameter and calculates the current usage of a + resource. + :type callable function: + :param verify: Boolean denoting whether or not to verify the new usage + after executing a claim. This can be useful for handling + race conditions between clients claiming resources. + :type verify: boolean + + """ + + if not isinstance(claim, ProjectClaim): + msg = 'claim must be an instance of oslo_limit.limit.ProjectClaim.' + raise ValueError(msg) + if callback and not callable(callback): + msg = 'callback must be a callable function.' + raise ValueError(msg) + if verify and not isinstance(verify, bool): + msg = 'verify must be a boolean value.' + raise ValueError(msg) + + self.claim = claim + self.callback = callback + self.verify = verify + + def __enter__(self): + pass + + def __exit__(self, *args): + pass diff --git a/oslo_limit/tests/test_limit.py b/oslo_limit/tests/test_limit.py index 0ee3914..c06176b 100644 --- a/oslo_limit/tests/test_limit.py +++ b/oslo_limit/tests/test_limit.py @@ -91,3 +91,66 @@ class TestProjectClaim(base.BaseTestCase): invalid_quantity, quantity=invalid_quantity ) + + +class TestEnforcer(base.BaseTestCase): + + def setUp(self): + super(TestEnforcer, self).setUp() + self.resource_name = uuid.uuid4().hex + self.project_id = uuid.uuid4().hex + self.quantity = 10 + self.claim = limit.ProjectClaim( + self.resource_name, self.project_id, quantity=self.quantity + ) + + def _get_usage_for_project(self, project_id): + return 8 + + def test_required_parameters(self): + enforcer = limit.Enforcer(self.claim) + + self.assertEqual(self.claim, enforcer.claim) + self.assertIsNone(enforcer.callback) + self.assertTrue(enforcer.verify) + + def test_optional_parameters(self): + callback = self._get_usage_for_project + enforcer = limit.Enforcer(self.claim, callback=callback, verify=True) + + self.assertEqual(self.claim, enforcer.claim) + self.assertEqual(self._get_usage_for_project, enforcer.callback) + self.assertTrue(enforcer.verify) + + def test_callback_must_be_callable(self): + invalid_callback_types = [uuid.uuid4().hex, 5, 5.1] + + for invalid_callback in invalid_callback_types: + self.assertRaises( + ValueError, + limit.Enforcer, + self.claim, + callback=invalid_callback + ) + + def test_verify_must_be_boolean(self): + invalid_verify_types = [uuid.uuid4().hex, 5, 5.1] + + for invalid_verify in invalid_verify_types: + self.assertRaises( + ValueError, + limit.Enforcer, + self.claim, + callback=self._get_usage_for_project, + verify=invalid_verify + ) + + def test_claim_must_be_an_instance_of_project_claim(self): + invalid_claim_types = [uuid.uuid4().hex, 5, 5.1, True, False, [], {}] + + for invalid_claim in invalid_claim_types: + self.assertRaises( + ValueError, + limit.Enforcer, + invalid_claim, + )