From 88025d0733f765cad08cef1e643143cfd0f144b9 Mon Sep 17 00:00:00 2001 From: Aaron Tan Date: Mon, 24 Oct 2016 17:32:30 -0400 Subject: [PATCH] Basic verification architecture added. --- awx/api/generics.py | 8 ++++++++ awx/api/views.py | 25 +++++++++++++++++++++++++ awx/main/scheduler/dag_simple.py | 3 +++ 3 files changed, 36 insertions(+) diff --git a/awx/api/generics.py b/awx/api/generics.py index 4c4247b23d..7b58f14edb 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -370,6 +370,9 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView): # Base class for a sublist view that allows for creating subobjects and # attaching/detaching them from the parent. + def is_valid_relation(self, parent, sub): + return None + def get_description_context(self): d = super(SubListCreateAttachDetachAPIView, self).get_description_context() d.update({ @@ -406,6 +409,11 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView): skip_sub_obj_read_check=created): raise PermissionDenied() + # Verify that the relationship to be added is valid. + attach_errors = self.is_valid_relation(parent, sub) + if attach_errors is not None: + return Response(attach_errors, status=status.HTTP_400_BAD_REQUEST) + # Attach the object to the collection. if sub not in relationship.all(): relationship.add(sub) diff --git a/awx/api/views.py b/awx/api/views.py index 699d5c9e81..90621155c5 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -69,6 +69,8 @@ from awx.api.renderers import * # noqa from awx.api.serializers import * # noqa from awx.api.metadata import RoleMetadata from awx.main.consumers import emit_channel_notification +from awx.main.scheduler.dag_simple import SimpleDAG + logger = logging.getLogger('awx.api.views') @@ -2656,6 +2658,29 @@ class WorkflowJobTemplateNodeChildrenBaseList(EnforceParentRelationshipMixin, Su self.check_parent_access(parent) return getattr(parent, self.relationship).all() + def is_valid_relation(self, parent, sub): + workflow_nodes = parent.workflow_job_template.workflow_job_template_nodes.all() + graph = SimpleDAG() + for workflow_node in workflow_nodes: + graph.add_node(workflow_node) + + find = False + for node_type in ['success_nodes', 'failure_nodes', 'always_nodes']: + for workflow_node in workflow_nodes: + related_nodes = getattr(workflow_node, node_type).all() + for related_node in related_nodes: + graph.add_edge(workflow_node, related_node, node_type) + if not find and parent == workflow_node and\ + sub == related_node and self.relationship == node_type: + find = True + if not find: + graph.add_edge(parent, sub, self.relationship) + + if graph.cycle_detected(): + return {"Error": "cycle detected!"} + + return None + class WorkflowJobTemplateNodeSuccessNodesList(WorkflowJobTemplateNodeChildrenBaseList): relationship = 'success_nodes' diff --git a/awx/main/scheduler/dag_simple.py b/awx/main/scheduler/dag_simple.py index aeb0ff759e..3fbb16901f 100644 --- a/awx/main/scheduler/dag_simple.py +++ b/awx/main/scheduler/dag_simple.py @@ -138,3 +138,6 @@ class SimpleDAG(object): roots.append(n) return roots + # TODO + def cycle_detected(self): + return False