summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZane Bitter <zbitter@redhat.com>2019-10-25 17:07:12 -0400
committerramishra <ramishra@redhat.com>2021-03-15 17:38:14 +0530
commitaf7f8e380a7425c65ac88f5bc68e57757d9b2cc1 (patch)
tree819e3fee67e3e52a6d7e5738c8ac576cf4152bac
parent34ecc26a116ee603a3b40d51c00962d2bd26d5bc (diff)
downloadheat-af7f8e380a7425c65ac88f5bc68e57757d9b2cc1.tar.gz
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
-rw-r--r--heat/api/openstack/v1/stacks.py25
-rw-r--r--heat/api/openstack/v1/util.py18
-rw-r--r--heat/policies/stacks.py12
-rw-r--r--releasenotes/notes/update-no-change-policy-728ed49e6b81da53.yaml6
4 files changed, 60 insertions, 1 deletions
diff --git a/heat/api/openstack/v1/stacks.py b/heat/api/openstack/v1/stacks.py
index d84adbc92..1da826509 100644
--- a/heat/api/openstack/v1/stacks.py
+++ b/heat/api/openstack/v1/stacks.py
@@ -166,6 +166,17 @@ class InstantiationData(object):
params = self.data.items()
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):
"""WSGI controller for stacks resource in Heat v1 API.
@@ -496,7 +507,8 @@ class StackController(object):
raise exc.HTTPAccepted()
- @util.registered_identified_stack
+ @util.no_policy_enforce
+ @util._identified_stack
def update_patch(self, req, identity, body):
"""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
"""
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)
self.rpc_client.update_stack(
diff --git a/heat/api/openstack/v1/util.py b/heat/api/openstack/v1/util.py
index 70a22d420..46ae955eb 100644
--- a/heat/api/openstack/v1/util.py
+++ b/heat/api/openstack/v1/util.py
@@ -48,6 +48,24 @@ def registered_policy_enforce(handler):
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):
"""Decorator that passes a stack identifier instead of path components.
diff --git a/heat/policies/stacks.py b/heat/policies/stacks.py
index 5591ba5ff..cebcf5af5 100644
--- a/heat/policies/stacks.py
+++ b/heat/policies/stacks.py
@@ -472,6 +472,18 @@ stacks_policies = [
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(
name=POLICY_ROOT % 'preview_update',
check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
scope_types=['system', 'project'],
diff --git a/releasenotes/notes/update-no-change-policy-728ed49e6b81da53.yaml b/releasenotes/notes/update-no-change-policy-728ed49e6b81da53.yaml
new file mode 100644
index 000000000..cc237e6ac
--- /dev/null
+++ b/releasenotes/notes/update-no-change-policy-728ed49e6b81da53.yaml
@@ -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.