PUBAPI-1452 Add ListNetwork IP and GetNetworkIP to node-triton

Reviewed by: Trent Mick <trentm@gmail.com>
Approved by: Trent Mick <trentm@gmail.com>
This commit is contained in:
Mike Zeller 2017-10-24 09:32:39 -07:00
parent 12f2794c39
commit da5f3bade8
10 changed files with 663 additions and 2 deletions

View File

@ -8,6 +8,11 @@ Known issues:
(nothing yet)
## 5.5.0
- [PUBAPI-1452] Add ip subcommand to network, e.g.
`triton network ip`.
## 5.4.0
- [joyent/node-triton#74, TOOLS-1872] Filter instance list by tag, e.g.

View File

@ -386,7 +386,41 @@ CloudApi.prototype.getNetwork = function getNetwork(id, cb) {
});
};
/**
* <http://apidocs.joyent.com/cloudapi/#ListNetworkIPs>
*
* @param {String} - UUID
* @param {Function} callback of the form `function (err, ips, res)`
*/
CloudApi.prototype.listNetworkIps = function listNetworkIps(id, cb) {
assert.uuid(id, 'id');
assert.func(cb, 'cb');
var endpoint = this._path(format('/%s/networks/%s/ips', this.account, id));
this._request(endpoint, function (err, req, res, body) {
cb(err, body, res);
});
};
/**
* <http://apidocs.joyent.com/cloudapi/#GetNetworkIP>
*
* @param {Object} opts
* - {String} opts.id The network UUID, name, or shortID. Required.
* - {String} opts.ip The IP. Required.
* @param {Function} callback of the form `function (err, ip, res)`
*/
CloudApi.prototype.getNetworkIp = function getNetworkIp(opts, cb) {
assert.uuid(opts.id, 'id');
assert.string(opts.ip, 'ip');
assert.func(cb, 'cb');
var endpoint = this._path(format('/%s/networks/%s/ips/%s',
this.account, opts.id, opts.ip));
this._request(endpoint, function (err, req, res, body) {
cb(err, body, res);
});
};
// ---- datacenters

View File

@ -0,0 +1,81 @@
/*
* 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 2017 Joyent, Inc.
*
* `triton network ip get ...`
*/
var format = require('util').format;
var common = require('../../common');
var errors = require('../../errors');
function do_get(subcmd, opts, args, cb) {
if (opts.help) {
this.do_help('help', {}, [subcmd], cb);
return;
} else if (args.length !== 2) {
return cb(new errors.UsageError(format(
'incorrect number of args (%d)', args.length)));
}
var tritonapi = this.top.tritonapi;
common.cliSetupTritonApi({cli: this.top}, function onSetup(setupErr) {
if (setupErr) {
cb(setupErr);
return;
}
var getIpOpts = {
id: args[0],
ip: args[1]
};
tritonapi.getNetworkIp(getIpOpts, function (err, ip, res) {
if (err) {
return cb(err);
}
if (opts.json) {
console.log(JSON.stringify(ip));
} else {
console.log(JSON.stringify(ip, null, 4));
}
cb();
});
});
}
do_get.options = [
{
names: ['help', 'h'],
type: 'bool',
help: 'Show this help.'
},
{
names: ['json', 'j'],
type: 'bool',
help: 'JSON output.'
}
];
do_get.synopses = ['{{name}} {{cmd}} NETWORK IP'];
do_get.help = [
'Show a network ip.',
'',
'{{usage}}',
'',
'{{options}}',
'Where NETWORK is a network id, and IP is the ip address you want to get.'
].join('\n');
do_get.completionArgtypes = ['tritonnetwork', 'tritonnetworkip', 'none'];
module.exports = do_get;

View File

@ -0,0 +1,106 @@
/*
* 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 2017 Joyent, Inc.
*
* `triton network ip list ...`
*/
var format = require('util').format;
var tabula = require('tabula');
var vasync = require('vasync');
var common = require('../../common');
var errors = require('../../errors');
// columns default without -o
var columnsDefault = 'ip,managed,reserved,owner_uuid,belongs_to_uuid';
// sort default with -s
var sortDefault = 'ip';
function do_list(subcmd, opts, args, callback) {
var self = this;
if (opts.help) {
this.do_help('help', {}, [subcmd], callback);
return;
} else if (args.length !== 1) {
return callback(new errors.UsageError(format(
'incorrect number of args (%d)', args.length)));
}
var columns = columnsDefault;
if (opts.o) {
columns = opts.o;
}
columns = columns.split(',');
var sort = opts.s.split(',');
vasync.pipeline({arg: {cli: this.top}, funcs: [
common.cliSetupTritonApi,
function listIps(arg, next) {
self.top.tritonapi.listNetworkIps(args[0],
function (err, ips, res) {
if (err) {
next(err);
return;
}
arg.ips = ips;
next();
});
},
function doneIps(arg, next) {
var ips = arg.ips;
if (opts.json) {
common.jsonStream(ips);
} else {
tabula(ips, {
skipHeader: opts.H,
columns: columns,
sort: sort
});
}
next();
}
]}, callback);
}
do_list.options = [
{
names: ['help', 'h'],
type: 'bool',
help: 'Show this help.'
}
].concat(common.getCliTableOptions({
includeLong: true,
sortDefault: sortDefault
}));
do_list.synopses = ['{{name}} {{cmd}} NETWORK'];
do_list.help = [
'List network IPs.',
'',
'{{usage}}',
'',
'{{options}}',
'Fields (most are self explanatory, the significant ones are as follows):',
' managed IP is manged by Triton and cannot be modified directly.',
'',
'See https://apidocs.joyent.com/cloudapi/#ListNetworkIPs for a full' +
' listing.'
].join('\n');
do_list.aliases = ['ls'];
do_list.completionArgtypes = ['tritonnetwork', 'none'];
module.exports = do_list;

View File

@ -0,0 +1,49 @@
/*
* 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 2017 Joyent, Inc.
*
* `triton network ip...`
*/
var Cmdln = require('cmdln').Cmdln;
var util = require('util');
// ---- CLI class
function IpCLI(top) {
this.top = top.top;
Cmdln.call(this, {
name: top.name + ' ip',
/* BEGIN JSSTYLED */
desc: [
'List and manage Triton network IPs.'
].join('\n'),
/* END JSSTYLED */
helpOpts: {
minHelpCol: 24 /* line up with option help */
},
helpSubcmds: [
'help',
'list',
'get'
]
});
}
util.inherits(IpCLI, Cmdln);
IpCLI.prototype.init = function init(opts, args, cb) {
this.log = this.top.log;
Cmdln.prototype.init.apply(this, arguments);
};
IpCLI.prototype.do_list = require('./do_list');
IpCLI.prototype.do_get = require('./do_get');
module.exports = IpCLI;

View File

@ -32,7 +32,8 @@ function NetworkCLI(top) {
helpSubcmds: [
'help',
'list',
'get'
'get',
'ip'
]
});
}
@ -45,6 +46,7 @@ NetworkCLI.prototype.init = function init(opts, args, cb) {
NetworkCLI.prototype.do_list = require('./do_list');
NetworkCLI.prototype.do_get = require('./do_get');
NetworkCLI.prototype.do_ip = require('./do_ip');
module.exports = NetworkCLI;

View File

@ -276,6 +276,32 @@ function _stepFwRuleId(arg, next) {
}
}
/**
* A function appropriate for `vasync.pipeline` funcs that takes a `arg.network`
* (or `arg.id` if there is no `arg.network`) network name, shortid or uuid,
* and determines the network id (setting it as `arg.netId`).
*/
function _stepNetId(arg, next) {
assert.object(arg.client, 'arg.client');
var id = arg.network || arg.id;
assert.string(id, 'arg.network || arg.id');
if (common.isUUID(id)) {
arg.netId = id;
next();
} else {
arg.client.getNetwork(id, function (err, net) {
if (err) {
next(err);
} else {
arg.netId = net.id;
next();
}
});
}
}
//---- TritonApi class
/**
@ -863,6 +889,87 @@ TritonApi.prototype.getNetwork = function getNetwork(name, cb) {
}
};
/**
* List an network's IPs.
*
* @param {String} name The network UUID, name, or short ID. Required.
* @param {Function} cb `function (err, ip, res)`
* On failure `err` is an error instance, else it is null.
* On success: `net` is an array of ip objects
*/
TritonApi.prototype.listNetworkIps = function listNetworkIps(name, cb) {
assert.string(name, 'name');
assert.func(cb, 'cb');
var self = this;
var ipArray;
var res;
vasync.pipeline({arg: {client: self, id: name}, funcs: [
_stepNetId,
function getIp(arg, next) {
self.cloudapi.listNetworkIps(arg.netId,
function (err, ips, _res) {
res = _res;
ipArray = ips;
if (err && err.restCode === 'ResourceNotFound' &&
err.exitStatus !== 3) {
// Wrap with *our* ResourceNotFound for exitStatus=3.
err = new errors.ResourceNotFoundError(err,
format('network with id %s was not found', name));
}
next(err);
});
}
]}, function (err) {
cb(err, ipArray, res);
});
};
/**
* Get an network IP.
*
* @param {Object} opts
* - {String} opts.id The network UUID, name, or shortID. Required.
* - {String} opts.ip The IP. Required.
* @param {Function} cb `function (err, ip, res)`
* On failure `err` is an error instance, else it is null.
* On success: `ip` is an ip object
*/
TritonApi.prototype.getNetworkIp = function getNetworkIp(opts, cb) {
assert.string(opts.id, 'id');
assert.string(opts.ip, 'userIp');
assert.func(cb, 'cb');
var self = this;
var ipObj;
var res;
vasync.pipeline({arg: {client: self, id: opts.id}, funcs: [
_stepNetId,
function getIp(arg, next) {
self.cloudapi.getNetworkIp({id: arg.netId,
ip: opts.ip}, function (err, ip, _res) {
res = _res;
ipObj = ip;
if (err && err.restCode === 'ResourceNotFound' &&
err.exitStatus !== 3) {
// Wrap with *our* ResourceNotFound for exitStatus=3.
err = new errors.ResourceNotFoundError(err,
format('network with id %s was not found', opts.id));
}
next(err);
});
}
]}, function (err) {
cb(err, ipObj, res);
});
};
/**
* Get an instance.

View File

@ -1,7 +1,7 @@
{
"name": "triton",
"description": "Joyent Triton CLI and client (https://www.joyent.com/triton)",
"version": "5.4.0",
"version": "5.5.0",
"author": "Joyent (joyent.com)",
"homepage": "https://github.com/joyent/node-triton",
"dependencies": {

View File

@ -0,0 +1,104 @@
/*
* 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 2017 Joyent, Inc.
*/
/*
* Integration tests for using ip-related APIs as a module.
*/
var h = require('./helpers');
var test = require('tape');
// --- Globals
var CLIENT;
var NET;
var IP;
// --- Tests
test('TritonApi network ips', function (tt) {
tt.test(' setup', function (t) {
h.createClient(function (err, client_) {
t.error(err);
CLIENT = client_;
t.end();
});
});
tt.test(' setup: net', function (t) {
var opts = {
account: CLIENT.profile.account
};
CLIENT.cloudapi.listNetworks(opts, function (err, nets) {
if (h.ifErr(t, err))
return t.end();
t.ok(Array.isArray(nets), 'networks');
// Array.find() is only in newer node versions
while (nets.length > 0) {
var elm = nets.shift();
if (elm.fabric === true) {
NET = elm;
break;
}
}
t.ok(NET, 'fabric network required');
t.end();
});
});
tt.test(' TritonApi listIps', function (t) {
if (!NET) {
return t.end();
}
CLIENT.listNetworkIps(NET.id, function (err, ips) {
if (h.ifErr(t, err))
return t.end();
t.ok(Array.isArray(ips), 'ips');
IP = ips[0];
t.end();
});
});
tt.test(' TritonApi getIp', function (t) {
if (!NET || !IP) {
return t.end();
}
var opts = {
id: NET.id,
ip: IP.ip
};
CLIENT.getNetworkIp(opts, function (err, ip) {
if (h.ifErr(t, err, 'no err'))
return t.end();
t.deepEqual(ip, IP, 'ip');
t.end();
});
});
tt.test(' teardown: client', function (t) {
CLIENT.close();
t.end();
});
});

