joyent/node-triton#146 triton instance rename --wait

This commit is contained in:
Yang Yong 2016-12-21 11:30:24 +09:00 committed by Trent Mick
parent 586b1924a0
commit 5dab69c002
4 changed files with 149 additions and 22 deletions

View File

@ -778,7 +778,7 @@ CloudApi.prototype.getMachine = function getMachine(opts, cb) {
* @param {Object} opts * @param {Object} opts
* - id {UUID} Required. The machine id. * - id {UUID} Required. The machine id.
* - {String} name. The machine name * - {String} name. The machine name
* @param {Function} callback of the form `function (err, res)` * @param {Function} callback of the form `function (err, body, res)`
*/ */
CloudApi.prototype.renameMachine = function renameMachine(opts, callback) { CloudApi.prototype.renameMachine = function renameMachine(opts, callback) {
assert.uuid(opts.id, 'opts.id'); assert.uuid(opts.id, 'opts.id');
@ -793,7 +793,7 @@ CloudApi.prototype.renameMachine = function renameMachine(opts, callback) {
path: format('/%s/machines/%s', this.account, opts.id), path: format('/%s/machines/%s', this.account, opts.id),
data: data data: data
}, function (err, req, res, body) { }, function (err, req, res, body) {
callback(err, res); callback(err, body, res);
}); });
}; };

View File

@ -7,13 +7,7 @@
var common = require('../common'); var common = require('../common');
var errors = require('../errors'); var errors = require('../errors');
function perror(err) {
console.error('error: %s', err.message);
}
function do_rename(subcmd, opts, args, callback) { function do_rename(subcmd, opts, args, callback) {
var self = this;
if (opts.help) { if (opts.help) {
this.do_help('help', {}, [subcmd], callback); this.do_help('help', {}, [subcmd], callback);
return; return;
@ -24,14 +18,30 @@ function do_rename(subcmd, opts, args, callback) {
callback(new errors.UsageError('missing NEWNAME arg')); callback(new errors.UsageError('missing NEWNAME arg'));
return; return;
} }
var cOpts = {id: args[0], name: args[1]};
self.top.tritonapi.renameInstance(cOpts, function (err) { var id = args[0];
if (err) { var name = args[1];
callback(err); console.log('Renaming instance %s to "%s"', id, name);
return;
var tritonapi = this.top.tritonapi;
common.cliSetupTritonApi({cli: this.top}, function onSetup(setupErr) {
if (setupErr) {
callback(setupErr);
} }
console.log('Renamed instance %s to "%s"', cOpts.id, cOpts.name);
callback(); tritonapi.renameInstance({
id: id,
name: name,
wait: opts.wait,
waitTimeout: opts.wait_timeout * 1000
}, function (err) {
if (err) {
callback(err);
return;
}
console.log('Renamed instance %s to "%s"', id, name);
callback();
});
}); });
} }
@ -41,6 +51,18 @@ do_rename.options = [
names: ['help', 'h'], names: ['help', 'h'],
type: 'bool', type: 'bool',
help: 'Show this help.' help: 'Show this help.'
},
{
names: ['wait', 'w'],
type: 'bool',
help: 'Block until renaming instance is complete.'
},
{
names: ['wait-timeout'],
type: 'positiveInteger',
default: 120,
help: 'The number of seconds to wait before timing out with an error. '
+ 'The default is 120 seconds.'
} }
]; ];
@ -53,7 +75,11 @@ do_rename.help = [
'', '',
'{{options}}', '{{options}}',
'Where "INST" is an instance name, id, or short id', 'Where "INST" is an instance name, id, or short id',
'and "NEWNAME" is an instance name.' 'and "NEWNAME" is an instance name.',
'',
'Changing an instance name is asynchronous.',
'Use "--wait" to not return until',
'the changes are completed.'
].join('\n'); ].join('\n');
do_rename.completionArgtypes = ['tritoninstance', 'none']; do_rename.completionArgtypes = ['tritoninstance', 'none'];

View File

