PUBAPI-1233/PUBAPI-1234 - add support for --snapshot= flag when

starting a machine, and shortId support for fwrules.
This commit is contained in:
Marsell Kukuljevic 2016-02-05 23:54:13 +11:00
parent f3956df8ce
commit e6334db9e1
12 changed files with 197 additions and 58 deletions

View File

@ -1067,6 +1067,7 @@ function getMachineSnapshot(opts, cb) {
* - {String} name (required) the snapshot's name. * - {String} name (required) the snapshot's name.
* @param {Function} callback of the form f(err, res). * @param {Function} callback of the form f(err, res).
*/ */
CloudApi.prototype.startMachineFromSnapshot =
function startMachineFromSnapshot(opts, cb) { function startMachineFromSnapshot(opts, cb) {
assert.object(opts, 'opts'); assert.object(opts, 'opts');
assert.uuid(opts.id, 'opts.id'); assert.uuid(opts.id, 'opts.id');
@ -1075,13 +1076,13 @@ function startMachineFromSnapshot(opts, cb) {
this._request({ this._request({
method: 'POST', method: 'POST',
path: format('/%s/machine/%s/snapshots/%s', this.account, opts.id, path: format('/%s/machines/%s/snapshots/%s', this.account, opts.id,
opts.name), opts.name),
data: opts data: opts
}, function (err, req, res, body) { }, function (err, req, res, body) {
cb(err, body, res); cb(err, body, res);
}); });
} };
/** /**

View File

@ -314,8 +314,7 @@ function uuidToShortId(s) {
* *
* Short IDs: * Short IDs:
* - UUID prefix * - UUID prefix
* - allow '-' to be elided (to support using containers IDs from * - allow '-' to be elided (to support using containers IDs from docker)
* docker)
* - support docker ID *longer* than a UUID? The curr implementation does. * - support docker ID *longer* than a UUID? The curr implementation does.
*/ */
function normShortId(s) { function normShortId(s) {

View File

@ -12,8 +12,6 @@
var assert = require('assert-plus'); var assert = require('assert-plus');
var format = require('util').format; var format = require('util').format;
var fs = require('fs');
var sshpk = require('sshpk');
var vasync = require('vasync'); var vasync = require('vasync');
var common = require('../common'); var common = require('../common');
@ -60,19 +58,26 @@ function do_delete(subcmd, opts, args, cb) {
}); });
}, },
function deleteThem(_, next) { function deleteThem(_, next) {
vasync.forEachPipeline({ vasync.forEachParallel({
inputs: ruleIds, inputs: ruleIds,
func: function deleteOne(id, nextId) { func: function deleteOne(id, nextId) {
cli.tritonapi.cloudapi.deleteFirewallRule({ cli.tritonapi.getFirewallRule(id, function (err, fwrule) {
id: id
}, function (err) {
if (err) { if (err) {
nextId(err); nextId(err);
return; return;
} }
console.log('Deleted rule %s', id); cli.tritonapi.cloudapi.deleteFirewallRule({
nextId(); id: fwrule.id
}, function (err2) {
if (err2) {
nextId(err2);
return;
}
console.log('Deleted rule %s', id);
nextId();
});
}); });
} }
}, next); }, next);

View File

@ -11,6 +11,7 @@
*/ */
var assert = require('assert-plus'); var assert = require('assert-plus');
var vasync = require('vasync');
var common = require('../common'); var common = require('../common');
var errors = require('../errors'); var errors = require('../errors');
@ -25,27 +26,45 @@ function do_disable(subcmd, opts, args, cb) {
} }
if (args.length === 0) { if (args.length === 0) {
cb(new errors.UsageError('Missing FWRULE-ID argument')); cb(new errors.UsageError('Missing FWRULE-ID argument(s)'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('Incorrect number of arguments'));
return; return;
} }
var id = args[0];
var cli = this.top; var cli = this.top;
// XXX add support for shortId vasync.forEachParallel({
cli.tritonapi.cloudapi.disableFirewallRule(id, function onRule(err) { inputs: args,
if (err) { func: function disableOne(id, nextId) {
cb(err); if (common.isUUID(id)) {
return; enable();
return;
}
// we need to look up the full UUID if the given id is a short id
cli.tritonapi.getFirewallRule(id, function onRule(err, fwrule) {
if (err) {
nextId(err);
return;
}
id = fwrule.id;
enable();
});
function enable() {
cli.tritonapi.cloudapi.disableFirewallRule(id, function (err) {
if (err) {
nextId(err);
return;
}
console.log('Disabled firewall rule %s', id);
nextId();
});
}
} }
}, cb);
console.log('Disabled firewall rule %s', id);
cb();
});
} }

