From b2b917abf7e8805fc3ea529bb33ab76b75fd73e9 Mon Sep 17 00:00:00 2001 From: Kasra Bigdeli Date: Sun, 31 Mar 2019 12:55:29 -0700 Subject: [PATCH] Added volume deletation to app delete endpoint --- src/api/ApiStatusCodes.ts | 3 +- src/docker/DockerApi.ts | 24 ++++++++++++++ src/routes/apps/AppDefinitionRouter.ts | 19 ++++++++++- src/user/ServiceManager.ts | 46 ++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/api/ApiStatusCodes.ts b/src/api/ApiStatusCodes.ts index 07dc707..ceb5309 100644 --- a/src/api/ApiStatusCodes.ts +++ b/src/api/ApiStatusCodes.ts @@ -28,9 +28,10 @@ class ApiStatusCodes { } } - static readonly STATUS_OK_DEPLOY_STARTED = 101 static readonly STATUS_ERROR_GENERIC = 1000 static readonly STATUS_OK = 100 + static readonly STATUS_OK_DEPLOY_STARTED = 101 + static readonly STATUS_OK_PARTIALLY = 102 static readonly STATUS_ERROR_CAPTAIN_NOT_INITIALIZED = 1001 static readonly STATUS_ERROR_USER_NOT_INITIALIZED = 1101 static readonly STATUS_ERROR_NOT_AUTHORIZED = 1102 diff --git a/src/docker/DockerApi.ts b/src/docker/DockerApi.ts index 7cbfdf4..9531d29 100644 --- a/src/docker/DockerApi.ts +++ b/src/docker/DockerApi.ts @@ -11,6 +11,7 @@ import { VolumesTypes, } from '../models/OtherTypes' import DockerService from '../models/DockerService' +import Utils from '../utils/Utils' const Base64 = Base64Provider.Base64 @@ -754,6 +755,29 @@ class DockerApi { return self.dockerode.getService(serviceName).remove() } + deleteVols(vols: string[]) { + const self = this + + const promises: (() => Promise)[] = [] + const failedVols: string[] = [] + + vols.forEach(v => { + promises.push(function() { + return self.dockerode + .getVolume(v) // + .remove() // { force: true } + .catch(err => { + failedVols.push(v) + }) + }) + }) + + return Utils.runPromises(promises) // + .then(function() { + return failedVols + }) + } + isServiceRunningByName(serviceName: string) { return this.dockerode .getService(serviceName) diff --git a/src/routes/apps/AppDefinitionRouter.ts b/src/routes/apps/AppDefinitionRouter.ts index 218f38c..158de2c 100644 --- a/src/routes/apps/AppDefinitionRouter.ts +++ b/src/routes/apps/AppDefinitionRouter.ts @@ -291,6 +291,7 @@ router.post('/delete/', function(req, res, next) { .serviceManager let appName = req.body.appName + let volumes = req.body.volumes || [] Logger.d('Deleting app started: ' + appName) @@ -299,8 +300,24 @@ router.post('/delete/', function(req, res, next) { return serviceManager.removeApp(appName) }) .then(function() { + return serviceManager.removeVolsSafe(volumes) + }) + .then(function(failedVolsToRemoved) { Logger.d('AppName is deleted: ' + appName) - res.send(new BaseApi(ApiStatusCodes.STATUS_OK, 'App is deleted')) + + if (failedVolsToRemoved.length) { + res.send( + new BaseApi( + ApiStatusCodes.STATUS_OK_PARTIALLY, + 'App is deleted. Some volumes were not safe to delete. Delete skipped for: ' + + failedVolsToRemoved.join(' , ') + ) + ) + } else { + res.send( + new BaseApi(ApiStatusCodes.STATUS_OK, 'App is deleted') + ) + } }) .catch(ApiStatusCodes.createCatcher(res)) }) diff --git a/src/user/ServiceManager.ts b/src/user/ServiceManager.ts index 31419bc..6ce4e7f 100644 --- a/src/user/ServiceManager.ts +++ b/src/user/ServiceManager.ts @@ -332,6 +332,52 @@ class ServiceManager { }) } + removeVolsSafe(volumes: string[]) { + const self = this + + const dockerApi = this.dockerApi + const dataStore = this.dataStore + + const volsFailedToDelete: IHashMapGeneric = {} + + return Promise.resolve() + .then(function() { + return dataStore.getAppsDataStore().getAppDefinitions() + }) + .then(function(apps) { + // Don't even try deleting volumes which are present in other app definitions + Object.keys(apps).forEach(appName => { + const app = apps[appName] + const volsInApp = app.volumes || [] + + volsInApp.forEach(v => { + const volName = v.volumeName + if (!volName) return + if (volumes.indexOf(volName) >= 0) { + volsFailedToDelete[volName] = true + } + }) + }) + + const volumesTryToDelete: string[] = [] + + volumes.forEach(v => { + if (!volsFailedToDelete[v]) { + volumesTryToDelete.push(v) + } + }) + + return dockerApi.deleteVols(volumesTryToDelete) + }) + .then(function(failedVols) { + failedVols.forEach(v => { + volsFailedToDelete[v] = true + }) + + return Object.keys(volsFailedToDelete) + }) + } + getUnusedImages(mostRecentLimit: number) { Logger.d( 'Getting unused images, excluding most recent ones: ' +