From 12c9cb64a64d54d146327f8e96eec524b8fd13e4 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 1 Sep 2015 10:43:28 -0700 Subject: [PATCH] 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. --- TODO.txt | 5 +- lib/distractions.js | 33 ++++++++++++ lib/do_create_instance.js | 19 ++----- lib/do_wait_instance.js | 105 ++++++++++++++++++++++++++------------ 4 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 lib/distractions.js diff --git a/TODO.txt b/TODO.txt index 2b9ebca..fbd0bf6 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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 diff --git a/lib/distractions.js b/lib/distractions.js new file mode 100644 index 0000000..2032870 --- /dev/null +++ b/lib/distractions.js @@ -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 +} diff --git a/lib/do_create_instance.js b/lib/do_create_instance.js index 0101ca8..82df940 100644 --- a/lib/do_create_instance.js +++ b/lib/do_create_instance.js @@ -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); diff --git a/lib/do_wait_instance.js b/lib/do_wait_instance.js index be4a7c0..a75b31c 100644 --- a/lib/do_wait_instance.js +++ b/lib/do_wait_instance.js @@ -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'],