201 lines
5.9 KiB
JavaScript
Executable File
201 lines
5.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
require('supererror')({ splatchError: true });
|
|
|
|
var superagent = require('superagent'),
|
|
async = require('async'),
|
|
yesno = require('yesno'),
|
|
program = require('commander');
|
|
|
|
var DIGITALOCEAN = 'https://api.digitalocean.com/v2';
|
|
|
|
var ENVIRONMENTS = {
|
|
'dev': {
|
|
tag: 'dev',
|
|
releaseUrl: 'https://s3.amazonaws.com/dev-cloudron-releases/versions.json',
|
|
digitalOceanToken: process.env.DIGITAL_OCEAN_TOKEN_DEV
|
|
},
|
|
'staging': {
|
|
tag: 'staging',
|
|
releaseUrl: 'https://s3.amazonaws.com/staging-cloudron-releases/versions.json',
|
|
digitalOceanToken: process.env.DIGITAL_OCEAN_TOKEN_STAGING
|
|
},
|
|
'prod': {
|
|
tag: 'prod',
|
|
releaseUrl: 'https://s3.amazonaws.com/prod-cloudron-releases/versions.json',
|
|
digitalOceanToken: process.env.DIGITAL_OCEAN_TOKEN_PROD
|
|
}
|
|
};
|
|
|
|
function deleteImage(image, token, callback) {
|
|
var url = DIGITALOCEAN + '/images/' + image.id;
|
|
|
|
console.log('Deleting image %s ...', image.name);
|
|
|
|
superagent.del(url).set('Authorization', 'Bearer ' + token).end(function (error, result) {
|
|
if (error || result.error) return callback(error || result.error);
|
|
|
|
callback(null);
|
|
});
|
|
}
|
|
|
|
function getImages(token, callback) {
|
|
var images = [];
|
|
var nextPage = DIGITALOCEAN + '/images?private=true';
|
|
|
|
async.doWhilst(function (callback) {
|
|
superagent.get(nextPage).set('Authorization', 'Bearer ' + token).end(function (error, result) {
|
|
if (error || result.error) return callback(error || result.error);
|
|
|
|
nextPage = (result.body.links && result.body.links.pages && nextPage !== result.body.links.pages.next) ? result.body.links.pages.next : null;
|
|
images = images.concat(result.body.images);
|
|
|
|
callback(null);
|
|
});
|
|
}, function () { return !!nextPage; }, function (error) {
|
|
if (error) return callback(error);
|
|
|
|
callback(null, images);
|
|
});
|
|
}
|
|
|
|
function printImages(env, images, releases, callback) {
|
|
console.log('');
|
|
console.log('%s:', env.tag);
|
|
console.log('');
|
|
|
|
var imageRegExp = new RegExp('box-(?:dev|staging|prod)-[0-9,a-f]{7}-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}');
|
|
|
|
images.forEach(function (image) {
|
|
if (!imageRegExp.test(image.name)) return;
|
|
|
|
var releaseNumber = [];
|
|
for (var release in releases) {
|
|
if (releases.hasOwnProperty(release)) {
|
|
if (releases[release].imageId === image.id) {
|
|
releaseNumber.push(release);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(' %s : %s %s\t[%s]', image.id, image.name, releaseNumber.length ? releaseNumber.join(', ') : ' ', image.regions);
|
|
});
|
|
|
|
console.log('');
|
|
|
|
callback(null);
|
|
}
|
|
|
|
function deleteUnusedImages(env, images, releases, callback) {
|
|
console.log('');
|
|
console.log('Cleanup images on %s:', env.tag);
|
|
|
|
var imagesToCleanup = [];
|
|
|
|
var imageRegExp = new RegExp('box-(?:dev|staging|prod)-[0-9,a-f]{7}-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}');
|
|
|
|
images.forEach(function (image) {
|
|
if (!imageRegExp.test(image.name)) return;
|
|
|
|
for (var release in releases) {
|
|
if (releases.hasOwnProperty(release)) {
|
|
if (releases[release].imageId === image.id) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we reached here so no release found
|
|
imagesToCleanup.push(image);
|
|
});
|
|
|
|
if (imagesToCleanup.length === 0) {
|
|
console.log('All images belong to a release.');
|
|
return callback(null);
|
|
}
|
|
|
|
imagesToCleanup.forEach(function (image) {
|
|
console.log(' %s : %s [%s]', image.id, image.name, image.regions);
|
|
});
|
|
|
|
console.log('');
|
|
|
|
yesno.ask('Do you want to delete those images? [y/N]', false, function (ok) {
|
|
if (ok) {
|
|
async.each(imagesToCleanup, function (image, callback) {
|
|
deleteImage(image, env.digitalOceanToken, callback);
|
|
}, callback);
|
|
return;
|
|
}
|
|
|
|
callback(null);
|
|
});
|
|
}
|
|
|
|
function exit(error) {
|
|
if (error) console.error(error);
|
|
process.exit(error ? 1 : 0);
|
|
}
|
|
|
|
function listImages(options) {
|
|
var env = ENVIRONMENTS[options.env];
|
|
|
|
getImages(env.digitalOceanToken, function (error, result) {
|
|
if (error) return exit(error);
|
|
|
|
var images = result;
|
|
superagent.get(env.releaseUrl).end(function (error, result) {
|
|
if (error || result.error) return exit(error || result.error);
|
|
|
|
// we get it as text
|
|
var releases = JSON.parse(result.text);
|
|
|
|
printImages(env, images, releases, exit);
|
|
});
|
|
});
|
|
}
|
|
|
|
function cleanupImages(options) {
|
|
var env = ENVIRONMENTS[options.env];
|
|
|
|
getImages(env.digitalOceanToken, function (error, result) {
|
|
if (error) return exit(error);
|
|
|
|
var images = result;
|
|
superagent.get(env.releaseUrl).end(function (error, result) {
|
|
if (error || result.error) return exit(error || result.error);
|
|
|
|
// we get it as text
|
|
var releases = JSON.parse(result.text);
|
|
|
|
deleteUnusedImages(env, images, releases, exit);
|
|
});
|
|
});
|
|
}
|
|
|
|
program.version('0.0.1');
|
|
|
|
program.command('list')
|
|
.option('--env <dev/staging/prod>', 'Environment (dev/staging/prod)', 'dev')
|
|
.description('List images of environment')
|
|
.action(listImages);
|
|
|
|
program.command('cleanup')
|
|
.option('--env <dev/staging/prod>', 'Environment (dev/staging/prod)', 'dev')
|
|
.description('Cleanup images of environment')
|
|
.action(cleanupImages);
|
|
|
|
program.parse(process.argv);
|
|
|
|
if (!process.argv.slice(2).length) {
|
|
program.outputHelp();
|
|
} else { // https://github.com/tj/commander.js/issues/338
|
|
var knownCommand = program.commands.some(function (command) { return command._name === process.argv[2]; });
|
|
if (!knownCommand) {
|
|
console.error('Unknown command: ' + process.argv[2]);
|
|
process.exit(1);
|
|
}
|
|
}
|