@ -817,7 +817,6 @@ TritonApi.prototype.getInstance = function getInstance(opts, cb) {
if (inst || instFromList) { if (inst || instFromList) {
return next(); return next();
} }
self.cloudapi.listMachines({name: opts.id}, function (err, insts) { self.cloudapi.listMachines({name: opts.id}, function (err, insts) {
if (err) { if (err) {
return next(err); return next(err);
@ -2266,14 +2265,24 @@ TritonApi.prototype.deletePolicy = function deletePolicy(opts, cb) {
* rename a machine by id. * rename a machine by id.
* *
* @param {Object} opts * @param {Object} opts
* - id {UUID} or The machine name or shortID Required. * - {String} id: Required. The instance name, short id, or id (a UUID).
* - {String} name. The machine name * - {String} name: Required. The new instance name.
* @param {Function} callback of the form `function (err, res)` * - {Boolean} wait: Wait (via polling) until the rename is complete.
* Warning: A concurrent rename of the same instance can result in this
* polling being unable to notice the change. Use `waitTimeout` to
* put an upper bound.
* - {Number} waitTimeout: The number of milliseconds after which to
* timeout (call `cb` with a timeout error) waiting. Only relevant if
* `opts.wait === true`. Default is Infinity (i.e. it doesn't timeout).
* @param {Function} callback of the form `function (err, _, res)`
*/ */
TritonApi.prototype.renameInstance = function renameInstance(opts, cb) { TritonApi.prototype.renameInstance = function renameInstance(opts, cb) {
assert.string(opts.id, 'opts.id'); assert.string(opts.id, 'opts.id');
assert.string(opts.name, 'opts.name'); assert.string(opts.name, 'opts.name');
assert.optionalBool(opts.wait, 'opts.wait');
assert.optionalNumber(opts.waitTimeout, 'opts.waitTimeout');
assert.func(cb, 'cb'); assert.func(cb, 'cb');
var self = this; var self = this;
var res; var res;
@ -2282,16 +2291,86 @@ TritonApi.prototype.renameInstance = function renameInstance(opts, cb) {
function renameMachine(arg, next) { function renameMachine(arg, next) {
self.cloudapi.renameMachine({id: arg.instId, name: opts.name}, self.cloudapi.renameMachine({id: arg.instId, name: opts.name},
function (err, _res) { function (err, _, _res) {
res = _res; res = _res;
next(err); next(err);
}); });
},
function waitForNameChanges(arg, next) {
if (!opts.wait) {
next();
return;
}
self._waitForInstanceRename({
id: arg.instId,
timeout: opts.waitTimeout,
name: opts.name
}, next);
} }
]}, function (err) { ]}, function (err) {
cb(err, res); cb(err, null, res);
}); });
}; };
/**
* Shared implementation for any methods to change instance name.
*
* @param {Object} opts
* - {String} id: The instance ID Required.
* - {String} name: Required change new name
* - {Number} timeout: The number of milliseconds after which to
* timeout (call `cb` with a timeout error) waiting.
* Default is Infinity (i.e. it doesn't timeout).
* @param {Function} cb: `function (err)`
*/
TritonApi.prototype._waitForInstanceRename =
function _waitForInstanceRename(opts, cb) {
var self = this;
assert.object(opts, 'opts');
assert.uuid(opts.id, 'opts.id');
assert.optionalNumber(opts.timeout, 'opts.timeout');
var timeout = opts.hasOwnProperty('timeout') ? opts.timeout : Infinity;
assert.ok(timeout > 0, 'opts.timeout must be greater than zero');
assert.string(opts.name, 'opts.name');
assert.func(cb, 'cb');
/*
* Hardcoded 2s poll interval for now. Not yet configurable, being mindful
* of avoiding lots of clients naively swamping a CloudAPI and hitting
* throttling.
*/
var POLL_INTERVAL = 2 * 1000;
var startTime = Date.now();
var poll = function () {
self.cloudapi.getMachine({id: opts.id}, function (err, machine) {
if (err) {
cb(err);
return;
}
if (opts.name === machine.name) {
cb();
return;
} else {
var elapsedTime = Date.now() - startTime;
if (elapsedTime > timeout) {
cb(new errors.TimeoutError(format('timeout waiting for '
+ 'instance %s rename (elapsed %ds)',
opts.id, Math.round(elapsedTime / 1000))));
} else {
setTimeout(poll, POLL_INTERVAL);
}
}
});
};
setImmediate(poll);
};
//---- exports //---- exports
module.exports = { module.exports = {

View File

@ -24,6 +24,7 @@ var h = require('./helpers');
// --- globals // --- globals
var INST_ALIAS = f('nodetritontest-managewf-%s', os.hostname()); var INST_ALIAS = f('nodetritontest-managewf-%s', os.hostname());
var INST_ALIAS_NEWNAME = INST_ALIAS + '-renamed';
var opts = { var opts = {
skip: !h.CONFIG.allowWriteActions skip: !h.CONFIG.allowWriteActions
@ -236,6 +237,27 @@ test('triton manage workflow', opts, function (tt) {
}); });
}); });
// rename the instance
tt.test(' triton inst rename', function (t) {
var args = ['inst', 'rename', '-w', instance.id, INST_ALIAS_NEWNAME];
h.safeTriton(t, args, function (err, stdout) {
t.ok(stdout.match(/^Renaming instance/m),
'"Renaming instance" in stdout');
t.ok(stdout.match(/^Renamed instance/m),
'"Renamed instance" in stdout');
t.end();
});
});
tt.test(' confirm renamed', function (t) {
h.safeTriton(t, {json: true, args: ['inst', 'get', '-j',
INST_ALIAS_NEWNAME]},
function (err, inst) {
t.equal(inst.name, INST_ALIAS_NEWNAME, 'instance was renamed');
t.end();
});
});
// remove test instance // remove test instance
tt.test(' cleanup (triton delete)', function (t) { tt.test(' cleanup (triton delete)', function (t) {
h.safeTriton(t, ['delete', '-w', instance.id], function () { h.safeTriton(t, ['delete', '-w', instance.id], function () {