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
This commit is contained in:
parent
62162cfced
commit
2394cff063
|
@ -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."""
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue