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'
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

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 ...`
*/
var bigspinner = require('bigspinner');
var format = require('util').format;
var tabula = require('tabula');
var vasync = require('vasync');
var common = require('./common');
var distractions = require('./distractions');
var errors = require('./errors');
@ -107,18 +107,9 @@ function do_create_instance(subcmd, opts, args, callback) {
return next();
}
var spinner;
var distraction;
if (!opts.quiet && process.stderr.isTTY) {
var BORDER = 10;
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'
});
distraction = distractions.createDistraction();
}
// Dry-run: fake wait for a few seconds.
@ -135,8 +126,8 @@ function do_create_instance(subcmd, opts, args, callback) {
id: ctx.inst.id,
states: ['running', 'failed']
}, function (err, inst) {
if (spinner) {
spinner.destroy();
if (distraction) {
distraction.destroy();
}
if (err) {
return next(err);

View File

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