View File

@ -11,6 +11,7 @@
*/ */
var assert = require('assert-plus'); var assert = require('assert-plus');
var vasync = require('vasync');
var common = require('../common'); var common = require('../common');
var errors = require('../errors'); var errors = require('../errors');
@ -25,27 +26,45 @@ function do_enable(subcmd, opts, args, cb) {
} }
if (args.length === 0) { if (args.length === 0) {
cb(new errors.UsageError('Missing FWRULE-ID argument')); cb(new errors.UsageError('Missing FWRULE-ID argument(s)'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('Incorrect number of arguments'));
return; return;
} }
var id = args[0];
var cli = this.top; var cli = this.top;
// XXX add support for shortId vasync.forEachParallel({
cli.tritonapi.cloudapi.enableFirewallRule(id, function onRule(err) { inputs: args,
if (err) { func: function enableOne(id, nextId) {
cb(err); if (common.isUUID(id)) {
return; enable();
return;
}
// we need to look up the full UUID if the given id is a short id
cli.tritonapi.getFirewallRule(id, function onRule(err, fwrule) {
if (err) {
nextId(err);
return;
}
id = fwrule.id;
enable();
});
function enable() {
cli.tritonapi.cloudapi.enableFirewallRule(id, function (err) {
if (err) {
nextId(err);
return;
}
console.log('Enabled firewall rule %s', id);
nextId();
});
}
} }
}, cb);
console.log('Enabled firewall rule %s', id);
cb();
});
} }
@ -60,7 +79,7 @@ do_enable.help = [
'Enable a specific firewall rule.', 'Enable a specific firewall rule.',
'', '',
'Usage:', 'Usage:',
' {{name}} enable FWRULE-ID', ' {{name}} enable FWRULE-ID [FWRULE-ID...]',
'', '',
'{{options}}' '{{options}}'
].join('\n'); ].join('\n');

View File

