joyent/node-triton#146 triton instance rename --wait
This commit is contained in:
parent
586b1924a0
commit
5dab69c002
@ -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);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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,15 +18,31 @@ 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];
|
||||||
|
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) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('Renamed instance %s to "%s"', cOpts.id, cOpts.name);
|
console.log('Renamed instance %s to "%s"', id, name);
|
||||||
callback();
|
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'];
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 () {
|
||||||
|
Reference in New Issue
Block a user