node-triton#78 'triton image delete IMAGE'
This commit is contained in:
parent
879e86efa3
commit
810f0add56
@ -1,7 +1,8 @@
|
|||||||
# node-triton changelog
|
# node-triton changelog
|
||||||
|
|
||||||
## 4.3.2 (not yet released)
|
## 4.4.0 (not yet released)
|
||||||
|
|
||||||
|
- #78 `triton image delete IMAGE`
|
||||||
- #79 Fix `triton instance get NAME` to make sure it gets the `dns_names` CNS
|
- #79 Fix `triton instance get NAME` to make sure it gets the `dns_names` CNS
|
||||||
field.
|
field.
|
||||||
- PUBAPI-1227: Note that `triton image list` doesn't include Docker images, at
|
- PUBAPI-1227: Note that `triton image list` doesn't include Docker images, at
|
||||||
|
@ -539,6 +539,26 @@ CloudApi.prototype.getImage = function getImage(opts, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an image by id.
|
||||||
|
* <http://apidocs.joyent.com/cloudapi/#DeleteImage>
|
||||||
|
*
|
||||||
|
* @param {String} id (required) The image id.
|
||||||
|
* @param {Function} callback of the form `function (err, res)`
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.deleteImage = function deleteImage(id, callback) {
|
||||||
|
var self = this;
|
||||||
|
assert.uuid(id, 'id');
|
||||||
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
|
var opts = {
|
||||||
|
path: format('/%s/images/%s', self.account, id),
|
||||||
|
method: 'DELETE'
|
||||||
|
};
|
||||||
|
this._request(opts, function (err, req, res) {
|
||||||
|
callback(err, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <http://apidocs.joyent.com/cloudapi/#CreateImageFromMachine>
|
* <http://apidocs.joyent.com/cloudapi/#CreateImageFromMachine>
|
||||||
@ -684,16 +704,16 @@ CloudApi.prototype.getMachine = function getMachine(opts, cb) {
|
|||||||
/**
|
/**
|
||||||
* delete a machine by id.
|
* delete a machine by id.
|
||||||
*
|
*
|
||||||
* @param {String} uuid (required) The machine id.
|
* @param {String} id (required) The machine id.
|
||||||
* @param {Function} callback of the form `function (err, res)`
|
* @param {Function} callback of the form `function (err, res)`
|
||||||
*/
|
*/
|
||||||
CloudApi.prototype.deleteMachine = function deleteMachine(uuid, callback) {
|
CloudApi.prototype.deleteMachine = function deleteMachine(id, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
assert.string(uuid, 'uuid');
|
assert.uuid(id, 'id');
|
||||||
assert.func(callback, 'callback');
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
var opts = {
|
var opts = {
|
||||||
path: format('/%s/machines/%s', self.account, uuid),
|
path: format('/%s/machines/%s', self.account, id),
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
};
|
};
|
||||||
this._request(opts, function (err, req, res) {
|
this._request(opts, function (err, req, res) {
|
||||||
|
155
lib/do_image/do_delete.js
Normal file
155
lib/do_image/do_delete.js
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton image delete ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var format = require('util').format;
|
||||||
|
var vasync = require('vasync');
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
var errors = require('../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_delete(subcmd, opts, args, cb) {
|
||||||
|
var self = this;
|
||||||
|
if (opts.help) {
|
||||||
|
return this.do_help('help', {}, [subcmd], cb);
|
||||||
|
} else if (args.length < 1) {
|
||||||
|
return cb(new errors.UsageError('missing IMAGE arg(s)'));
|
||||||
|
}
|
||||||
|
var ids = args;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
|
/*
|
||||||
|
* Lookup images, if not given UUIDs: we'll need to do it anyway
|
||||||
|
* for the DeleteImage call(s), and doing so explicitly here allows
|
||||||
|
* us to emit better output.
|
||||||
|
*/
|
||||||
|
function getImgs(ctx, next) {
|
||||||
|
ctx.imgFromId = {};
|
||||||
|
ctx.missingIds = [];
|
||||||
|
// TODO: this should have a concurrency
|
||||||
|
vasync.forEachParallel({
|
||||||
|
inputs: ids,
|
||||||
|
func: function getImg(id, nextImg) {
|
||||||
|
if (common.isUUID(id)) {
|
||||||
|
// TODO: get info from cache if we have it
|
||||||
|
ctx.imgFromId[id] = {
|
||||||
|
id: id,
|
||||||
|
_repr: id
|
||||||
|
};
|
||||||
|
nextImg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: allow use of cache here
|
||||||
|
self.top.tritonapi.getImage(id, function (err, img) {
|
||||||
|
if (err) {
|
||||||
|
if (err.statusCode === 404) {
|
||||||
|
ctx.missingIds.push(id);
|
||||||
|
nextImg();
|
||||||
|
} else {
|
||||||
|
nextImg(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.imgFromId[img.id] = img;
|
||||||
|
img._repr = format('%s (%s@%s)', img.id,
|
||||||
|
img.name, img.version);
|
||||||
|
nextImg();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
|
||||||
|
function errOnMissingIds(ctx, next) {
|
||||||
|
if (ctx.missingIds.length === 1) {
|
||||||
|
next(new errors.TritonError('no such image: '
|
||||||
|
+ ctx.missingIds[0]));
|
||||||
|
} else if (ctx.missingIds.length > 1) {
|
||||||
|
next(new errors.TritonError('no such images: '
|
||||||
|
+ ctx.missingIds.join(', ')));
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
function confirm(ctx, next) {
|
||||||
|
if (opts.force) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(ctx.imgFromId);
|
||||||
|
var msg;
|
||||||
|
if (keys.length === 1) {
|
||||||
|
msg = format('Delete image %s? [y/n] ',
|
||||||
|
ctx.imgFromId[keys[0]]._repr);
|
||||||
|
} else {
|
||||||
|
msg = format('Delete %d images? [y/n] ', keys.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
common.promptYesNo({msg: msg}, function (answer) {
|
||||||
|
if (answer !== 'y') {
|
||||||
|
console.error('Aborting');
|
||||||
|
next(true); // early abort signal
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
function deleteThem(ctx, next) {
|
||||||
|
// TODO: forEachParallel with concurrency
|
||||||
|
vasync.forEachPipeline({
|
||||||
|
inputs: Object.keys(ctx.imgFromId),
|
||||||
|
func: function deleteOne(id, nextOne) {
|
||||||
|
self.top.tritonapi.cloudapi.deleteImage(id, function (err) {
|
||||||
|
if (!err) {
|
||||||
|
console.log('Deleted image %s',
|
||||||
|
ctx.imgFromId[id]._repr);
|
||||||
|
}
|
||||||
|
nextOne(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, next);
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
do_delete.help = [
|
||||||
|
/* BEGIN JSSTYLED */
|
||||||
|
'Delete one or more images.',
|
||||||
|
'',
|
||||||
|
'Usage:',
|
||||||
|
' {{name}} delete IMAGE [IMAGE...]',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'Where "IMAGE" is an image ID (a full UUID), an image name (selects the',
|
||||||
|
'latest, by "published_at", image with that name), an image "name@version"',
|
||||||
|
'(selects latest match by "published_at"), or an image short ID (ID prefix).'
|
||||||
|
/* END JSSTYLED */
|
||||||
|
].join('\n');
|
||||||
|
do_delete.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['force', 'f'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Skip confirmation of delete.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
do_delete.aliases = ['rm'];
|
||||||
|
module.exports = do_delete;
|
@ -23,7 +23,7 @@ function ImageCLI(top) {
|
|||||||
name: top.name + ' image',
|
name: top.name + ' image',
|
||||||
/* BEGIN JSSTYLED */
|
/* BEGIN JSSTYLED */
|
||||||
desc: [
|
desc: [
|
||||||
'List, get, create and update Triton images.'
|
'List, get, create and manage Triton images.'
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
/* END JSSTYLED */
|
/* END JSSTYLED */
|
||||||
helpOpts: {
|
helpOpts: {
|
||||||
@ -34,6 +34,7 @@ function ImageCLI(top) {
|
|||||||
'list',
|
'list',
|
||||||
'get',
|
'get',
|
||||||
'create',
|
'create',
|
||||||
|
'delete',
|
||||||
'wait'
|
'wait'
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@ -48,6 +49,7 @@ ImageCLI.prototype.init = function init(opts, args, cb) {
|
|||||||
ImageCLI.prototype.do_list = require('./do_list');
|
ImageCLI.prototype.do_list = require('./do_list');
|
||||||
ImageCLI.prototype.do_get = require('./do_get');
|
ImageCLI.prototype.do_get = require('./do_get');
|
||||||
ImageCLI.prototype.do_create = require('./do_create');
|
ImageCLI.prototype.do_create = require('./do_create');
|
||||||
|
ImageCLI.prototype.do_delete = require('./do_delete');
|
||||||
ImageCLI.prototype.do_wait = require('./do_wait');
|
ImageCLI.prototype.do_wait = require('./do_wait');
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,16 +362,20 @@ TritonApi.prototype.getImage = function getImage(opts, cb) {
|
|||||||
var s = opts.name.split('@');
|
var s = opts.name.split('@');
|
||||||
var name = s[0];
|
var name = s[0];
|
||||||
var version = s[1];
|
var version = s[1];
|
||||||
|
var nameSelector;
|
||||||
|
|
||||||
var listOpts = {
|
var listOpts = {
|
||||||
// Explicitly include inactive images.
|
// Explicitly include inactive images.
|
||||||
state: 'all'
|
state: 'all'
|
||||||
};
|
};
|
||||||
if (version) {
|
if (version) {
|
||||||
|
nameSelector = name + '@' + version;
|
||||||
listOpts.name = name;
|
listOpts.name = name;
|
||||||
listOpts.version = version;
|
listOpts.version = version;
|
||||||
// XXX This is bogus now?
|
// XXX This is bogus now?
|
||||||
listOpts.useCache = opts.useCache;
|
listOpts.useCache = opts.useCache;
|
||||||
|
} else {
|
||||||
|
nameSelector = name;
|
||||||
}
|
}
|
||||||
this.cloudapi.listImages(listOpts, function (err, imgs) {
|
this.cloudapi.listImages(listOpts, function (err, imgs) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -399,11 +403,13 @@ TritonApi.prototype.getImage = function getImage(opts, cb) {
|
|||||||
cb(null, shortIdMatches[0]);
|
cb(null, shortIdMatches[0]);
|
||||||
} else if (shortIdMatches.length === 0) {
|
} else if (shortIdMatches.length === 0) {
|
||||||
cb(new errors.ResourceNotFoundError(format(
|
cb(new errors.ResourceNotFoundError(format(
|
||||||
'no image with name or short id "%s" was found', name)));
|
'no image with %s or short id "%s" was found',
|
||||||
|
nameSelector, name)));
|
||||||
} else {
|
} else {
|
||||||
cb(new errors.ResourceNotFoundError(
|
cb(new errors.ResourceNotFoundError(
|
||||||
format('no image with name "%s" was found '
|
format('no image with %s "%s" was found '
|
||||||
+ 'and "%s" is an ambiguous short id', name)));
|
+ 'and "%s" is an ambiguous short id',
|
||||||
|
nameSelector, name, name)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "triton",
|
"name": "triton",
|
||||||
"description": "Joyent Triton CLI and client (https://www.joyent.com/triton)",
|
"description": "Joyent Triton CLI and client (https://www.joyent.com/triton)",
|
||||||
"version": "4.3.2",
|
"version": "4.4.0",
|
||||||
"author": "Joyent (joyent.com)",
|
"author": "Joyent (joyent.com)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert-plus": "0.2.0",
|
"assert-plus": "0.2.0",
|
||||||
|
Reference in New Issue
Block a user