@ -36,8 +36,7 @@ function do_get(subcmd, opts, args, cb) {
var id = args[0]; var id = args[0];
var cli = this.top; var cli = this.top;
// XXX add support for shortId cli.tritonapi.getFirewallRule(id, function onRule(err, fwrule) {
cli.tritonapi.cloudapi.getFirewallRule(id, function onRule(err, fwrule) {
if (err) { if (err) {
cb(err); cb(err);
return; return;

View File

@ -58,7 +58,7 @@ function do_list(subcmd, opts, args, cb) {
if (columns.indexOf('shortid') !== -1) { if (columns.indexOf('shortid') !== -1) {
rules.forEach(function (rule) { rules.forEach(function (rule) {
rule.shortid = common.normShortId(rule.id); rule.shortid = common.uuidToShortId(rule.id);
}); });
} }

View File

@ -106,6 +106,13 @@ function do_update(subcmd, opts, args, cb) {
function validateIt(ctx, next) { function validateIt(ctx, next) {
var keys = Object.keys(ctx.data); var keys = Object.keys(ctx.data);
if (keys.length === 0) {
console.log('No fields given for firewall rule update');
next();
return;
}
for (var i = 0; i < keys.length; i++) { for (var i = 0; i < keys.length; i++) {
var key = keys[i]; var key = keys[i];
var value = ctx.data[key]; var value = ctx.data[key];
@ -129,24 +136,38 @@ function do_update(subcmd, opts, args, cb) {
next(); next();
}, },
function updateAway(ctx, next) { // we need to look up the full UUID if the given id is a short id
var keys = Object.keys(ctx.data); function getFullId(ctx, next) {
if (keys.length === 0) { if (common.isUUID(id)) {
console.log('No fields given for firewall rule update'); ctx.data.id = id;
next(); next();
return; return;
} }
ctx.data.id = id; tritonapi.getFirewallRule(id, function onRule(err, fwrule) {
tritonapi.cloudapi.updateFirewallRule(ctx.data, function (err) {
if (err) { if (err) {
next(err); next(err);
return; return;
} }
ctx.data.id = fwrule.id;
next();
});
},
function updateAway(ctx, next) {
var data = ctx.data;
tritonapi.cloudapi.updateFirewallRule(data, function (err) {
if (err) {
next(err);
return;
}
delete data.id;
console.log('Updated firewall rule %s (fields: %s)', id, console.log('Updated firewall rule %s (fields: %s)', id,
keys.join(', ')); Object.keys(data).join(', '));
next(); next();
}); });

View File

@ -66,6 +66,14 @@ function gen_do_ACTION(opts) {
} }
]; ];
if (action === 'start') {
do_ACTION.options.push({
names: ['snapshot'],
type: 'string',
help: 'Name of snapshot to start machine with.'
});
}
return do_ACTION; return do_ACTION;
} }
@ -77,7 +85,8 @@ function _doTheAction(action, subcmd, opts, args, callback) {
var command, state; var command, state;
switch (action) { switch (action) {
case 'start': case 'start':
command = 'startMachine'; command = opts.snapshot ? 'startMachineFromSnapshot' :
'startMachine';
state = 'running'; state = 'running';
break; break;
case 'stop': case 'stop':
@ -126,7 +135,12 @@ function _doTheAction(action, subcmd, opts, args, callback) {
// called when "uuid" is set // called when "uuid" is set
function done() { function done() {
self.top.tritonapi.cloudapi[command](uuid, var cOpts = uuid;
if (command === 'startMachineFromSnapshot') {
cOpts = { id: uuid, name: opts.snapshot };
}
self.top.tritonapi.cloudapi[command](cOpts,
function (err, body, res) { function (err, body, res) {
if (err) { if (err) {

View File

@ -30,7 +30,9 @@ function SnapshotCLI(top) {
'list', 'list',
'get', 'get',
'delete' 'delete'
] ],
helpBody: 'Machines can be rolled back to a snapshot using\n' +
'`triton instance start --snapshot=SNAPSHOT-NAME`'
}); });
} }
util.inherits(SnapshotCLI, Cmdln); util.inherits(SnapshotCLI, Cmdln);

View File

@ -221,7 +221,7 @@ TritonApi.prototype._cacheGetJson = function _cacheGetJson(key, cb) {
* *
* @param opts {Object} Optional. * @param opts {Object} Optional.
* - useCache {Boolean} Default false. Whether to use Triton's local cache. * - useCache {Boolean} Default false. Whether to use Triton's local cache.
* Note that the *currently* implementation will only use the cache * Note that the *current* implementation will only use the cache
* when there are no filter options. * when there are no filter options.
* - ... all other cloudapi ListImages options per * - ... all other cloudapi ListImages options per
* <https://apidocs.joyent.com/cloudapi/#ListImages> * <https://apidocs.joyent.com/cloudapi/#ListImages>
@ -537,6 +537,53 @@ TritonApi.prototype.getNetwork = function getNetwork(name, cb) {
}; };
/**
* Get a firewall rule by ID, or short ID, in that order.
*
* If there is more than one firewall rule with that short ID, then this errors
* out.
*/
TritonApi.prototype.getFirewallRule = function getFirewallRule(id, cb) {
assert.string(id, 'id');
assert.func(cb, 'cb');
if (common.isUUID(id)) {
this.cloudapi.getFirewallRule(id, function (err, fwrule) {
if (err) {
if (err.restCode === 'ResourceNotFound') {
err = new errors.ResourceNotFoundError(err,
format('firewall rule with id %s was not found', id));
}
cb(err);
} else {
cb(null, fwrule);
}
});
} else {
this.cloudapi.listFirewallRules(function (err, fwrules) {
if (err) {
return cb(err);
}
var shortIdMatches = fwrules.filter(function (fwrule) {
return fwrule.id.slice(0, 8) === id;
});
if (shortIdMatches.length === 1) {
cb(null, shortIdMatches[0]);
} else if (shortIdMatches.length === 0) {
cb(new errors.ResourceNotFoundError(format(
'no firewall rule with short id "%s" was found', id)));
} else {
cb(new errors.ResourceNotFoundError(
format('"%s" is an ambiguous short id, with multiple ' +
'matching firewall rules', id)));
}
});
}
};
/** /**
* Get an instance by ID, exact name, or short ID, in that order. * Get an instance by ID, exact name, or short ID, in that order.
* *

View File

@ -86,6 +86,19 @@ test('triton snapshot', function (tt) {
}); });
}); });
tt.test(' triton instance start --snapshot', function (t) {
var cmd = 'instance start ' + INST + ' -w --snapshot=' + SNAP_NAME;
h.triton(cmd, function (err, stdout, stderr) {
if (h.ifErr(t, err, 'triton instance start --snapshot'))
return t.end();
t.ok(stdout.match('Start instance ' + INST));
t.end();
});
});
tt.test(' triton snapshot delete', function (t) { tt.test(' triton snapshot delete', function (t) {
var cmd = 'snapshot delete ' + INST + ' ' + SNAP_NAME + ' -w --force'; var cmd = 'snapshot delete ' + INST + ' ' + SNAP_NAME + ' -w --force';
h.triton(cmd, function (err, stdout, stderr) { h.triton(cmd, function (err, stdout, stderr) {