summaryrefslogtreecommitdiff
path: root/oslo_context
diff options
context:
space:
mode:
authorJamie Lennox <jamielennox@gmail.com>2016-05-09 11:11:55 +1000
committerAdam Young <ayoung@redhat.com>2016-09-12 13:48:34 -0400
commit2394cff0631944a9259bfe04925e444d9f817758 (patch)
treeba0488f1eb2b6b4a5d4ec2daf36cf5cfcdc400c0 /oslo_context
parent62162cfced9bc91216c6d79f4389260bd696b297 (diff)
downloadoslo-context-2394cff0631944a9259bfe04925e444d9f817758.tar.gz
Provide a way to deprecate policy values
If we want to move all the services over to a standard policy enforcement dict we need a way to start notifying deployers when the policy enforcement files are using deprecated values. Instead of returning a dictionary return an object that acts like a dictionary but emits a DeprecationWarning whenever a deprecated policy enforcement value is read from it. Change-Id: I4b2fda188bbccfd491556cc5631e5c4a76314492
Diffstat (limited to 'oslo_context')
-rw-r--r--oslo_context/context.py75
-rw-r--r--oslo_context/tests/test_context.py30
2 files changed, 98 insertions, 7 deletions
diff --git a/oslo_context/context.py b/oslo_context/context.py
index 1a2ce21..481b18c 100644
--- a/oslo_context/context.py
+++ b/oslo_context/context.py
@@ -26,9 +26,11 @@ context or provide additional information in their specific WSGI pipeline
or logging context.
"""
+import collections
import itertools
import threading
import uuid
+import warnings
from positional import positional
@@ -60,6 +62,62 @@ def generate_request_id():
return 'req-%s' % uuid.uuid4()
+class _DeprecatedPolicyValues(collections.MutableMapping):
+ """A Dictionary that manages current and deprecated policy values.
+
+ Anything added to this dictionary after initial creation is considered a
+ deprecated key that we are trying to move services away from. Accessing
+ these values as oslo.policy will do will trigger a DeprecationWarning.
+ """
+
+ def __init__(self, data):
+ self._data = data
+ self._deprecated = {}
+
+ def __getitem__(self, k):
+ try:
+ return self._data[k]
+ except KeyError:
+ pass
+
+ try:
+ val = self._deprecated[k]
+ except KeyError:
+ pass
+ else:
+ warnings.warn('Policy enforcement is depending on the value of '
+ '%s. This key is deprecated. Please update your '
+ 'policy file to use the standard policy values.' % k,
+ DeprecationWarning)
+ return val
+
+ raise KeyError(k)
+
+ def __setitem__(self, k, v):
+ self._deprecated[k] = v
+
+ def __delitem__(self, k):
+ del self._deprecated[k]
+
+ def __iter__(self):
+ return iter(self._dict)
+
+ def __len__(self):
+ return len(self._dict)
+
+ def __str__(self):
+ return self._dict.__str__()
+
+ def __repr__(self):
+ return self._dict.__repr__()
+
+ @property
+ def _dict(self):
+ d = self._deprecated.copy()
+ d.update(self._data)
+ return d
+
+
class RequestContext(object):
"""Helper class to represent useful information about a request context.
@@ -128,12 +186,17 @@ class RequestContext(object):
with either deprecated values or additional attributes used by that
service specific policy.
"""
- return {'user_id': self.user,
- 'user_domain_id': self.user_domain,
- 'project_id': self.tenant,
- 'project_domain_id': self.project_domain,
- 'roles': self.roles,
- 'is_admin_project': self.is_admin_project}
+ # NOTE(jamielennox): We need a way to allow projects to provide old
+ # deprecated policy values that trigger a warning when used in favour
+ # of our standard ones. This object acts like a dict but only values
+ # from oslo.policy don't show a warning.
+ return _DeprecatedPolicyValues({
+ 'user_id': self.user,
+ 'user_domain_id': self.user_domain,
+ 'project_id': self.tenant,
+ 'project_domain_id': self.project_domain,
+ 'roles': self.roles,
+ 'is_admin_project': self.is_admin_project})
def to_dict(self):
"""Return a dictionary of context attributes."""
diff --git a/oslo_context/tests/test_context.py b/oslo_context/tests/test_context.py
index 956c1e5..9a32fac 100644
--- a/oslo_context/tests/test_context.py
+++ b/oslo_context/tests/test_context.py
@@ -471,7 +471,6 @@ class ContextTest(test_base.BaseTestCase):
'is_admin_project': True},
ctx.to_policy_values())
- # is_admin_project False gets passed through
ctx = context.RequestContext(user=user,
user_domain=user_domain,
tenant=tenant,
@@ -493,3 +492,32 @@ class ContextTest(test_base.BaseTestCase):
self.assertEqual(1, len(self.warnings.log))
self.assertIn('__init__ takes at most 1 positional',
str(self.warnings.log[0].message))
+
+ def test_policy_deprecations(self):
+ user = uuid.uuid4().hex
+ user_domain = uuid.uuid4().hex
+ tenant = uuid.uuid4().hex
+ project_domain = uuid.uuid4().hex
+ roles = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex]
+
+ ctx = context.RequestContext(user=user,
+ user_domain=user_domain,
+ tenant=tenant,
+ project_domain=project_domain,
+ roles=roles)
+
+ policy = ctx.to_policy_values()
+ key = uuid.uuid4().hex
+ val = uuid.uuid4().hex
+
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+
+ # no warning triggered by adding key to dict
+ policy[key] = val
+ self.assertEqual(0, len(w))
+
+ # warning triggered by fetching key from dict
+ self.assertIs(val, policy[key])
+ self.assertEqual(1, len(w))
+ self.assertIn(key, str(w[0].message))