joyent/node-triton#74 `triton instance list` filtering on tags

Reviewed by: Josh Wilsdon <josh@wilsdon.ca>
Approved by: Josh Wilsdon <josh@wilsdon.ca>
This commit is contained in:
Trent Mick 2017-09-29 12:12:53 -07:00
parent e0b6c7e0bc
commit 69a4598458
4 changed files with 48 additions and 27 deletions

View File

@ -6,7 +6,8 @@ Known issues:
## not yet released
(nothing yet)
- [joyent/node-triton#74, TOOLS-1872] Filter instance list by tag, e.g.
`triton instance list tag.foo=bar`.
## 5.3.2

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright 2017 Joyent, Inc.
* Copyright (c) 2017, Joyent, Inc.
*/
var assert = require('assert-plus');
@ -135,7 +135,8 @@ function jsonStream(arr, stream) {
* types, e.g the string 'false' is converted to the boolean primitive "false".
*
* @param {String} kv
* @param {String[]} validKeys: Optional
* @param {Array} validKeys: Optional array of strings or regexes matching
* valid keys.
* @param {Object} options: Optional
* - @param disableTypeConversions {Boolean} Optional. If true, then no
* type conversion of values is performed, and all values are returned as
@ -148,7 +149,7 @@ function jsonStream(arr, stream) {
*/
function _parseKeyValue(kv, validKeys, options) {
assert.string(kv, 'kv');
assert.optionalArrayOfString(validKeys, 'validKeys');
assert.optionalArray(validKeys, 'validKeys');
assert.optionalObject(options, 'options');
options = options || {};
assert.optionalBool(options.disableTypeConversions,
@ -156,6 +157,7 @@ function _parseKeyValue(kv, validKeys, options) {
assert.optionalObject(options.typeHintFromKey, 'options.typeHintFromKey');
assert.optionalBool(options.failOnEmptyValue, 'options.failOnEmptyValue');
var i;
var idx = kv.indexOf('=');
if (idx === -1) {
throw new errors.UsageError(format('invalid key=value: "%s"', kv));
@ -163,11 +165,23 @@ function _parseKeyValue(kv, validKeys, options) {
var k = kv.slice(0, idx);
var typeHint;
var v = kv.slice(idx + 1);
var validKey;
if (validKeys && validKeys.indexOf(k) === -1) {
throw new errors.UsageError(format(
'invalid key: "%s" (must be one of "%s")',
k, validKeys.join('", "')));
if (validKeys) {
var foundMatch = false;
for (i = 0; i < validKeys.length; i++) {
validKey = validKeys[i];
if ((validKey instanceof RegExp && validKey.test(k)) ||
k === validKey) {
foundMatch = true;
break;
}
}
if (!foundMatch) {
throw new errors.UsageError(format(
'invalid key: "%s" (must match one of: %s)',
k, validKeys.join(', ')));
}
}
if (v === '' && options.failOnEmptyValue) {
@ -204,14 +218,13 @@ function _parseKeyValue(kv, validKeys, options) {
* given an array of key=value pairs, break them into a JSON predicate
*
* @param {Array} kvs - an array of key=value pairs
* @param {Object[]} validKeys - an array of objects representing valid keys
* that can be used in the first argument "kvs".
* @param {Array} validKeys: Optional array of strings or regexes matching
* valid keys.
* @param {String} compositionType - the way each key/value pair will be
* combined to form a JSON predicate. Valid values are 'or' and 'and'.
*/
function jsonPredFromKv(kvs, validKeys, compositionType) {
assert.arrayOfString(kvs, 'kvs');
assert.arrayOfString(validKeys, 'validKeys');
assert.string(compositionType, 'string');
assert.ok(compositionType === 'or' || compositionType === 'and',
'compositionType');
@ -1125,8 +1138,8 @@ function tildeSync(s) {
* - @param typeHintFromKey {Object} Optional. Type hints for input keys.
* E.g. if parsing 'foo=false' and `typeHintFromKey={foo: 'string'}`,
* then we do NOT parse it to a boolean `false`.
* - @param validKeys {String[]} Optional. List of valid keys. By default
* all keys are valid.
* - @param {Array} validKeys: Optional array of strings or regexes
* matching valid keys. By default all keys are valid.
* - @param failOnEmptyValue {Boolean} Optional. If true, then a key with a
* value that is the empty string throws an error. Default is false.
*/
@ -1139,7 +1152,6 @@ function objFromKeyValueArgs(args, opts)
assert.optionalBool(opts.disableTypeConversions,
'opts.disableTypeConversions');
assert.optionalObject(opts.typeHintFromKey, opts.typeHintFromKey);
assert.optionalArrayOfString(opts.validKeys, 'opts.validKeys');
assert.optionalBool(opts.failOnEmptyValue, 'opts.failOnEmptyValue');
var obj = {};

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright 2016 Joyent, Inc.
* Copyright (c) 2017, Joyent, Inc.
*
* `triton instance list ...`
*/
@ -22,13 +22,16 @@ var common = require('../common');
* See <https://apidocs.joyent.com/cloudapi/#ListMachines>.
*/
var validFilters = [
'type',
'brand', // Added in CloudAPI 8.0.0
'name',
'brand', // Added in CloudAPI 8.0.0
'docker', // Added in CloudAPI 8.0.0
'image',
'state',
'memory',
'docker' // Added in CloudAPI 8.0.0
'name',
'state',
// jsl:ignore
/^tag\./,
// jsl:end
'type'
];
// columns default without -o
@ -74,7 +77,6 @@ function do_list(subcmd, opts, args, callback) {
listOpts.credentials = true;
}
var imgs = [];
var insts;
@ -197,10 +199,10 @@ do_list.help = [
'',
'{{options}}',
'Filters:',
' FIELD=VALUE Equality filter. Supported fields: type, brand, name,',
' image, state, and memory',
' FIELD=VALUE Equality filter. Supported fields: brand, image,',
' memory, name, state, tag.TAGNAME, and type.',
' FIELD=true|false Boolean filter. Supported fields: docker (added in',
' CloudAPI 8.0.0)',
' CloudAPI 8.0.0).',
'',
'Fields (most are self explanatory, "*" indicates a field added client-side',
'for convenience):',
@ -211,7 +213,11 @@ do_list.help = [
' "K" the brand is "kvm"',
' age* Approximate time since created, e.g. 1y, 2w.',
' img* The image "name@version", if available, else its',
' "shortid".'
' "shortid".',
'',
'Examples:',
' {{name}} ls -Ho id state=running # IDs of running insts',
' {{name}} ls docker=true tag.foo=bar # Docker insts w/ "foo=bar" tag'
/* END JSSTYLED */
].join('\n');

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (c) 2015, Joyent, Inc.
* Copyright (c) 2017, Joyent, Inc.
*/
/*
@ -135,7 +135,9 @@ test('objFromKeyValueArgs', function (t) {
// valid parameters
kv = common.objFromKeyValueArgs(arr, {
validKeys: ['foo', 'bar', 'baz'],
// jsl:ignore
validKeys: ['foo', /^ba.$/],
// jsl:end
disableDotted: true,
disableTypeConversions: true
});