Image cleanup added to settings

This commit is contained in:
Kasra Bigdeli
2018-05-12 14:18:15 -07:00
parent d56c31909a
commit 609361a62d
9 changed files with 470 additions and 9 deletions
+75 -1
View File
@@ -419,6 +419,30 @@ angular
callback(null);
});
},
getUnusedImages: function (mostRecentLimit, callback) {
$http
.get(BASE_API + 'user/appDefinitions/unusedImages/?mostRecentLimit=' + (mostRecentLimit || '0'), createConfig())
.then(
function (response) {
callback(response.data);
},
function () {
callback(null);
});
},
deleteImages: function (imageIds, callback) {
$http
.post(BASE_API + 'user/appDefinitions/deleteImages', {
imageIds: imageIds
}, createConfig())
.then(
function (response) {
callback(response.data);
},
function () {
callback(null);
});
},
deleteApp: function (appName, callback) {
$http
.post(BASE_API + 'user/appDefinitions/delete', {
@@ -1679,14 +1703,18 @@ angular.module('RDash')
'$uibModal', '$state', SettingsCtrl]);
function SettingsCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
apiManager, captainToast, $uibModal, $state) {
apiManager, captainToast, $uibModal, $state) {
$scope.loadingState = {
changePassword: {},
versionCheck: {},
cleanUp: {},
nginxConfig: {}
};
$scope.cleanUp = {};
$scope.cleanUp.mostRecentLimit = 2;
$scope.passwords = {};
(function ChangePasswordCtrl() {
@@ -1751,6 +1779,52 @@ function SettingsCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
}());
(function cleanUpCtrl() {
$scope.onGetOldImagesClicked = function () {
$scope.loadingState.cleanUp.enabled = true;
apiManager.getUnusedImages($scope.cleanUp.mostRecentLimit, function (data) {
if (captainToast.showErrorToastIfNeeded(data)) {
return;
}
$scope.loadingState.cleanUp.enabled = false;
var unusedImages = data.data.unusedImages;
for (var i = 0; i < unusedImages.length; i++) {
unusedImages[i].remove = true;
}
$scope.cleanUp.unusedImages = unusedImages;
});
}
$scope.onRemoveImagesClicked = function () {
$scope.loadingState.cleanUp.enabled = true;
var imageIds = [];
for (var i = 0; i < $scope.cleanUp.unusedImages.length; i++) {
if ($scope.cleanUp.unusedImages[i].remove) {
imageIds.push($scope.cleanUp.unusedImages[i].id);
}
}
apiManager.deleteImages(imageIds, function (data) {
if (captainToast.showErrorToastIfNeeded(data)) {
return;
}
$scope.loadingState.cleanUp.enabled = false;
// forcing a refresh
$scope.onGetOldImagesClicked();
});
}
}());
(function nginxConfigCtrl() {
function getInitialValues() {
+68 -1
View File
@@ -90,7 +90,7 @@
<hr>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="col-lg-6">
<rd-widget>
@@ -154,4 +154,71 @@
</rd-widget-body>
</rd-widget>
</div>
<div class="col-lg-6">
<rd-widget>
<rd-widget-header icon="fa-cloud-download" title="Disk Cleanup">
</rd-widget-header>
<rd-widget-body loading="loadingState.cleanUp.enabled">
<div class="row">
<div class="col-md-6">
<div class="input-group" uib-tooltip="for example, enter 2 in order to exclude 2 most recent builds during clean-up">
<span class="input-group-addon">Keep Most Recent:</span>
<input type="number" class="form-control" ng-model="cleanUp.mostRecentLimit">
</div>
</div>
<div class="pull-right">
<button type="button" class="btn btn-default" ng-click="onGetOldImagesClicked()">
<span>
<i class="fa fa-refresh"></i>
</span> &nbsp; Fetch Old Images</button>
</div>
</div>
<div class="row" ng-show="cleanUp.unusedImages">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th style="padding-left: 35px">Remove?</th>
<th class="text-center">Image Name</th>
<th class="text-center">ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="img in cleanUp.unusedImages">
<td class="text-center">
<input type="checkbox" ng-model="img.remove">
</td>
<td class="text-center">{{img.description}}</td>
<td class="text-center">{{img.id}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row" ng-show="cleanUp.unusedImages">
<div class="pull-right">
<button type="button" class="btn btn-primary" ng-click="onRemoveImagesClicked()">
<span>
<i class="fa fa-trash"></i>
</span> &nbsp; Remove Images</button>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>
+14 -5
View File
@@ -130,17 +130,26 @@ class DataStore {
getImageName(authObj, appName, version) {
if (version === 0) {
version = '0';
}
let authPrefix = '';
if (authObj) {
authPrefix = authObj.serveraddress + '/' + authObj.username + '/';
}
return authPrefix + 'img-' + this.getNameSpace() + '--' + appName + (version ? (':' + version) : '');
return authPrefix + this.getImageNameWithoutAuthObj(appName, version);
}
getImageNameWithoutAuthObj(appName, version) {
if (version === 0) {
version = '0';
}
return this.getImageNameBase() + appName + (version ? (':' + version) : '');
}
getImageNameBase() {
return 'img-' + this.getNameSpace() + '--';
}
getRootDomain() {
+31
View File
@@ -1318,6 +1318,37 @@ class DockerApi {
})
}
deleteImages(imageIds) {
const self = this;
return Promise.resolve()
.then(function () {
let promises = [];
for (let i = 0; i < imageIds.length; i++) {
const imageId = imageIds[i];
let p = self.dockerode.getImage(imageId).remove()
.catch(function (err) {
Logger.e(err);
});
promises.push(p);
}
return Promise.all(promises);
})
}
getImages() {
const self = this;
return Promise.resolve()
.then(function () {
return self.dockerode.listImages();
})
}
getNodeLables(nodeId) {
const self = this;
return self.dockerode
@@ -32,6 +32,71 @@ router.get('/oneclickapps', function (req, res, next) {
});
});
// unused iamges
router.get('/unusedImages', function (req, res, next) {
let dataStore = res.locals.user.dataStore;
let serviceManager = res.locals.user.serviceManager;
Promise.resolve()
.then(function () {
let mostRecentLimit = Number(req.query.mostRecentLimit || '0');
return serviceManager.getUnusedImages(mostRecentLimit);
})
.then(function (unusedImages) {
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, "Unused images retrieved.");
baseApi.data = {};
baseApi.data.unusedImages = unusedImages;
res.send(baseApi);
})
.catch(function (error) {
Logger.e(error);
if (error && error.captainErrorType) {
res.send(new BaseApi(error.captainErrorType, error.apiMessage));
return;
}
res.sendStatus(500);
});
});
// unused iamges
router.post('/deleteImages', function (req, res, next) {
let dataStore = res.locals.user.dataStore;
let serviceManager = res.locals.user.serviceManager;
let imageIds = req.body.imageIds || [];
Promise.resolve()
.then(function () {
return serviceManager.deleteImages(imageIds);
})
.then(function () {
let baseApi = new BaseApi(ApiStatusCodes.STATUS_OK, "Images Deleted.");
res.send(baseApi);
})
.catch(function (error) {
Logger.e(error);
if (error && error.captainErrorType) {
res.send(new BaseApi(error.captainErrorType, error.apiMessage));
return;
}
res.sendStatus(500);
});
});
// Get All App Definitions
router.get('/', function (req, res, next) {
+70
View File
@@ -670,6 +670,76 @@ class ServiceManager {
});
}
getUnusedImages(mostRecentLimit) {
Logger.d('Getting unused images, excluding most recent ones: ' + mostRecentLimit);
const self = this;
let dockerApi = this.dockerApi;
let dataStore = this.dataStore;
let allImages = null;
return Promise.resolve()
.then(function () {
return dockerApi
.getImages();
})
.then(function (images) {
allImages = images;
return dataStore.getAppDefinitions()
})
.then(function (apps) {
let unusedImages = [];
for (let i = 0; i < allImages.length; i++) {
const img = allImages[i];
let imageInUse = false;
for (let j = 0; j < img.RepoTags.length; j++) {
const repoTag = img.RepoTags[j];
Object.keys(apps).forEach(function (key, index) {
let app = apps[key];
app.appName = key;
for (let k = 0; k < (mostRecentLimit + 1); k++) {
if (repoTag.indexOf(dataStore.getImageNameWithoutAuthObj(app.appName, Number(app.deployedVersion) - k)) >= 0) {
imageInUse = true;
}
}
});
}
if (!imageInUse) {
unusedImages.push({
id: img.Id,
description: (img.RepoTags && img.RepoTags.length) ? img.RepoTags[0] : 'untagged'
})
}
}
return unusedImages;
});
}
deleteImages(imageIds){
Logger.d('Deleting images...');
const self = this;
let dockerApi = this.dockerApi;
let dataStore = this.dataStore;
let allImages = null;
return Promise.resolve()
.then(function () {
return dockerApi
.deleteImages(imageIds);
});
}
ensureServiceInitedAndUpdated(appName, version) {
Logger.d('Ensure service inited and Updated for: ' + appName);
+24
View File
@@ -331,6 +331,30 @@
callback(null);
});
},
getUnusedImages: function (mostRecentLimit, callback) {
$http
.get(BASE_API + 'user/appDefinitions/unusedImages/?mostRecentLimit=' + (mostRecentLimit || '0'), createConfig())
.then(
function (response) {
callback(response.data);
},
function () {
callback(null);
});
},
deleteImages: function (imageIds, callback) {
$http
.post(BASE_API + 'user/appDefinitions/deleteImages', {
imageIds: imageIds
}, createConfig())
.then(
function (response) {
callback(response.data);
},
function () {
callback(null);
});
},
deleteApp: function (appName, callback) {
$http
.post(BASE_API + 'user/appDefinitions/delete', {
@@ -4,14 +4,18 @@ angular.module('RDash')
'$uibModal', '$state', SettingsCtrl]);
function SettingsCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
apiManager, captainToast, $uibModal, $state) {
apiManager, captainToast, $uibModal, $state) {
$scope.loadingState = {
changePassword: {},
versionCheck: {},
cleanUp: {},
nginxConfig: {}
};
$scope.cleanUp = {};
$scope.cleanUp.mostRecentLimit = 2;
$scope.passwords = {};
(function ChangePasswordCtrl() {
@@ -76,6 +80,52 @@ function SettingsCtrl($scope, $cookieStore, $rootScope, pageDefinitions,
}());
(function cleanUpCtrl() {
$scope.onGetOldImagesClicked = function () {
$scope.loadingState.cleanUp.enabled = true;
apiManager.getUnusedImages($scope.cleanUp.mostRecentLimit, function (data) {
if (captainToast.showErrorToastIfNeeded(data)) {
return;
}
$scope.loadingState.cleanUp.enabled = false;
var unusedImages = data.data.unusedImages;
for (var i = 0; i < unusedImages.length; i++) {
unusedImages[i].remove = true;
}
$scope.cleanUp.unusedImages = unusedImages;
});
}
$scope.onRemoveImagesClicked = function () {
$scope.loadingState.cleanUp.enabled = true;
var imageIds = [];
for (var i = 0; i < $scope.cleanUp.unusedImages.length; i++) {
if ($scope.cleanUp.unusedImages[i].remove) {
imageIds.push($scope.cleanUp.unusedImages[i].id);
}
}
apiManager.deleteImages(imageIds, function (data) {
if (captainToast.showErrorToastIfNeeded(data)) {
return;
}
$scope.loadingState.cleanUp.enabled = false;
// forcing a refresh
$scope.onGetOldImagesClicked();
});
}
}());
(function nginxConfigCtrl() {
function getInitialValues() {
+72 -1
View File
@@ -91,7 +91,7 @@
<hr/>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="col-lg-6">
<rd-widget>
@@ -158,4 +158,75 @@
</rd-widget-body>
</rd-widget>
</div>
<div class="col-lg-6">
<rd-widget>
<rd-widget-header icon="fa-cloud-download" title="Disk Cleanup">
</rd-widget-header>
<rd-widget-body loading="loadingState.cleanUp.enabled">
<div class="row">
<div class="col-md-6">
<div class="input-group"
uib-tooltip="for example, enter 2 in order to exclude 2 most recent builds during clean-up">
<span class="input-group-addon">Keep Most Recent:</span>
<input type="number" class="form-control"
ng-model="cleanUp.mostRecentLimit">
</div>
</div>
<div class="pull-right">
<button type="button" class="btn btn-default"
ng-click="onGetOldImagesClicked()">
<span>
<i class="fa fa-refresh"></i>
</span> &nbsp; Fetch Old Images</button>
</div>
</div>
<div class="row" ng-show="cleanUp.unusedImages">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th style="padding-left: 35px;">Remove?</th>
<th class="text-center">Image Name</th>
<th class="text-center">ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="img in cleanUp.unusedImages">
<td class="text-center">
<input type="checkbox" ng-model="img.remove">
</td>
<td class="text-center">{{img.description}}</td>
<td class="text-center">{{img.id}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row" ng-show="cleanUp.unusedImages">
<div class="pull-right">
<button type="button" class="btn btn-primary"
ng-click="onRemoveImagesClicked()">
<span>
<i class="fa fa-trash"></i>
</span> &nbsp; Remove Images</button>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>