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:
Jamie Lennox 2016-05-09 11:11:55 +10:00 committed by Adam Young
parent 62162cfced
commit 2394cff063
2 changed files with 98 additions and 7 deletions

View File

@ -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."""

View File

@ -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))