Add functionality to deprecate policies
This patch adds the ability to invoke deprecation messages when certain policies are used. This gives developers a way to communicate changing policies to operators in a programmable way. bp policy-deprecation Change-Id: I6348299970a1be502909f698a432b3bcaf6b9c8b
This commit is contained in:
parent
89d226916c
commit
d3ea093c18
@ -127,6 +127,28 @@ def _format_rule_default_yaml(default, include_help=True):
|
|||||||
'op': op,
|
'op': op,
|
||||||
'text': text})
|
'text': text})
|
||||||
|
|
||||||
|
if default.deprecated_for_removal:
|
||||||
|
text = (
|
||||||
|
'# DEPRECATED\n# "%(name)s" has been deprecated since '
|
||||||
|
'%(since)s.\n%(reason)s\n%(text)s'
|
||||||
|
) % {'name': default.name,
|
||||||
|
'since': default.deprecated_since,
|
||||||
|
'reason': _format_help_text(default.deprecated_reason),
|
||||||
|
'text': text}
|
||||||
|
elif default.deprecated_rule:
|
||||||
|
text = (
|
||||||
|
'# DEPRECATED\n# "%(old_name)s":"%(old_check_str)s" has been '
|
||||||
|
'deprecated since %(since)s in favor of '
|
||||||
|
'"%(name)s":"%(check_str)s".\n'
|
||||||
|
'%(reason)s\n%(text)s'
|
||||||
|
) % {'old_name': default.deprecated_rule.name,
|
||||||
|
'old_check_str': default.deprecated_rule.check_str,
|
||||||
|
'since': default.deprecated_since,
|
||||||
|
'name': default.name,
|
||||||
|
'check_str': default.check_str,
|
||||||
|
'reason': _format_help_text(default.deprecated_reason),
|
||||||
|
'text': text}
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
@ -221,6 +221,7 @@ by setting the ``policy_default_rule`` configuration setting to the
|
|||||||
desired rule name.
|
desired rule name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
@ -547,6 +548,66 @@ class Enforcer(object):
|
|||||||
force_reload, False)
|
force_reload, False)
|
||||||
|
|
||||||
for default in self.registered_rules.values():
|
for default in self.registered_rules.values():
|
||||||
|
if default.deprecated_rule:
|
||||||
|
deprecated_msg = (
|
||||||
|
'Policy "%(old_name)s":"%(old_check_str)s" was '
|
||||||
|
'deprecated in %(release)s in favor of "%(name)s":'
|
||||||
|
'"%(check_str)s". Reason: %(reason)s. Either ensure '
|
||||||
|
'your deployment is ready for the new default or '
|
||||||
|
'copy/paste the deprecated policy into your policy '
|
||||||
|
'file and maintain it manually.' % {
|
||||||
|
'old_name': default.deprecated_rule.name,
|
||||||
|
'old_check_str': default.deprecated_rule.check_str,
|
||||||
|
'release': default.deprecated_since,
|
||||||
|
'name': default.name,
|
||||||
|
'check_str': default.check_str,
|
||||||
|
'reason': default.deprecated_reason
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if default.deprecated_rule.name != default.name and (
|
||||||
|
default.deprecated_rule.name in self.rules):
|
||||||
|
# Print a warning because the actual policy name is
|
||||||
|
# changing. If deployers are relying on an override for
|
||||||
|
# foo:bar and it's getting renamed to foo:create_bar
|
||||||
|
# then they need to be able to see that before they
|
||||||
|
# roll out the next release.
|
||||||
|
warnings.warn(deprecated_msg)
|
||||||
|
if (default.deprecated_rule.check_str !=
|
||||||
|
default.check_str and default.name not in
|
||||||
|
self.rules):
|
||||||
|
# In this case, the default check_str is changing. We
|
||||||
|
# need to let operators know that this is going to
|
||||||
|
# change. If they don't want to override it, they are
|
||||||
|
# going to have to make sure the right infrastructure
|
||||||
|
# exists before they upgrade. This overrides the new
|
||||||
|
# check with an OrCheck that combines the new and old
|
||||||
|
# check_str attributes from the new and deprecated
|
||||||
|
# policies. This will make it so that deployments don't
|
||||||
|
# break on upgrade, but they receive log messages
|
||||||
|
# telling them stuff is going to change if they don't
|
||||||
|
# maintain the policy manually or add infrastructure to
|
||||||
|
# their deployment to support the new policy.
|
||||||
|
default.check = _parser.parse_rule(
|
||||||
|
default.check_str + ' or ' +
|
||||||
|
default.deprecated_rule.check_str
|
||||||
|
)
|
||||||
|
warnings.warn(deprecated_msg)
|
||||||
|
if default.deprecated_for_removal and (
|
||||||
|
default.name in self.rules):
|
||||||
|
# If a policy is going to be removed altogether, then we
|
||||||
|
# need to make sure we let operators know so they can clean
|
||||||
|
# up their policy files, if they are overriding it.
|
||||||
|
warnings.warn(
|
||||||
|
'Policy "%(policy)s":"%(check_str)s" was '
|
||||||
|
'deprecated for removal in %(release)s. Reason: '
|
||||||
|
'%(reason)s. Its value may be silently ignored in '
|
||||||
|
'the future.' % {
|
||||||
|
'policy': default.name,
|
||||||
|
'check_str': default.check_str,
|
||||||
|
'release': default.deprecated_since,
|
||||||
|
'reason': default.deprecated_reason
|
||||||
|
}
|
||||||
|
)
|
||||||
if default.name not in self.rules:
|
if default.name not in self.rules:
|
||||||
self.rules[default.name] = default.check
|
self.rules[default.name] = default.check
|
||||||
|
|
||||||
@ -822,12 +883,54 @@ class RuleDefault(object):
|
|||||||
:param description: A plain text description of the policy. This will be
|
:param description: A plain text description of the policy. This will be
|
||||||
used to comment sample policy files for use by
|
used to comment sample policy files for use by
|
||||||
deployers.
|
deployers.
|
||||||
|
:param deprecated_rule: :class:`.DeprecatedRule`
|
||||||
|
:param deprecated_for_removal: indicates whether the policy is planned for
|
||||||
|
removal in a future release.
|
||||||
|
:param deprecated_reason: indicates why this policy is planned for removal
|
||||||
|
in a future release. Silently ignored if
|
||||||
|
deprecated_for_removal is False.
|
||||||
|
:param deprecated_since: indicates which release this policy was deprecated
|
||||||
|
in. Accepts any string, though valid version
|
||||||
|
strings are encouraged. Silently ignored if
|
||||||
|
deprecated_for_removal is False.
|
||||||
|
|
||||||
|
.. versionchanged 1.29
|
||||||
|
Added *deprecated_rule* parameter.
|
||||||
|
|
||||||
|
.. versionchanged 1.29
|
||||||
|
Added *deprecated_for_removal* parameter.
|
||||||
|
|
||||||
|
.. versionchanged 1.29
|
||||||
|
Added *deprecated_reason* parameter.
|
||||||
|
|
||||||
|
.. versionchanged 1.29
|
||||||
|
Added *deprecated_since* parameter.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, check_str, description=None):
|
def __init__(self, name, check_str, description=None,
|
||||||
|
deprecated_rule=None, deprecated_for_removal=False,
|
||||||
|
deprecated_reason=None, deprecated_since=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.check_str = check_str
|
self.check_str = check_str
|
||||||
self.check = _parser.parse_rule(check_str)
|
self.check = _parser.parse_rule(check_str)
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.deprecated_rule = copy.deepcopy(deprecated_rule) or []
|
||||||
|
self.deprecated_for_removal = deprecated_for_removal
|
||||||
|
self.deprecated_reason = deprecated_reason
|
||||||
|
self.deprecated_since = deprecated_since
|
||||||
|
|
||||||
|
if self.deprecated_rule:
|
||||||
|
if not isinstance(self.deprecated_rule, DeprecatedRule):
|
||||||
|
raise ValueError(
|
||||||
|
'deprecated_rule must be a DeprecatedRule object.'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (deprecated_for_removal or deprecated_rule) and (
|
||||||
|
deprecated_reason is None or deprecated_since is None):
|
||||||
|
raise ValueError(
|
||||||
|
'%(name)s deprecated without deprecated_reason or '
|
||||||
|
'deprecated_since. Both must be supplied if deprecating a '
|
||||||
|
'policy' % {'name': self.name}
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '"%(name)s": "%(check_str)s"' % {'name': self.name,
|
return '"%(name)s": "%(check_str)s"' % {'name': self.name,
|
||||||
@ -871,9 +974,16 @@ class DocumentedRuleDefault(RuleDefault):
|
|||||||
operations=[{'path': '/foo', 'method': 'GET'},
|
operations=[{'path': '/foo', 'method': 'GET'},
|
||||||
{'path': '/some', 'method': 'POST'}]
|
{'path': '/some', 'method': 'POST'}]
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, check_str, description, operations):
|
def __init__(self, name, check_str, description, operations,
|
||||||
|
deprecated_rule=None, deprecated_for_removal=False,
|
||||||
|
deprecated_reason=None, deprecated_since=None):
|
||||||
super(DocumentedRuleDefault, self).__init__(
|
super(DocumentedRuleDefault, self).__init__(
|
||||||
name, check_str, description)
|
name, check_str, description,
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_for_removal=deprecated_for_removal,
|
||||||
|
deprecated_reason=deprecated_reason,
|
||||||
|
deprecated_since=deprecated_since
|
||||||
|
)
|
||||||
self.operations = operations
|
self.operations = operations
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -906,3 +1016,172 @@ class DocumentedRuleDefault(RuleDefault):
|
|||||||
if len(op.keys()) > 2:
|
if len(op.keys()) > 2:
|
||||||
raise InvalidRuleDefault('Operation contains > 2 keys')
|
raise InvalidRuleDefault('Operation contains > 2 keys')
|
||||||
self._operations = ops
|
self._operations = ops
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedRule(object):
|
||||||
|
|
||||||
|
"""Represents a Deprecated policy or rule.
|
||||||
|
|
||||||
|
Here's how you can use it to change a policy's default role or rule. Assume
|
||||||
|
the following policy exists in code::
|
||||||
|
|
||||||
|
from oslo_policy import policy
|
||||||
|
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:fizz',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'POST'}]
|
||||||
|
)
|
||||||
|
|
||||||
|
The next snippet will maintain the deprecated option, but allow
|
||||||
|
``foo:create_bar`` to default to ``role:bang`` instead of ``role:fizz``::
|
||||||
|
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:fizz'
|
||||||
|
)
|
||||||
|
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:bang',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='role:bang is a better default',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
|
||||||
|
DeprecatedRule can be used to change the policy name itself. Assume the
|
||||||
|
following policy exists in code::
|
||||||
|
|
||||||
|
from oslo_policy import policy
|
||||||
|
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:post_bar',
|
||||||
|
check_str='role:fizz',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'POST'}]
|
||||||
|
)
|
||||||
|
|
||||||
|
For the sake of consistency, let's say we want to replace ``foo:post_bar``
|
||||||
|
with ``foo:create_bar``, but keep the same ``check_str`` as the default. We
|
||||||
|
can accomplish this by doing::
|
||||||
|
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:post_bar',
|
||||||
|
check_str='role:fizz'
|
||||||
|
)
|
||||||
|
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:fizz',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='foo:create_bar is more consistent',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
|
||||||
|
Finally, let's use DeprecatedRule to break a policy into more granular
|
||||||
|
policies. Let's assume the following policy exists in code::
|
||||||
|
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='role:bazz',
|
||||||
|
description='Create, read, update, or delete a bar.',
|
||||||
|
operations=[
|
||||||
|
{
|
||||||
|
'path': '/v1/bars',
|
||||||
|
'method': 'POST'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'path': '/v1/bars',
|
||||||
|
'method': 'GET'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'path': '/v1/bars/{bar_id}',
|
||||||
|
'method': 'GET'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'path': '/v1/bars/{bar_id}',
|
||||||
|
'method': 'PATCH'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'path': '/v1/bars/{bar_id}',
|
||||||
|
'method': 'DELETE'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
Here we can see the same policy is used to protect multiple operations on
|
||||||
|
bars. This prevents operators from being able to assign different roles to
|
||||||
|
different actions that can be taken on bar. For example, what if an
|
||||||
|
operator wanted to require a less restrictive role or rule to list bars but
|
||||||
|
a more restrictive rule to delete them? The following will introduce a
|
||||||
|
policy that helps achieve that and deprecate the original, overly-broad
|
||||||
|
policy::
|
||||||
|
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='role:bazz'
|
||||||
|
)
|
||||||
|
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:bang',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='foo:create_bar is more granular than foo:bar',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:list_bars',
|
||||||
|
check_str='role:bazz',
|
||||||
|
description='List bars.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'GET'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='foo:list_bars is more granular than foo:bar',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:get_bar',
|
||||||
|
check_str='role:bazz',
|
||||||
|
description='Get a bar.',
|
||||||
|
operations=[{'path': '/v1/bars/{bar_id}', 'method': 'GET'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='foo:get_bar is more granular than foo:bar',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:update_bar',
|
||||||
|
check_str='role:bang',
|
||||||
|
description='Update a bar.',
|
||||||
|
operations=[{'path': '/v1/bars/{bar_id}', 'method': 'PATCH'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='foo:update_bar is more granular than foo:bar',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name='foo:delete_bar',
|
||||||
|
check_str='role:bang',
|
||||||
|
description='Delete a bar.',
|
||||||
|
operations=[{'path': '/v1/bars/{bar_id}', 'method': 'DELETE'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='foo:delete_bar is more granular than foo:bar',
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
|
||||||
|
.. versionchanged 1.29
|
||||||
|
Added *DeprecatedRule* object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, check_str):
|
||||||
|
"""Construct a DeprecatedRule object.
|
||||||
|
|
||||||
|
:param name: the policy name
|
||||||
|
:param check_str: the value of the policy's check string
|
||||||
|
"""
|
||||||
|
self.name = name
|
||||||
|
self.check_str = check_str
|
||||||
|
@ -841,6 +841,161 @@ class RuleDefaultTestCase(base.PolicyBaseTestCase):
|
|||||||
self.assertNotEqual(opt1, opt2)
|
self.assertNotEqual(opt1, opt2)
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentedRuleDefaultDeprecationTestCase(base.PolicyBaseTestCase):
|
||||||
|
|
||||||
|
def test_deprecate_a_policy_check_string(self):
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:fizz'
|
||||||
|
)
|
||||||
|
|
||||||
|
rule_list = [policy.DocumentedRuleDefault(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:bang',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='"role:bang" is a better default',
|
||||||
|
deprecated_since='N'
|
||||||
|
)]
|
||||||
|
enforcer = policy.Enforcer(self.conf)
|
||||||
|
enforcer.register_defaults(rule_list)
|
||||||
|
expected_msg = (
|
||||||
|
'Policy "foo:create_bar":"role:fizz" was deprecated in N in favor '
|
||||||
|
'of "foo:create_bar":"role:bang". Reason: "role:bang" is a better '
|
||||||
|
'default. Either ensure your deployment is ready for the new '
|
||||||
|
'default or copy/paste the deprecated policy into your policy '
|
||||||
|
'file and maintain it manually.'
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch('warnings.warn') as mock_warn:
|
||||||
|
enforcer.load_rules()
|
||||||
|
mock_warn.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
|
def test_deprecate_a_policy_name(self):
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='role:baz'
|
||||||
|
)
|
||||||
|
|
||||||
|
rule_list = [policy.DocumentedRuleDefault(
|
||||||
|
name='foo:create_bar',
|
||||||
|
check_str='role:baz',
|
||||||
|
description='Create a bar.',
|
||||||
|
operations=[{'path': '/v1/bars/', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason=(
|
||||||
|
'"foo:bar" is not granular enough. If your deployment has '
|
||||||
|
'overridden "foo:bar", ensure you override the new policies '
|
||||||
|
'with same role or rule. Not doing this will require the '
|
||||||
|
'service to assume the new defaults for "foo:bar:create", '
|
||||||
|
'"foo:bar:update", "foo:bar:list", and "foo:bar:delete", '
|
||||||
|
'which might be backwards incompatible for your deployment'
|
||||||
|
),
|
||||||
|
deprecated_since='N'
|
||||||
|
)]
|
||||||
|
expected_msg = (
|
||||||
|
'Policy "foo:bar":"role:baz" was deprecated in N in favor of '
|
||||||
|
'"foo:create_bar":"role:baz". Reason: "foo:bar" is not granular '
|
||||||
|
'enough. If your deployment has overridden "foo:bar", ensure you '
|
||||||
|
'override the new policies with same role or rule. Not doing this '
|
||||||
|
'will require the service to assume the new defaults for '
|
||||||
|
'"foo:bar:create", "foo:bar:update", "foo:bar:list", and '
|
||||||
|
'"foo:bar:delete", which might be backwards incompatible for your '
|
||||||
|
'deployment. Either ensure your deployment is ready for the new '
|
||||||
|
'default or copy/paste the deprecated policy into your policy '
|
||||||
|
'file and maintain it manually.'
|
||||||
|
)
|
||||||
|
|
||||||
|
rules = jsonutils.dumps({'foo:bar': 'role:bang'})
|
||||||
|
self.create_config_file('policy.json', rules)
|
||||||
|
enforcer = policy.Enforcer(self.conf)
|
||||||
|
enforcer.register_defaults(rule_list)
|
||||||
|
|
||||||
|
with mock.patch('warnings.warn') as mock_warn:
|
||||||
|
enforcer.load_rules(True)
|
||||||
|
mock_warn.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
|
def test_deprecate_a_policy_for_removal(self):
|
||||||
|
rule_list = [policy.DocumentedRuleDefault(
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='role:baz',
|
||||||
|
description='Create a foo.',
|
||||||
|
operations=[{'path': '/v1/foos/', 'method': 'POST'}],
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason=(
|
||||||
|
'"foo:bar" is no longer a policy used by the service'
|
||||||
|
),
|
||||||
|
deprecated_since='N'
|
||||||
|
)]
|
||||||
|
expected_msg = (
|
||||||
|
'Policy "foo:bar":"role:baz" was deprecated for removal in N. '
|
||||||
|
'Reason: "foo:bar" is no longer a policy used by the service. Its '
|
||||||
|
'value may be silently ignored in the future.'
|
||||||
|
)
|
||||||
|
rules = jsonutils.dumps({'foo:bar': 'role:bang'})
|
||||||
|
self.create_config_file('policy.json', rules)
|
||||||
|
enforcer = policy.Enforcer(self.conf)
|
||||||
|
enforcer.register_defaults(rule_list)
|
||||||
|
|
||||||
|
with mock.patch('warnings.warn') as mock_warn:
|
||||||
|
enforcer.load_rules()
|
||||||
|
mock_warn.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
|
def test_deprecated_policy_for_removal_must_include_deprecated_since(self):
|
||||||
|
self.assertRaises(
|
||||||
|
ValueError,
|
||||||
|
policy.DocumentedRuleDefault,
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='rule:baz',
|
||||||
|
description='Create a foo.',
|
||||||
|
operations=[{'path': '/v1/foos/', 'method': 'POST'}],
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_reason='Some reason.'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_deprecated_policy_must_include_deprecated_since(self):
|
||||||
|
deprecated_rule = policy.DeprecatedRule(
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='rule:baz'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
ValueError,
|
||||||
|
policy.DocumentedRuleDefault,
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='rule:baz',
|
||||||
|
description='Create a foo.',
|
||||||
|
operations=[{'path': '/v1/foos/', 'method': 'POST'}],
|
||||||
|
deprecated_rule=deprecated_rule,
|
||||||
|
deprecated_reason='Some reason.'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_deprecated_rule_requires_deprecated_rule_object(self):
|
||||||
|
self.assertRaises(
|
||||||
|
ValueError,
|
||||||
|
policy.DocumentedRuleDefault,
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='rule:baz',
|
||||||
|
description='Create a foo.',
|
||||||
|
operations=[{'path': '/v1/foos/', 'method': 'POST'}],
|
||||||
|
deprecated_rule='foo:bar',
|
||||||
|
deprecated_reason='Some reason.'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_deprecated_policy_must_include_deprecated_reason(self):
|
||||||
|
self.assertRaises(
|
||||||
|
ValueError,
|
||||||
|
policy.DocumentedRuleDefault,
|
||||||
|
name='foo:bar',
|
||||||
|
check_str='rule:baz',
|
||||||
|
description='Create a foo.',
|
||||||
|
operations=[{'path': '/v1/foos/', 'method': 'POST'}],
|
||||||
|
deprecated_for_removal=True,
|
||||||
|
deprecated_since='N'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DocumentedRuleDefaultTestCase(base.PolicyBaseTestCase):
|
class DocumentedRuleDefaultTestCase(base.PolicyBaseTestCase):
|
||||||
|
|
||||||
def test_contain_operations(self):
|
def test_contain_operations(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user