From 5b5f87b7e358da1c1543dd1ba77d98e13b933cb7 Mon Sep 17 00:00:00 2001 From: Ilya Kutukov Date: Tue, 15 Mar 2016 20:34:07 +0300 Subject: [PATCH] Custom graph support added to the tasks and graph info handlers Following handlers are extended with graph_type query string parameter: /releases//deployment_tasks/ /clusters//deployment_tasks/ /clusters//serialized_tasks/ /clusters//deploy_tasks/graph.gv This parameter allows handler to use deployment graph data from the scope of the given graph type. Change-Id: I0e9af118af9aff57df8efa1e9139134566660c4b Implements: blueprint custom-graph-execution --- nailgun/nailgun/api/v1/handlers/base.py | 3 +- .../nailgun/api/v1/handlers/orchestrator.py | 6 +- nailgun/nailgun/objects/cluster.py | 2 +- nailgun/nailgun/plugins/adapters.py | 16 ++-- nailgun/nailgun/plugins/manager.py | 4 +- .../test_graph_related_handlers.py | 81 +++++++++++++++++++ .../integration/test_orchestrator_handlers.py | 25 ++++++ 7 files changed, 125 insertions(+), 12 deletions(-) diff --git a/nailgun/nailgun/api/v1/handlers/base.py b/nailgun/nailgun/api/v1/handlers/base.py index 9a5cc81c8f..854fd46a5c 100644 --- a/nailgun/nailgun/api/v1/handlers/base.py +++ b/nailgun/nailgun/api/v1/handlers/base.py @@ -602,12 +602,13 @@ class OrchestratorDeploymentTasksHandler(SingleHandler): obj = self.get_object_or_404(self.single, obj_id) end = web.input(end=None).end start = web.input(start=None).start + graph_type = web.input(graph_type=None).graph_type # web.py depends on [] to understand that there will be multiple inputs include = web.input(include=[]).include # merged (cluster + plugins + release) tasks is returned for cluster # but the own release tasks is returned for release - tasks = self.single.get_deployment_tasks(obj) + tasks = self.single.get_deployment_tasks(obj, graph_type=graph_type) if end or start: graph = orchestrator_graph.GraphSolver(tasks) return graph.filter_subgraph( diff --git a/nailgun/nailgun/api/v1/handlers/orchestrator.py b/nailgun/nailgun/api/v1/handlers/orchestrator.py index fccd513390..75a63acf33 100644 --- a/nailgun/nailgun/api/v1/handlers/orchestrator.py +++ b/nailgun/nailgun/api/v1/handlers/orchestrator.py @@ -326,9 +326,10 @@ class TaskDeployGraph(BaseHandler): * 400 (failed to get graph) """ web.header('Content-Type', 'text/vnd.graphviz', unique=True) + graph_type = web.input(graph_type=None).graph_type cluster = self.get_object_or_404(objects.Cluster, cluster_id) - tasks = objects.Cluster.get_deployment_tasks(cluster) + tasks = objects.Cluster.get_deployment_tasks(cluster, graph_type) graph = orchestrator_graph.GraphSolver(tasks) tasks = web.input(tasks=None).tasks @@ -383,12 +384,13 @@ class SerializedTasksHandler(NodesFilterMixin, BaseHandler): self.checked_data(self.validator.validate_placement, data=nodes, cluster=cluster) tasks = web.input(tasks=None).tasks + graph_type = web.input(graph_type=None).graph_type task_ids = [t.strip() for t in tasks.split(',')] if tasks else None try: serialized_tasks = task_based_deployment.TasksSerializer.serialize( cluster, nodes, - objects.Cluster.get_deployment_tasks(cluster), + objects.Cluster.get_deployment_tasks(cluster, graph_type), task_ids=task_ids ) return {'tasks_directory': serialized_tasks[0], diff --git a/nailgun/nailgun/objects/cluster.py b/nailgun/nailgun/objects/cluster.py index e32f82b915..e4a4adbfbc 100644 --- a/nailgun/nailgun/objects/cluster.py +++ b/nailgun/nailgun/objects/cluster.py @@ -1029,7 +1029,7 @@ class Cluster(NailgunObject): # graph types not supported by plugin manager interface yet plugins_deployment_tasks = PluginManager.get_plugins_deployment_tasks( - instance) + instance, graph_type) return cls._merge_tasks_lists([ release_deployment_tasks, diff --git a/nailgun/nailgun/plugins/adapters.py b/nailgun/nailgun/plugins/adapters.py index 29618f6053..6ca6a617fc 100644 --- a/nailgun/nailgun/plugins/adapters.py +++ b/nailgun/nailgun/plugins/adapters.py @@ -23,6 +23,7 @@ from urlparse import urljoin import six import yaml +from nailgun import consts from nailgun.errors import errors from nailgun.logger import logger from nailgun.objects.deployment_graph import DeploymentGraph @@ -123,13 +124,11 @@ class PluginAdapterBase(object): return settings.PLUGINS_SLAVES_SCRIPTS_PATH.format( plugin_name=self.path_name) - # todo(ikutukov): actually getter-setter approach don't allow us to - # work with graph types on plugins level. - # Should be reworked to getters and setters - @property - def deployment_tasks(self): + def get_deployment_tasks(self, graph_type=None): + if graph_type is None: + graph_type = consts.DEFAULT_DEPLOYMENT_GRAPH_TYPE deployment_tasks = [] - graph_instance = DeploymentGraph.get_for_model(self.plugin) + graph_instance = DeploymentGraph.get_for_model(self.plugin, graph_type) if graph_instance: for task in DeploymentGraph.get_tasks(graph_instance): if task.get('parameters'): @@ -138,6 +137,11 @@ class PluginAdapterBase(object): deployment_tasks.append(task) return deployment_tasks + # fixme(ikutukov): this getter only for default graph type, drop in future + @property + def deployment_tasks(self): + return self.get_deployment_tasks() + # it will better to replace to getters and setters @deployment_tasks.setter def deployment_tasks(self, value): diff --git a/nailgun/nailgun/plugins/manager.py b/nailgun/nailgun/plugins/manager.py index 55b4f5b4e9..ce7fa12f64 100644 --- a/nailgun/nailgun/plugins/manager.py +++ b/nailgun/nailgun/plugins/manager.py @@ -204,13 +204,13 @@ class PluginManager(object): return list(all_roles.values()) @classmethod - def get_plugins_deployment_tasks(cls, cluster): + def get_plugins_deployment_tasks(cls, cluster, graph_type=None): deployment_tasks = [] processed_tasks = {} enabled_plugins = ClusterPlugins.get_enabled(cluster.id) for plugin_adapter in map(wrap_plugin, enabled_plugins): - depl_tasks = plugin_adapter.deployment_tasks + depl_tasks = plugin_adapter.get_deployment_tasks(graph_type) for t in depl_tasks: t_id = t['id'] diff --git a/nailgun/nailgun/test/integration/test_graph_related_handlers.py b/nailgun/nailgun/test/integration/test_graph_related_handlers.py index ca046919ca..3a526bb81f 100644 --- a/nailgun/nailgun/test/integration/test_graph_related_handlers.py +++ b/nailgun/nailgun/test/integration/test_graph_related_handlers.py @@ -171,6 +171,34 @@ class TestReleaseGraphHandler(BaseGraphTasksTests, DeploymentTasksTestMixin): self.cluster.release) self.assertEqual(resp.json, release_tasks) + def test_get_custom_deployment_tasks(self): + objects.DeploymentGraph.create_for_model( + {'tasks': [ + { + 'id': 'custom-task', + 'type': 'puppet' + } + ]}, self.cluster.release, 'custom-graph') + + resp = self.app.get( + reverse( + 'ReleaseDeploymentTasksHandler', + kwargs={'obj_id': self.cluster.release_id} + ) + '?graph_type=custom-graph', + headers=self.default_headers + ) + self.assertItemsEqual( + resp.json, + [ + { + 'id': 'custom-task', + 'task_name': 'custom-task', + 'version': '1.0.0', + 'type': 'puppet' + } + ] + ) + def test_upload_deployment_tasks(self): tasks = self.get_correct_tasks() resp = self.app.put( @@ -263,6 +291,34 @@ class TestClusterGraphHandler(BaseGraphTasksTests, DeploymentTasksTestMixin): self.cluster.release) self.assertItemsEqual(resp.json, release_tasks) + def test_get_custom_deployment_tasks(self): + objects.DeploymentGraph.create_for_model( + {'tasks': [ + { + 'id': 'custom-task', + 'type': 'puppet' + } + ]}, self.cluster, 'custom-graph') + + resp = self.app.get( + reverse( + 'ClusterDeploymentTasksHandler', + kwargs={'obj_id': self.cluster.id} + ) + '?graph_type=custom-graph', + headers=self.default_headers + ) + self.assertItemsEqual( + resp.json, + [ + { + 'id': 'custom-task', + 'task_name': 'custom-task', + 'version': '1.0.0', + 'type': 'puppet' + } + ] + ) + def test_upload_deployment_tasks(self): tasks = self.get_correct_tasks() resp = self.app.put( @@ -545,3 +601,28 @@ class TestTaskDeployGraph(BaseGraphTasksTests): ) self.assertEqual(resp.status_code, 400) self.assertIn('Task types nonexistent do not exist', resp.body) + + +class TestTaskDeployCustomGraph(BaseGraphTasksTests): + + content_type = 'text/vnd.graphviz' + + def setUp(self): + super(TestTaskDeployCustomGraph, self).setUp() + self.env.create() + self.cluster = self.env.clusters[-1] + + def test_get_custom_tasks(self): + objects.DeploymentGraph.create_for_model( + {'tasks': [ + {'id': 'pre_deployment', 'type': 'stage'}, + {'id': 'custom-task', 'required_for': ['pre_deployment'], + 'type': 'puppet'}, + ]}, self.cluster, 'custom-graph') + + resp = self.app.get( + reverse('TaskDeployGraph', kwargs={ + 'cluster_id': self.cluster.id, + }) + '?graph_type=custom-graph', + ) + self.assertIn('"custom-task" -> pre_deployment;', resp.body) diff --git a/nailgun/nailgun/test/integration/test_orchestrator_handlers.py b/nailgun/nailgun/test/integration/test_orchestrator_handlers.py index 6f8a99e156..61475096c5 100644 --- a/nailgun/nailgun/test/integration/test_orchestrator_handlers.py +++ b/nailgun/nailgun/test/integration/test_orchestrator_handlers.py @@ -471,6 +471,31 @@ class TestSerializedTasksHandler(BaseIntegrationTest): for task_name, task in six.iteritems(resp.json['tasks_directory']): self.assertIn(task['type'], consts.ORCHESTRATOR_TASK_TYPES) + @patch.object(TaskProcessor, 'ensure_task_based_deploy_allowed') + def test_custom_serialized_tasks_returned(self, _): + objects.DeploymentGraph.create_for_model( + {'tasks': [ + { + 'id': 'first-custom-task', + 'type': 'stage', + 'requires': ['pre_deployment_end'] + }, { + 'id': 'second-custom-task', + 'type': 'stage', + 'requires': ['deploy_start'] + } + ]}, self.cluster, 'custom-graph') + + resp = self.get_serialized_tasks( + self.cluster.id, graph_type=['custom-graph']) + self.assertEqual(resp.status_code, 200) + self.assertIn('tasks_graph', resp.json) + self.assertIn('tasks_directory', resp.json) + tasks_graph = resp.json['tasks_graph'] + expected_tasks = ['first-custom-task', 'second-custom-task'] + self.assertItemsEqual( + [t['id'] for t in tasks_graph['null']], expected_tasks) + @patch.object(TaskProcessor, 'ensure_task_based_deploy_allowed') def test_query_nodes_and_tasks(self, _): not_skipped = ['globals']