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
* - id {UUID} Required. The machine id.
* - {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) {
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),
data: data
}, function (err, req, res, body) {
callback(err, res);
callback(err, body, res);
});
};

View File

@ -7,13 +7,7 @@
var common = require('../common');
var errors = require('../errors');
function perror(err) {
console.error('error: %s', err.message);
}
function do_rename(subcmd, opts, args, callback) {
var self = this;
if (opts.help) {
this.do_help('help', {}, [subcmd], callback);
return;
@ -24,15 +18,31 @@ function do_rename(subcmd, opts, args, callback) {
callback(new errors.UsageError('missing NEWNAME arg'));
return;
}
var cOpts = {id: args[0], name: args[1]};
self.top.tritonapi.renameInstance(cOpts, function (err) {
var id = args[0];
var name = args[1];
console.log('Renaming instance %s to "%s"', id, name);
var tritonapi = this.top.tritonapi;
common.cliSetupTritonApi({cli: this.top}, function onSetup(setupErr) {
if (setupErr) {
callback(setupErr);
}
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"', cOpts.id, cOpts.name);
console.log('Renamed instance %s to "%s"', id, name);
callback();
});
});
}
@ -41,6 +51,18 @@ do_rename.options = [
names: ['help', 'h'],
type: 'bool',
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}}',
'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');
do_rename.completionArgtypes = ['tritoninstance', 'none'];

View File

@ -817,7 +817,6 @@ TritonApi.prototype.getInstance = function getInstance(opts, cb) {
if (inst || instFromList) {
return next();
}
self.cloudapi.listMachines({name: opts.id}, function (err, insts) {
if (err) {
return next(err);
@ -2266,14 +2265,24 @@ TritonApi.prototype.deletePolicy = function deletePolicy(opts, cb) {
* rename a machine by id.
*
* @param {Object} opts
* - id {UUID} or The machine name or shortID Required.
* - {String} name. The machine name
* @param {Function} callback of the form `function (err, res)`
* - {String} id: Required. The instance name, short id, or id (a UUID).
* - {String} name: Required. The new instance name.
* - {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) {
assert.string(opts.id, 'opts.id');
assert.string(opts.name, 'opts.name');
assert.optionalBool(opts.wait, 'opts.wait');
assert.optionalNumber(opts.waitTimeout, 'opts.waitTimeout');
assert.func(cb, 'cb');
var self = this;
var res;
@ -2282,16 +2291,86 @@ TritonApi.prototype.renameInstance = function renameInstance(opts, cb) {
function renameMachine(arg, next) {
self.cloudapi.renameMachine({id: arg.instId, name: opts.name},
function (err, _res) {
function (err, _, _res) {
res = _res;
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) {
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
module.exports = {

View File

@ -24,6 +24,7 @@ var h = require('./helpers');
// --- globals
var INST_ALIAS = f('nodetritontest-managewf-%s', os.hostname());
var INST_ALIAS_NEWNAME = INST_ALIAS + '-renamed';
var opts = {
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
tt.test(' cleanup (triton delete)', function (t) {
h.safeTriton(t, ['delete', '-w', instance.id], function () {