diff --git a/app/scripts/modules/core/delivery/executionGroup/execution/execution.directive.js b/app/scripts/modules/core/delivery/executionGroup/execution/execution.directive.js index 3e672862b1e..dbaafa7db55 100644 --- a/app/scripts/modules/core/delivery/executionGroup/execution/execution.directive.js +++ b/app/scripts/modules/core/delivery/executionGroup/execution/execution.directive.js @@ -90,7 +90,7 @@ module.exports = angular header: 'Really stop execution of ' + this.execution.name + '?', buttonText: 'Stop running ' + this.execution.name, destructive: false, - submitMethod: () => executionService.cancelExecution(this.execution.id) + submitMethod: () => executionService.cancelExecution(this.application, this.execution.id) }); }; diff --git a/app/scripts/modules/core/delivery/service/execution.service.js b/app/scripts/modules/core/delivery/service/execution.service.js index 5a339907b5a..aff43809d48 100644 --- a/app/scripts/modules/core/delivery/service/execution.service.js +++ b/app/scripts/modules/core/delivery/service/execution.service.js @@ -2,13 +2,12 @@ let angular = require('angular'); module.exports = angular.module('spinnaker.core.delivery.executions.service', [ - require('angular-ui-router'), require('../../scheduler/scheduler.service.js'), require('../../cache/deckCacheFactory.js'), require('../../utils/appendTransform.js'), require('./executions.transformer.service.js') ]) - .factory('executionService', function($stateParams, $http, $timeout, $q, $log, + .factory('executionService', function($http, $timeout, $q, $log, scheduler, settings, appendTransform, executionsTransformer) { const activeStatuses = ['RUNNING', 'SUSPENDED', 'NOT_STARTED']; @@ -72,27 +71,47 @@ module.exports = angular.module('spinnaker.core.delivery.executions.service', [ }); } - function cancelExecution(executionId, applicationName) { + function waitUntilPipelineIsCancelled(application, executionId) { + return getRunningExecutions(application.name).then((executions) => { + let match = executions.filter((execution) => execution.id === executionId); + let deferred = $q.defer(); + if (match && !match.length) { + application.reloadExecutions().then(deferred.resolve); + return deferred.promise; + } + return $timeout(() => waitUntilPipelineIsCancelled(application, executionId), 1000); + }); + } + + function waitUntilPipelineIsDeleted(application, executionId) { + + return getExecutions(application.name).then((executions) => { + let match = executions.filter((execution) => execution.id === executionId); + let deferred = $q.defer(); + if (match && !match.length) { + application.reloadExecutions().then(deferred.resolve); + return deferred.promise; + } + return $timeout(() => waitUntilPipelineIsDeleted(application, executionId), 1000); + }); + } + + function cancelExecution(application, executionId) { var deferred = $q.defer(); $http({ method: 'PUT', url: [ settings.gateUrl, 'applications', - (applicationName || $stateParams.application), // TODO: remove stateParams + application.name, 'pipelines', executionId, 'cancel', ].join('/') }).then( - function() { - scheduler.scheduleImmediate(); - deferred.resolve(); - }, - function(exception) { - deferred.reject(exception && exception.data ? exception.message : null); - } - ); + () => waitUntilPipelineIsCancelled(application, executionId).then(deferred.resolve), + (exception) => deferred.reject(exception && exception.data ? exception.message : null) + ); return deferred.promise; } @@ -106,12 +125,8 @@ module.exports = angular.module('spinnaker.core.delivery.executions.service', [ executionId, ].join('/') }).then( - function() { - application.reloadExecutions().then(deferred.resolve); - }, - function(exception) { - deferred.reject(exception && exception.data ? exception.data.message : null); - } + () => waitUntilPipelineIsDeleted(application, executionId).then(deferred.resolve), + (exception) => deferred.reject(exception && exception.data ? exception.data.message : null) ); return deferred.promise; } diff --git a/app/scripts/modules/core/delivery/service/execution.service.spec.js b/app/scripts/modules/core/delivery/service/execution.service.spec.js index b76f3188bd8..2095229185e 100644 --- a/app/scripts/modules/core/delivery/service/execution.service.spec.js +++ b/app/scripts/modules/core/delivery/service/execution.service.spec.js @@ -2,11 +2,11 @@ describe('Service: executionService', function () { - //NOTE: This is only testing the service dependencies. Please add more tests. - var executionService; var $httpBackend; var settings; + var timeout; + var $q; beforeEach( window.module( @@ -15,10 +15,12 @@ describe('Service: executionService', function () { ); beforeEach( - window.inject(function (_executionService_, _$httpBackend_, _settings_) { + window.inject(function (_executionService_, _$httpBackend_, _settings_, _$timeout_, _$q_) { executionService = _executionService_; $httpBackend = _$httpBackend_; settings = _settings_; + timeout = _$timeout_; + $q = _$q_; }) ); @@ -27,8 +29,75 @@ describe('Service: executionService', function () { $httpBackend.verifyNoOutstandingRequest(); }); - it('should instantiate the controller', function () { - expect(executionService).toBeDefined(); + describe('cancelling pipeline', function () { + it('should wait until pipeline is not running, then resolve', function () { + let completed = false; + let executionId = 'abc'; + let cancelUrl = [ settings.gateUrl, 'applications', 'deck', 'pipelines', executionId, 'cancel' ].join('/'); + let checkUrl = [ settings.gateUrl, 'applications', 'deck', 'pipelines' ].join('/') + .concat('?statuses=RUNNING,SUSPENDED,NOT_STARTED'); + let application = { name: 'deck', reloadExecutions: () => $q.when(null) }; + + $httpBackend.expectPUT(cancelUrl).respond(200, []); + $httpBackend.expectGET(checkUrl).respond(200, [{id: executionId}]); + + executionService.cancelExecution(application, executionId).then(() => completed = true); + $httpBackend.flush(); + expect(completed).toBe(false); + + $httpBackend.expectGET(checkUrl).respond(200, [{id: 'some-other-execution'}]); + timeout.flush(); + $httpBackend.flush(); + expect(completed).toBe(true); + }); + + it('should propagate rejection from failed cancel', function () { + let failed = false; + let executionId = 'abc'; + let cancelUrl = [ settings.gateUrl, 'applications', 'deck', 'pipelines', executionId, 'cancel' ].join('/'); + let application = { name: 'deck', reloadExecutions: () => $q.when(null) }; + + $httpBackend.expectPUT(cancelUrl).respond(500, []); + + executionService.cancelExecution(application, executionId).then(angular.noop, () => failed = true); + $httpBackend.flush(); + expect(failed).toBe(true); + }); + }); + + describe('deleting pipeline', function () { + it('should wait until pipeline is missing, then resolve', function () { + let completed = false; + let executionId = 'abc'; + let deleteUrl = [ settings.gateUrl, 'pipelines', executionId ].join('/'); + let checkUrl = [ settings.gateUrl, 'applications', 'deck', 'pipelines' ].join('/'); + let application = { name: 'deck', reloadExecutions: () => $q.when(null) }; + + $httpBackend.expectDELETE(deleteUrl).respond(200, []); + $httpBackend.expectGET(checkUrl).respond(200, [{id: executionId}]); + + executionService.deleteExecution(application, executionId).then(() => completed = true); + $httpBackend.flush(); + expect(completed).toBe(false); + + $httpBackend.expectGET(checkUrl).respond(200, [{id: 'some-other-execution'}]); + timeout.flush(); + $httpBackend.flush(); + expect(completed).toBe(true); + }); + + it('should propagate rejection from failed delete', function () { + let failed = false; + let executionId = 'abc'; + let deleteUrl = [ settings.gateUrl, 'pipelines', executionId ].join('/'); + let application = { name: 'deck', reloadExecutions: () => $q.when(null) }; + + $httpBackend.expectDELETE(deleteUrl).respond(500, []); + + executionService.deleteExecution(application, executionId).then(angular.noop, () => failed = true); + $httpBackend.flush(); + expect(failed).toBe(true); + }); }); describe('when fetching pipelines', function () {