Add separate policy for updates with no changes
Allow operators to set a different (presumably looser) policy on PATCH updates that don't make any changes to the stack, but just retrigger a new update traversal (that will result in e.g. replacing any unhealthy resources). Change-Id: Id29e7ec7f6cf127177ea7ab29127b0568afaa18b Task: 37305
This commit is contained in:
parent
34ecc26a11
commit
af7f8e380a
@ -166,6 +166,17 @@ class InstantiationData(object):
|
|||||||
params = self.data.items()
|
params = self.data.items()
|
||||||
return dict((k, v) for k, v in params if k not in self.PARAMS)
|
return dict((k, v) for k, v in params if k not in self.PARAMS)
|
||||||
|
|
||||||
|
def no_change(self):
|
||||||
|
assert self.patch
|
||||||
|
return ((self.template() is None) and
|
||||||
|
(self.environment() ==
|
||||||
|
environment_format.default_for_missing({})) and
|
||||||
|
(not self.files()) and
|
||||||
|
(not self.environment_files()) and
|
||||||
|
(self.files_container() is None) and
|
||||||
|
(not any(k != rpc_api.PARAM_EXISTING
|
||||||
|
for k in self.args().keys())))
|
||||||
|
|
||||||
|
|
||||||
class StackController(object):
|
class StackController(object):
|
||||||
"""WSGI controller for stacks resource in Heat v1 API.
|
"""WSGI controller for stacks resource in Heat v1 API.
|
||||||
@ -496,7 +507,8 @@ class StackController(object):
|
|||||||
|
|
||||||
raise exc.HTTPAccepted()
|
raise exc.HTTPAccepted()
|
||||||
|
|
||||||
@util.registered_identified_stack
|
@util.no_policy_enforce
|
||||||
|
@util._identified_stack
|
||||||
def update_patch(self, req, identity, body):
|
def update_patch(self, req, identity, body):
|
||||||
"""Update an existing stack with a new template.
|
"""Update an existing stack with a new template.
|
||||||
|
|
||||||
@ -504,6 +516,17 @@ class StackController(object):
|
|||||||
Add the flag patch to the args so the engine code can distinguish
|
Add the flag patch to the args so the engine code can distinguish
|
||||||
"""
|
"""
|
||||||
data = InstantiationData(body, patch=True)
|
data = InstantiationData(body, patch=True)
|
||||||
|
_target = {"project_id": req.context.tenant_id}
|
||||||
|
|
||||||
|
policy_act = 'update_no_change' if data.no_change() else 'update_patch'
|
||||||
|
allowed = req.context.policy.enforce(
|
||||||
|
context=req.context,
|
||||||
|
action=policy_act,
|
||||||
|
scope=self.REQUEST_SCOPE,
|
||||||
|
target=_target,
|
||||||
|
is_registered_policy=True)
|
||||||
|
if not allowed:
|
||||||
|
raise exc.HTTPForbidden()
|
||||||
|
|
||||||
args = self.prepare_args(data, is_update=True)
|
args = self.prepare_args(data, is_update=True)
|
||||||
self.rpc_client.update_stack(
|
self.rpc_client.update_stack(
|
||||||
|
@ -48,6 +48,24 @@ def registered_policy_enforce(handler):
|
|||||||
return handle_stack_method
|
return handle_stack_method
|
||||||
|
|
||||||
|
|
||||||
|
def no_policy_enforce(handler):
|
||||||
|
"""Decorator that does *not* enforce policies.
|
||||||
|
|
||||||
|
Checks the path matches the request context.
|
||||||
|
|
||||||
|
This is a handler method decorator.
|
||||||
|
"""
|
||||||
|
@functools.wraps(handler)
|
||||||
|
def handle_stack_method(controller, req, tenant_id, **kwargs):
|
||||||
|
if req.context.tenant_id != tenant_id and not (
|
||||||
|
req.context.is_admin or
|
||||||
|
req.context.system_scope == all):
|
||||||
|
raise exc.HTTPForbidden()
|
||||||
|
return handler(controller, req, **kwargs)
|
||||||
|
|
||||||
|
return handle_stack_method
|
||||||
|
|
||||||
|
|
||||||
def registered_identified_stack(handler):
|
def registered_identified_stack(handler):
|
||||||
"""Decorator that passes a stack identifier instead of path components.
|
"""Decorator that passes a stack identifier instead of path components.
|
||||||
|
|
||||||
|
@ -471,6 +471,18 @@ stacks_policies = [
|
|||||||
],
|
],
|
||||||
deprecated_rule=deprecated_update_patch
|
deprecated_rule=deprecated_update_patch
|
||||||
),
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name=POLICY_ROOT % 'update_no_change',
|
||||||
|
check_str='rule:%s' % (POLICY_ROOT % 'update_patch'),
|
||||||
|
scope_types=['system', 'project'],
|
||||||
|
description='Update stack (PATCH) with no changes.',
|
||||||
|
operations=[
|
||||||
|
{
|
||||||
|
'path': '/v1/{tenant_id}/stacks/{stack_name}/{stack_id}',
|
||||||
|
'method': 'PATCH'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name=POLICY_ROOT % 'preview_update',
|
name=POLICY_ROOT % 'preview_update',
|
||||||
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
|
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Operators can now set a separate ``stacks:update_no_change`` policy for
|
||||||
|
PATCH updates that don't modify the stack, independently of the existing
|
||||||
|
``stacks:update_patch`` policy.
|
Loading…
Reference in New Issue
Block a user