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 ## 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 ## 5.3.2

View File

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

View File

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