View File

@ -0,0 +1,173 @@
/*
* 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 2017 Joyent, Inc.
*/
/*
* Integration tests for `triton network ip`
*/
var h = require('./helpers');
var test = require('tape');
var common = require('../../lib/common');
// --- Globals
var networks;
var ips;
// --- Tests
test('triton network ip list', function (tt) {
tt.test(' triton network ip list -h', function (t) {
h.triton('network ip list -h', function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
t.ok(/Usage:\s+triton network ip list NETWORK/.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;
}
var net = JSON.parse(line);
if (net.fabric === true) {
networks.push(net);
}
});
t.ok(networks.length > 0, 'have at least one fabric network');
t.ok(common.isUUID(networks[0].id));
t.end();
});
});
tt.test(' triton network ip list', function (t) {
h.triton('network ip list', function (err, stdout, stderr) {
t.ok(err);
t.ok(/error \(Usage\)/.test(stderr));
t.end();
});
});
tt.test(' triton network ip list ID', function (t) {
h.triton('network ip list ' + networks[0].id,
function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
t.ok(/^IP\b/.test(stdout));
t.ok(/\bMANAGED\b/.test(stdout));
t.end();
});
});
tt.test(' triton network ip list SHORTID', function (t) {
var shortid = networks[0].id.split('-')[0];
h.triton('network ip list ' + shortid, function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
t.ok(/^IP\b/.test(stdout));
t.ok(/\bMANAGED\b/.test(stdout));
t.end();
});
});
tt.test(' triton network ip list -j', function (t) {
h.triton('network ip list -j ' + networks[0].id,
function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
ips = [];
stdout.split('\n').forEach(function (line) {
if (!line.trim()) {
return;
}
ips.push(JSON.parse(line));
});
t.ok(ips.length > 0, 'have at least one ip');
t.ok(ips[0].ip, 'ip obj has an ip');
t.end();
});
});
});
test('triton network ip get', function (tt) {
tt.test(' triton network ip get -h', function (t) {
h.triton('network ip get -h', function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
t.ok(/Usage:\s+triton network ip\b/.test(stdout));
t.end();
});
});
tt.test(' triton network ip help get', function (t) {
h.triton('network ip help get', function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
t.ok(/Usage:\s+triton network ip get\b/.test(stdout));
t.end();
});
});
tt.test(' triton network ip get', function (t) {
h.triton('network ip get', function (err, stdout, stderr) {
t.ok(err);
t.ok(/error \(Usage\)/.test(stderr));
t.end();
});
});
tt.test(' triton network ip get ID IP', function (t) {
h.triton('network ip get ' + networks[0].id + ' ' +
ips[0].ip, function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
var ip = JSON.parse(stdout);
t.equal(ip.ip, ips[0].ip);
t.end();
});
});
tt.test(' triton network ip get SHORTID IP', function (t) {
var shortid = networks[0].id.split('-')[0];
h.triton('network ip get ' + shortid + ' ' + ips[0].ip,
function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
var ip = JSON.parse(stdout);
t.equal(ip.ip, ips[0].ip);
t.end();
});
});
tt.test(' triton network ip get NAME IP', function (t) {
h.triton('network ip get ' + networks[0].name + ' ' +
ips[0].ip, function (err, stdout, stderr) {
if (h.ifErr(t, err))
return t.end();
var ip = JSON.parse(stdout);
t.equal(ip.ip, ips[0].ip);
t.end();
});
});
});