diff --git a/CHANGELOG.md b/CHANGELOG.md index 2452d43..8d0a060 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [Next Version - available as `edge`] +- New: Ability to delete multiple apps at once - Improved: Now the redirects include the path - Improved: SSH key handling to avoid human mistakes diff --git a/src/routes/user/apps/appdefinition/AppDefinitionRouter.ts b/src/routes/user/apps/appdefinition/AppDefinitionRouter.ts index 1548699..50453b6 100644 --- a/src/routes/user/apps/appdefinition/AppDefinitionRouter.ts +++ b/src/routes/user/apps/appdefinition/AppDefinitionRouter.ts @@ -249,14 +249,24 @@ router.post('/delete/', function (req, res, next) { const serviceManager = InjectionExtractor.extractUserFromInjected(res).user.serviceManager - const appName = req.body.appName - const volumes = req.body.volumes || [] + const appName: string = req.body.appName + const volumes: string[] = req.body.volumes || [] + const appNames: string[] = req.body.appNames || [] + const appsToDelete: string[] = appNames.length ? appNames : [appName] Logger.d(`Deleting app started: ${appName}`) Promise.resolve() .then(function () { - return serviceManager.removeApp(appName) + if (appNames.length > 0 && appName) { + throw ApiStatusCodes.createError( + ApiStatusCodes.ILLEGAL_OPERATION, + 'Either appName or appNames should be provided' + ) + } + }) + .then(function () { + return serviceManager.removeApps(appsToDelete) }) .then(function () { return Utils.getDelayedPromise(volumes.length ? 12000 : 0) @@ -265,7 +275,7 @@ router.post('/delete/', function (req, res, next) { return serviceManager.removeVolsSafe(volumes) }) .then(function (failedVolsToRemoved) { - Logger.d(`AppName is deleted: ${appName}`) + Logger.d(`Successfully deleted: ${appsToDelete.join(', ')}`) if (failedVolsToRemoved.length) { const returnVal = new BaseApi( diff --git a/src/user/ServiceManager.ts b/src/user/ServiceManager.ts index 8ecba0e..b20f5b4 100644 --- a/src/user/ServiceManager.ts +++ b/src/user/ServiceManager.ts @@ -8,12 +8,12 @@ import Logger from '../utils/Logger' import Utils from '../utils/Utils' import Authenticator from './Authenticator' import DockerRegistryHelper from './DockerRegistryHelper' +import ImageMaker, { BuildLogsManager } from './ImageMaker' import { EventLogger } from './events/EventLogger' import { CapRoverEventFactory, CapRoverEventType, } from './events/ICapRoverEvent' -import ImageMaker, { BuildLogsManager } from './ImageMaker' import DomainResolveChecker from './system/DomainResolveChecker' import LoadBalancerManager from './system/LoadBalancerManager' import requireFromString = require('require-from-string') @@ -456,40 +456,51 @@ class ServiceManager { }) } - removeApp(appName: string) { - Logger.d(`Removing service for: ${appName}`) + removeApps(appNames: string[]) { + Logger.d(`Removing service for: ${appNames.join(', ')}`) const self = this - const serviceName = this.dataStore - .getAppsDataStore() - .getServiceName(appName) - const dockerApi = this.dockerApi - const dataStore = this.dataStore + const removeAppPromise = function (appName: string) { + const serviceName = self.dataStore + .getAppsDataStore() + .getServiceName(appName) + const dockerApi = self.dockerApi + const dataStore = self.dataStore - return Promise.resolve() - .then(function () { - return self.ensureNotBuilding(appName) - }) - .then(function () { - Logger.d(`Check if service is running: ${serviceName}`) - return dockerApi.isServiceRunningByName(serviceName) - }) - .then(function (isRunning) { - if (isRunning) { - return dockerApi.removeServiceByName(serviceName) - } else { - Logger.w( - `Cannot delete service... It is not running: ${serviceName}` - ) - return true - } - }) - .then(function () { - return dataStore.getAppsDataStore().deleteAppDefinition(appName) - }) - .then(function () { - return self.reloadLoadBalancer() - }) + return Promise.resolve() + .then(function () { + return self.ensureNotBuilding(appName) + }) + .then(function () { + Logger.d(`Check if service is running: ${serviceName}`) + return dockerApi.isServiceRunningByName(serviceName) + }) + .then(function (isRunning) { + if (isRunning) { + return dockerApi.removeServiceByName(serviceName) + } else { + Logger.w( + `Cannot delete service... It is not running: ${serviceName}` + ) + return true + } + }) + .then(function () { + return dataStore + .getAppsDataStore() + .deleteAppDefinition(appName) + }) + .then(function () { + return self.reloadLoadBalancer() + }) + } + + const promises = [] + for (const appName of appNames) { + promises.push(removeAppPromise(appName)) + } + + return Promise.all(promises) } removeVolsSafe(volumes: string[]) { diff --git a/src/user/system/LoadBalancerManager.ts b/src/user/system/LoadBalancerManager.ts index dd5c52c..bb6062e 100644 --- a/src/user/system/LoadBalancerManager.ts +++ b/src/user/system/LoadBalancerManager.ts @@ -89,14 +89,14 @@ class LoadBalancerManager { consumeQueueIfAnyInNginxReloadQueue() { const self = this - const q = self.requestedReloadPromises.pop() - - if (!q) { + if (self.reloadInProcess) { + Logger.d('NGINX Reload already in process, Bouncing off...') return } - if (self.reloadInProcess) { - Logger.d('NGINX Reload already in process, Bouncing off...') + const q = self.requestedReloadPromises.pop() + + if (!q) { return }