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.
* @param {Function} callback of the form f(err, res).
*/
CloudApi.prototype.startMachineFromSnapshot =
function startMachineFromSnapshot(opts, cb) {
assert.object(opts, 'opts');
assert.uuid(opts.id, 'opts.id');
@ -1075,13 +1076,13 @@ function startMachineFromSnapshot(opts, cb) {
this._request({
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),
data: opts
}, function (err, req, res, body) {
cb(err, body, res);
});
}
};
/**

View File

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

View File

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

View File

@ -11,6 +11,7 @@
*/
var assert = require('assert-plus');
var vasync = require('vasync');
var common = require('../common');
var errors = require('../errors');
@ -25,28 +26,46 @@ function do_disable(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('Missing FWRULE-ID argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('Incorrect number of arguments'));
cb(new errors.UsageError('Missing FWRULE-ID argument(s)'));
return;
}
var id = args[0];
var cli = this.top;
// XXX add support for shortId
cli.tritonapi.cloudapi.disableFirewallRule(id, function onRule(err) {
vasync.forEachParallel({
inputs: args,
func: function disableOne(id, nextId) {
if (common.isUUID(id)) {
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) {
cb(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);
cb();
nextId();
});
}
}
}, cb);
}
do_disable.options = [

View File

@ -11,6 +11,7 @@
*/
var assert = require('assert-plus');
var vasync = require('vasync');
var common = require('../common');
var errors = require('../errors');
@ -25,28 +26,46 @@ function do_enable(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('Missing FWRULE-ID argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('Incorrect number of arguments'));
cb(new errors.UsageError('Missing FWRULE-ID argument(s)'));
return;
}
var id = args[0];
var cli = this.top;
// XXX add support for shortId
cli.tritonapi.cloudapi.enableFirewallRule(id, function onRule(err) {
vasync.forEachParallel({
inputs: args,
func: function enableOne(id, nextId) {
if (common.isUUID(id)) {
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) {
cb(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);
cb();
nextId();
});
}
}
}, cb);
}
do_enable.options = [
@ -60,7 +79,7 @@ do_enable.help = [
'Enable a specific firewall rule.',
'',
'Usage:',
' {{name}} enable FWRULE-ID',
' {{name}} enable FWRULE-ID [FWRULE-ID...]',
'',
'{{options}}'
].join('\n');

View File

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

View File

@ -58,7 +58,7 @@ function do_list(subcmd, opts, args, cb) {
if (columns.indexOf('shortid') !== -1) {
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) {
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++) {
var key = keys[i];
var value = ctx.data[key];
@ -129,24 +136,38 @@ function do_update(subcmd, opts, args, cb) {
next();
},
function updateAway(ctx, next) {
var keys = Object.keys(ctx.data);
if (keys.length === 0) {
console.log('No fields given for firewall rule update');
// we need to look up the full UUID if the given id is a short id
function getFullId(ctx, next) {
if (common.isUUID(id)) {
ctx.data.id = id;
next();
return;
}
ctx.data.id = id;
tritonapi.cloudapi.updateFirewallRule(ctx.data, function (err) {
tritonapi.getFirewallRule(id, function onRule(err, fwrule) {
if (err) {
next(err);
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,
keys.join(', '));
Object.keys(data).join(', '));
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;
}
@ -77,7 +85,8 @@ function _doTheAction(action, subcmd, opts, args, callback) {
var command, state;
switch (action) {
case 'start':
command = 'startMachine';
command = opts.snapshot ? 'startMachineFromSnapshot' :
'startMachine';
state = 'running';
break;
case 'stop':
@ -126,7 +135,12 @@ function _doTheAction(action, subcmd, opts, args, callback) {
// called when "uuid" is set
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) {
if (err) {

View File

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

View File

@ -221,7 +221,7 @@ TritonApi.prototype._cacheGetJson = function _cacheGetJson(key, cb) {
*
* @param opts {Object} Optional.
* - 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.
* - ... all other cloudapi ListImages options per
* <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.
*

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) {
var cmd = 'snapshot delete ' + INST + ' ' + SNAP_NAME + ' -w --force';
h.triton(cmd, function (err, stdout, stderr) {