From 7e5607b376d04bbfe7ee57c41ab6e70a57bac0ef Mon Sep 17 00:00:00 2001 From: Vitalii Myhal Date: Fri, 5 Feb 2016 12:55:17 -0600 Subject: [PATCH] Add --force flag for `fuel deploy-changes` command The flag allows to apply the changes to the cluster if the cluster is in the operational state. This allows you to deploy the changes without the reprovisioning procedure. Example: `fuel deploy-changes --env --force` Partial-Bug: 1540558 Change-Id: Ibc89fdbfbd0a36a890412cd8e861d35bcf930690 --- nailgun/nailgun/api/v1/handlers/cluster.py | 10 ++++++ nailgun/nailgun/api/v1/urls.py | 3 ++ nailgun/nailgun/task/helpers.py | 8 ++--- nailgun/nailgun/task/manager.py | 24 ++++++++++---- .../test_cluster_changes_handler.py | 33 +++++++++++++++++++ .../test/integration/test_task_managers.py | 16 +++++++++ 6 files changed, 83 insertions(+), 11 deletions(-) diff --git a/nailgun/nailgun/api/v1/handlers/cluster.py b/nailgun/nailgun/api/v1/handlers/cluster.py index 087eae443f..1c29e5024b 100644 --- a/nailgun/nailgun/api/v1/handlers/cluster.py +++ b/nailgun/nailgun/api/v1/handlers/cluster.py @@ -37,6 +37,7 @@ from nailgun.api.v1.validators.cluster import VmwareAttributesValidator from nailgun.logger import logger from nailgun import objects +from nailgun.task.manager import ApplyChangesForceTaskManager from nailgun.task.manager import ApplyChangesTaskManager from nailgun.task.manager import ClusterDeletionManager from nailgun.task.manager import ResetEnvironmentTaskManager @@ -87,6 +88,15 @@ class ClusterChangesHandler(DeferredTaskHandler): validator = ClusterChangesValidator +class ClusterChangesForceRedeployHandler(DeferredTaskHandler): + + log_message = u"Trying to force deployment of the environment '{env_id}'" + log_error = u"Error during execution of a forced deployment task " \ + u"on environment '{env_id}': {error}" + task_manager = ApplyChangesForceTaskManager + validator = ClusterChangesValidator + + class ClusterStopDeploymentHandler(DeferredTaskHandler): log_message = u"Trying to stop deployment on environment '{env_id}'" diff --git a/nailgun/nailgun/api/v1/urls.py b/nailgun/nailgun/api/v1/urls.py index ed87d47dbc..6ed35b7d39 100644 --- a/nailgun/nailgun/api/v1/urls.py +++ b/nailgun/nailgun/api/v1/urls.py @@ -26,6 +26,7 @@ from nailgun.api.v1.handlers.capacity import CapacityLogHandler from nailgun.api.v1.handlers.cluster import ClusterAttributesDefaultsHandler from nailgun.api.v1.handlers.cluster import ClusterAttributesHandler +from nailgun.api.v1.handlers.cluster import ClusterChangesForceRedeployHandler from nailgun.api.v1.handlers.cluster import ClusterChangesHandler from nailgun.api.v1.handlers.cluster import ClusterCollectionHandler from nailgun.api.v1.handlers.cluster import ClusterDeploymentTasksHandler @@ -159,6 +160,8 @@ urls = ( ClusterHandler, r'/clusters/(?P\d+)/changes/?$', ClusterChangesHandler, + r'/clusters/(?P\d+)/changes/redeploy/?$', + ClusterChangesForceRedeployHandler, r'/clusters/(?P\d+)/attributes/?$', ClusterAttributesHandler, r'/clusters/(?P\d+)/attributes/defaults/?$', diff --git a/nailgun/nailgun/task/helpers.py b/nailgun/nailgun/task/helpers.py index ba952fe43d..cc88ae734b 100644 --- a/nailgun/nailgun/task/helpers.py +++ b/nailgun/nailgun/task/helpers.py @@ -153,7 +153,7 @@ class TaskHelper(object): # TODO(aroma): considering moving this code to # nailgun Cluster object's methods @classmethod - def nodes_to_deploy(cls, cluster): + def nodes_to_deploy(cls, cluster, force=False): from nailgun import objects # preventing cycle import error nodes_to_deploy = [] @@ -166,9 +166,9 @@ class TaskHelper(object): cluster_roles.update(node.roles) for node in cluster.nodes: - if any([node.pending_addition, - node.needs_reprovision, - node.needs_redeploy]): + if force or any([node.pending_addition, + node.needs_reprovision, + node.needs_redeploy]): nodes_to_deploy.append(node) for role_name in node.pending_roles: update_required.update( diff --git a/nailgun/nailgun/task/manager.py b/nailgun/nailgun/task/manager.py index f4a75076a6..09fa21d423 100644 --- a/nailgun/nailgun/task/manager.py +++ b/nailgun/nailgun/task/manager.py @@ -162,7 +162,8 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): db().delete(task) db().flush() - def execute(self, nodes_to_provision_deploy=None, deployment_tasks=None): + def execute(self, nodes_to_provision_deploy=None, deployment_tasks=None, + force=False): logger.info( u"Trying to start deployment at cluster '{0}'".format( self.cluster.name or self.cluster.id @@ -178,7 +179,7 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): nodes_to_delete = TaskHelper.nodes_to_delete(self.cluster) nodes_to_deploy = nodes_to_provision_deploy or \ - TaskHelper.nodes_to_deploy(self.cluster) + TaskHelper.nodes_to_deploy(self.cluster, force) nodes_to_provision = TaskHelper.nodes_to_provision(self.cluster) if not any([nodes_to_provision, nodes_to_deploy, nodes_to_delete]): @@ -198,13 +199,14 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): self.cluster.id, supertask.id, nodes_to_provision_deploy=nodes_ids_to_deploy, - deployment_tasks=deployment_tasks + deployment_tasks=deployment_tasks, + force=force ) return supertask def _execute_async(self, supertask_id, deployment_tasks=None, - nodes_to_provision_deploy=None): + nodes_to_provision_deploy=None, force=False): """Function for execute task in the mule :param supertask_id: id of parent task @@ -218,7 +220,8 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): self._execute_async_content( supertask, deployment_tasks=deployment_tasks, - nodes_to_provision_deploy=nodes_to_provision_deploy) + nodes_to_provision_deploy=nodes_to_provision_deploy, + force=force) except Exception as e: logger.exception('Error occurred when running task') data = { @@ -243,7 +246,7 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): return task_deletion def _execute_async_content(self, supertask, deployment_tasks=None, - nodes_to_provision_deploy=None): + nodes_to_provision_deploy=None, force=False): """Processes supertask async in mule :param supertask: SqlAlchemy task object @@ -260,7 +263,7 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): n.needs_reprovision]), nodes_to_deploy) else: - nodes_to_deploy = TaskHelper.nodes_to_deploy(self.cluster) + nodes_to_deploy = TaskHelper.nodes_to_deploy(self.cluster, force) nodes_to_provision = TaskHelper.nodes_to_provision(self.cluster) nodes_to_delete = TaskHelper.nodes_to_delete(self.cluster) @@ -543,6 +546,13 @@ class ApplyChangesTaskManager(TaskManager, DeploymentCheckMixin): db().flush() +class ApplyChangesForceTaskManager(ApplyChangesTaskManager): + + def execute(self, **kwargs): + kwargs['force'] = True + return super(ApplyChangesForceTaskManager, self).execute(**kwargs) + + class SpawnVMsTaskManager(ApplyChangesTaskManager): deployment_type = consts.TASK_NAMES.spawn_vms diff --git a/nailgun/nailgun/test/integration/test_cluster_changes_handler.py b/nailgun/nailgun/test/integration/test_cluster_changes_handler.py index 1cd68660da..1529e3fc11 100644 --- a/nailgun/nailgun/test/integration/test_cluster_changes_handler.py +++ b/nailgun/nailgun/test/integration/test_cluster_changes_handler.py @@ -1824,3 +1824,36 @@ class TestHandlers(BaseIntegrationTest): supertask = self.env.launch_deployment() self.env.wait_ready(supertask, timeout=60) + + @fake_tasks() + def test_force_redeploy_changes(self): + self.env.create( + nodes_kwargs=[{'name': '', 'pending_addition': True}] + ) + + def _send_request(handler): + return self.app.put( + reverse( + handler, + kwargs={'cluster_id': self.env.clusters[0].id} + ), + headers=self.default_headers, + expect_errors=True + ) + + # Initial deployment + task = self.env.launch_deployment() + self.env.wait_ready(task, timeout=60) + + # Trying to redeploy on cluster in the operational state + resp = _send_request('ClusterChangesHandler') + self.assertEqual(resp.status_code, 400) + self.assertEqual(resp.json_body.get('message'), 'No changes to deploy') + + # Trying to force redeploy on cluster in the operational state + resp = _send_request('ClusterChangesForceRedeployHandler') + self.assertEqual(resp.status_code, 202) + self.assertEqual(resp.json_body.get('name'), + consts.TASK_NAMES.deploy) + self.assertEqual(resp.json_body.get('status'), + consts.TASK_STATUSES.pending) diff --git a/nailgun/nailgun/test/integration/test_task_managers.py b/nailgun/nailgun/test/integration/test_task_managers.py index 1df017be74..326e5745e3 100644 --- a/nailgun/nailgun/test/integration/test_task_managers.py +++ b/nailgun/nailgun/test/integration/test_task_managers.py @@ -678,6 +678,22 @@ class TestTaskManagers(BaseIntegrationTest): manager_ = manager.ApplyChangesTaskManager(cluster_db.id) self.assertRaises(errors.WrongNodeStatus, manager_.execute) + @fake_tasks() + def test_force_deploy_changes(self): + self.env.create( + nodes_kwargs=[ + {"status": NODE_STATUSES.ready} + ] + ) + cluster_db = self.env.clusters[0] + objects.Cluster.clear_pending_changes(cluster_db) + manager_ = manager.ApplyChangesForceTaskManager(cluster_db.id) + supertask = manager_.execute() + self.assertEqual(supertask.name, TASK_NAMES.deploy) + self.assertIn(supertask.status, (TASK_STATUSES.pending, + TASK_STATUSES.running, + TASK_STATUSES.ready)) + @fake_tasks() @mock.patch('nailgun.task.manager.tasks.DeletionTask.execute') def test_apply_changes_exception_caught(self, mdeletion_execute):