From 58a7c9977b866615bc49ce54b5a7bc83c835388f Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 2 Sep 2015 00:03:17 -0700 Subject: [PATCH] joyent/node-triton#4 triton networks/network to support shortId as well and to include fabric flag --- lib/do_images.js | 2 + lib/do_network.js | 49 ++------- lib/do_networks.js | 68 ++++++------ lib/triton.js | 67 ++++++++++-- test/integration/cli-networks.test.js | 146 ++++++++++++++++++++++++++ 5 files changed, 250 insertions(+), 82 deletions(-) create mode 100644 test/integration/cli-networks.test.js diff --git a/lib/do_images.js b/lib/do_images.js index b8c4462..76afe18 100644 --- a/lib/do_images.js +++ b/lib/do_images.js @@ -130,6 +130,7 @@ do_images.options = [ help: 'JSON stream output.' } ]; + do_images.help = ( /* BEGIN JSSTYLED */ 'List images.\n' + @@ -144,6 +145,7 @@ do_images.help = ( ' FIELD=~SUBSTRING Field substring filter. Supported fields: name\n' + '\n' + 'Fields (most are self explanatory, the client adds some for convenience):\n' + + ' shortid A short ID prefix.\n' + ' flags This is a set of single letter flags\n' + ' summarizing some fields. "P" indicates the\n' + ' image is public. "I" indicates an incremental\n' + diff --git a/lib/do_network.js b/lib/do_network.js index 03a1a48..10c7af1 100644 --- a/lib/do_network.js +++ b/lib/do_network.js @@ -4,60 +4,33 @@ * `triton network ...` */ +var format = require('util').format; + var common = require('./common'); +var errors = require('./errors'); + function do_network(subcmd, opts, args, cb) { if (opts.help) { this.do_help('help', {}, [subcmd], cb); return; } else if (args.length !== 1) { - cb(new Error('invalid args: ' + args)); - return; + return cb(new errors.UsageError(format( + 'incorrect number of args (%d): %s', args.length, args.join(' ')))); } - var id = args[0]; - - if (common.isUUID(id)) { - this.triton.cloudapi.getNetwork(id, done); - } else { - // we have to list all networks and find the one pertaining - // to the alias given - this.triton.cloudapi.listNetworks(function (err, networks) { - if (err) { - done(err); - return; - } - - var net; - // try to find the network - networks.forEach(function (network) { - if (network.name === id) - net = network; - }); - - if (net) { - // found! - done(null, net); - } else { - // not found - done(new Error('network ' + id + ' not found')); - } - }); - } - - function done(err, network) { + this.triton.getNetwork(args[0], function (err, net) { if (err) { - cb(err); - return; + return cb(err); } if (opts.json) { - console.log(JSON.stringify(network)); + console.log(JSON.stringify(net)); } else { - console.log(JSON.stringify(network, null, 4)); + console.log(JSON.stringify(net, null, 4)); } cb(); - } + }); } do_network.options = [ diff --git a/lib/do_networks.js b/lib/do_networks.js index ba964e2..0ed8de8 100644 --- a/lib/do_networks.js +++ b/lib/do_networks.js @@ -16,21 +16,6 @@ var validFilters = [ 'description' ]; -// valid output fields to be printed -var validFields = [ - 'id', - 'name', - 'public', - 'fabric', - 'gateway', - 'internet_nat', - 'provision_end_ip', - 'provision_start_ip', - 'resolvers', - 'subnet', - 'vlan_id' -]; - function do_networks(subcmd, opts, args, callback) { if (opts.help) { this.do_help('help', {}, [subcmd], callback); @@ -40,18 +25,14 @@ function do_networks(subcmd, opts, args, callback) { return; } - var columns = opts.o.trim().split(','); - var sort = opts.s.trim().split(','); - - /* not supported - var listOpts; - try { - listOpts = common.kvToObj(args, validFilters); - } catch (e) { - callback(e); - return; + var columns = 'shortid,name,subnet,gateway,fabric,vlan,public'.split(','); + if (opts.o) { + /* JSSTYLED */ + columns = opts.o.trim().split(/\s*,\s*/g); + } else if (opts.long) { + columns[0] = 'id'; } - */ + var sort = opts.s.trim().split(','); this.triton.cloudapi.listNetworks(function (err, networks) { if (err) { @@ -62,12 +43,15 @@ function do_networks(subcmd, opts, args, callback) { if (opts.json) { common.jsonStream(networks); } else { - // pretty print + for (var i = 0; i < networks.length; i++) { + var net = networks[i]; + net.shortid = net.id.split('-', 1)[0]; + net.vlan = net.vlan_id; + } tabula(networks, { skipHeader: opts.H, columns: columns, - sort: sort, - validFields: validFields + sort: sort }); } callback(); @@ -88,10 +72,14 @@ do_networks.options = [ { names: ['o'], type: 'string', - default: 'id,name,subnet,public,vlan_id,gateway', help: 'Specify fields (columns) to output.', helpArg: 'field1,...' }, + { + names: ['long', 'l'], + type: 'bool', + help: 'Long/wider output. Ignored if "-o ..." is used.' + }, { names: ['s'], type: 'string', @@ -105,13 +93,17 @@ do_networks.options = [ help: 'JSON output.' } ]; -do_networks.help = ( - 'List available networks.\n' - + '\n' - + 'Usage:\n' - + ' {{name}} networks\n' - + '\n' - + '{{options}}' -); +do_networks.help = [ + 'List available networks.', + '', + 'Usage:', + ' {{name}} networks', + '', + 'Fields (most are self explanatory, the client adds some for convenience):', + ' vlan A shorter alias for "vlan_id".', + ' shortid A short ID prefix.', + '', + '{{options}}' +].join('\n'); module.exports = do_networks; diff --git a/lib/triton.js b/lib/triton.js index 6cf3dee..dca383b 100644 --- a/lib/triton.js +++ b/lib/triton.js @@ -231,10 +231,10 @@ Triton.prototype.getImage = function getImage(name, cb) { cb(null, shortIdMatches[0]); } else if (shortIdMatches.length === 0) { cb(new Error(format( - 'no image with name or shortId "%s" was found', name))); + 'no image with name or short id "%s" was found', name))); } else { cb(new Error(format('no image with name "%s" was found ' - + 'and "%s" is an ambiguous shortId', name))); + + 'and "%s" is an ambiguous short id', name))); } }); } @@ -255,7 +255,7 @@ Triton.prototype.getPackage = function getPackage(name, cb) { if (err) { cb(err); } else if (!pkg.active) { - cb(new Error(format('image %s is not active', name))); + cb(new Error(format('package %s is not active', name))); } else { cb(null, pkg); } @@ -288,10 +288,65 @@ Triton.prototype.getPackage = function getPackage(name, cb) { cb(null, shortIdMatches[0]); } else if (shortIdMatches.length === 0) { cb(new Error(format( - 'no package with name or shortId "%s" was found', name))); + 'no package with name or short id "%s" was found', name))); } else { cb(new Error(format('no package with name "%s" was found ' - + 'and "%s" is an ambiguous shortId', name))); + + 'and "%s" is an ambiguous short id', name))); + } + }); + } +}; + + +/** + * Get an network by ID, exact name, or short ID, in that order. + * + * If the name is ambiguous, then this errors out. + */ +Triton.prototype.getNetwork = function getNetwork(name, cb) { + assert.string(name, 'name'); + assert.func(cb, 'cb'); + + if (common.isUUID(name)) { + this.cloudapi.getNetwork(name, function (err, net) { + if (err) { + cb(err); + } else { + cb(null, net); + } + }); + } else { + this.cloudapi.listNetworks(function (err, nets) { + if (err) { + return cb(err); + } + + var nameMatches = []; + var shortIdMatches = []; + for (var i = 0; i < nets.length; i++) { + var net = nets[i]; + if (net.name === name) { + nameMatches.push(net); + } + if (net.id.slice(0, 8) === name) { + shortIdMatches.push(net); + } + } + + if (nameMatches.length === 1) { + cb(null, nameMatches[0]); + } else if (nameMatches.length > 1) { + cb(new Error(format( + 'network name "%s" is ambiguous: matches %d networks', + name, nameMatches.length))); + } else if (shortIdMatches.length === 1) { + cb(null, shortIdMatches[0]); + } else if (shortIdMatches.length === 0) { + cb(new Error(format( + 'no network with name or short id "%s" was found', name))); + } else { + cb(new Error(format('no network with name "%s" was found ' + + 'and "%s" is an ambiguous short id', name))); } }); } @@ -392,7 +447,7 @@ Triton.prototype.getInstance = function getInstance(name, cb) { cb(null, inst); } else { cb(new Error(format( - 'no instance with name or shortId "%s" was found', name))); + 'no instance with name or short id "%s" was found', name))); } }); }; diff --git a/test/integration/cli-networks.test.js b/test/integration/cli-networks.test.js new file mode 100644 index 0000000..49fe481 --- /dev/null +++ b/test/integration/cli-networks.test.js @@ -0,0 +1,146 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Copyright (c) 2015, Joyent, Inc. + */ + +/* + * Integration tests for `triton network(s)` + */ + +var h = require('./helpers'); +var test = require('tape'); + +var common = require('../../lib/common'); + + +// --- Globals + +var networks; + + +// --- Tests + +test('triton networks', function (tt) { + + tt.test(' triton networks -h', function (t) { + h.triton('networks -h', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + t.ok(/Usage:\s+triton networks/.test(stdout)); + t.end(); + }); + }); + + tt.test(' triton help networks', function (t) { + h.triton('help networks', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + t.ok(/Usage:\s+triton networks/.test(stdout)); + t.end(); + }); + }); + + tt.test(' triton networks', function (t) { + h.triton('networks', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + t.ok(/^SHORTID\b/.test(stdout)); + t.ok(/\bFABRIC\b/.test(stdout)); + t.end(); + }); + }); + + tt.test(' triton networks -l', function (t) { + h.triton('networks -l', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + t.ok(/^ID\b/.test(stdout)); + t.end(); + }); + }); + + tt.test(' triton networks -j', function (t) { + h.triton('networks -j', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + networks = []; + stdout.split('\n').forEach(function (line) { + if (!line.trim()) { + return; + } + networks.push(JSON.parse(line)); + }); + t.ok(networks.length > 0, 'have at least one network'); + t.ok(common.isUUID(networks[0].id)); + t.end(); + }); + }); + +}); + + +test('triton network', function (tt) { + + tt.test(' triton network -h', function (t) { + h.triton('network -h', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + t.ok(/Usage:\s+triton network\b/.test(stdout)); + t.end(); + }); + }); + + tt.test(' triton help network', function (t) { + h.triton('help network', function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + t.ok(/Usage:\s+triton network\b/.test(stdout)); + t.end(); + }); + }); + + tt.test(' triton network', function (t) { + h.triton('network', function (err, stdout, stderr) { + t.ok(err); + t.ok(/error \(Usage\)/.test(stderr)); + t.end(); + }); + }); + + tt.test(' triton network ID', function (t) { + h.triton('network ' + networks[0].id, function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + var network = JSON.parse(stdout); + t.equal(network.id, networks[0].id); + t.end(); + }); + }); + + tt.test(' triton network SHORTID', function (t) { + var shortid = networks[0].id.split('-')[0]; + h.triton('network ' + shortid, function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + var network = JSON.parse(stdout); + t.equal(network.id, networks[0].id); + t.end(); + }); + }); + + tt.test(' triton network NAME', function (t) { + h.triton('network ' + networks[0].name, function (err, stdout, stderr) { + if (h.ifErr(t, err)) + return t.end(); + var network = JSON.parse(stdout); + t.equal(network.id, networks[0].id); + t.end(); + }); + }); + +});