Factor out spinner to prep for using it for 'triton wait'.

Also refactor 'triton wait' for debuggability and to avoid possible
multiple calls to the callback.
This commit is contained in:
Trent Mick 2015-09-01 10:43:28 -07:00
parent 9241f90ccf
commit 12c9cb64a6
4 changed files with 115 additions and 47 deletions

View File

@ -4,7 +4,10 @@ MPL blurb at the start of each file.
'make check' 'make check'
test suite test suite:
- all the commands: test/integration/cli-*.test.js
- TritonApi testing: test/integration/api-*.test.js
- more test/unit/...
note in README that full UUIDs is much faster in the API note in README that full UUIDs is much faster in the API

33
lib/distractions.js Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2015 Joyent Inc. All rights reserved.
*
* A CLI distraction during a long process (e.g. waiting for
* create).
*
* Usage:
* var distractions = require('./distractions');
* var distraction = distractions.createDistraction();
* setTimeout(function () {
* distraction.destroy();
* }, 5000);
*/
var bigspinner = require('bigspinner');
function createDistraction() {
var BORDER = 10;
return bigspinner.createSpinner({
delay: 50,
positions: 40,
stream: process.stderr,
height: Math.max(2, process.stdout.rows - 2 - BORDER),
width: Math.max(2, process.stdout.columns - 1 - BORDER),
hideCursor: true,
fontChar: '\u2588' // '\x1b[7m \x1b[m'
});
}
module.exports = {
createDistraction: createDistraction
}

View File

@ -4,12 +4,12 @@
* `triton create ...` * `triton create ...`
*/ */
var bigspinner = require('bigspinner');
var format = require('util').format; var format = require('util').format;
var tabula = require('tabula'); var tabula = require('tabula');
var vasync = require('vasync'); var vasync = require('vasync');
var common = require('./common'); var common = require('./common');
var distractions = require('./distractions');
var errors = require('./errors'); var errors = require('./errors');
@ -107,18 +107,9 @@ function do_create_instance(subcmd, opts, args, callback) {
return next(); return next();
} }
var spinner; var distraction;
if (!opts.quiet && process.stderr.isTTY) { if (!opts.quiet && process.stderr.isTTY) {
var BORDER = 10; distraction = distractions.createDistraction();
spinner = bigspinner.createSpinner({
delay: 50,
positions: 40,
stream: process.stderr,
height: Math.max(2, process.stdout.rows - 2 - BORDER),
width: Math.max(2, process.stdout.columns - 1 - BORDER),
hideCursor: true,
fontChar: '\u2588' // '\x1b[7m \x1b[m'
});
} }
// Dry-run: fake wait for a few seconds. // Dry-run: fake wait for a few seconds.
@ -135,8 +126,8 @@ function do_create_instance(subcmd, opts, args, callback) {
id: ctx.inst.id, id: ctx.inst.id,
states: ['running', 'failed'] states: ['running', 'failed']
}, function (err, inst) { }, function (err, inst) {
if (spinner) { if (distraction) {
spinner.destroy(); distraction.destroy();
} }
if (err) { if (err) {
return next(err); return next(err);

View File

@ -5,19 +5,19 @@
*/ */
var format = require('util').format; var format = require('util').format;
var vasync = require('vasync');
var common = require('./common'); var common = require('./common');
var distractions = require('./distractions');
var errors = require('./errors'); var errors = require('./errors');
function do_wait_instance(subcmd, opts, args, cb) { function do_wait_instance(subcmd, opts, args, cb) {
var self = this; var self = this;
if (opts.help) { if (opts.help) {
return this.do_help('help', {}, [subcmd], cb); return this.do_help('help', {}, [subcmd], cb);
} else if (args.length < 1) { } else if (args.length < 1) {
return cb(new errors.UsageError(format( return cb(new errors.UsageError('missing INSTANCE arg(s)'));
'incorrect number of args (%d): %s', args.length, args.join(' '))));
} }
var ids = args; var ids = args;
var states = []; var states = [];
@ -25,39 +25,80 @@ function do_wait_instance(subcmd, opts, args, cb) {
states = states.concat(s.trim().split(/\s*,\s*/g)); states = states.concat(s.trim().split(/\s*,\s*/g));
}); });
function log() { var distraction;
if (!opts.quiet)
console.log.apply(console, arguments);
}
var done = 0; var done = 0;
var instFromId = {};
var machines = {}; vasync.pipeline({funcs: [
function getInsts(_, next) {
var i = 0; vasync.forEachParallel({
ids.forEach(function (id) { inputs: ids,
i++; func: function getInst(id, nextInst) {
if (common.isUUID(id)) { self.triton.getInstance(id, function (err, inst) {
machines[id] = {};
go1();
return;
}
self.triton.getInstance(id, function (err, machine) {
if (err) { if (err) {
cb(err); return nextInst(err);
return;
} }
if (states.indexOf(machine.state) >= 0) { if (states.indexOf(inst.state) !== -1) {
// machine in acceptable state already... skip it console.log('%d/%d: Instance %s (%s) already %s',
log('%d/%d: %s already in acceptable state: %s', ++done, ids.length, inst.name, id, inst.state);
++done, ids.length, id, machine.state);
} else { } else {
machines[machine.id] = machine; instFromId[inst.id] = inst;
} }
go1(); nextInst();
}); });
},
}, next);
},
function waitForInsts(_, next) {
var idsToWaitFor = Object.keys(instFromId);
if (idsToWaitFor.length === 0) {
return next();
}
if (idsToWaitFor.length === 1) {
var inst2 = instFromId[idsToWaitFor[0]];
console.log(
"Waiting for instance %s (%s) to enter state (states: %s)",
inst2.name, inst2.id, states.join(', '));
} else {
console.log(
"Waiting for %d instances to enter state (states: %s)",
idsToWaitFor.length, states.join(', '));
}
var distraction;
if (false /* TODO: need BigSpinner.log first */
&& !opts.quiet && process.stderr.isTTY)
{
distraction = distractions.createDistraction();
}
vasync.forEachParallel({
inputs: idsToWaitFor,
func: function waitForInst(id, nextInst) {
self.triton.cloudapi.waitForMachineStates({
id: id,
states: states
}, function (err, inst, res) {
if (err) {
return nextInst(err);
}
console.log('%d/%d: Instance %s (%s) moved to state %s',
++done, ids.length, inst.name, inst.id, inst.state);
nextInst();
}); });
}
}, next);
}
]}, function (err) {
if (distraction) {
distraction.destroy();
}
cb(err);
});
function go1() { function go1() {
if (--i > 0) if (--i > 0)
@ -95,7 +136,7 @@ function do_wait_instance(subcmd, opts, args, cb) {
do_wait_instance.aliases = ['wait']; do_wait_instance.aliases = ['wait'];
do_wait_instance.help = [ do_wait_instance.help = [
'Wait on instances moving to given states.', 'Wait on instances changing state.',
'', '',
'Usage:', 'Usage:',
' {{name}} wait [-s STATES] INSTANCE [INSTANCE ...]', ' {{name}} wait [-s STATES] INSTANCE [INSTANCE ...]',
@ -114,7 +155,7 @@ do_wait_instance.options = [
{ {
names: ['quiet', 'q'], names: ['quiet', 'q'],
type: 'bool', type: 'bool',
help: 'Disable all output.' help: 'No progress spinner while waiting.'
}, },
{ {
names: ['states', 's'], names: ['states', 's'],