From 69a459845839867b3be5009d7ab908591b5c8d33 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 29 Sep 2017 12:12:53 -0700 Subject: [PATCH] joyent/node-triton#74 `triton instance list` filtering on tags Reviewed by: Josh Wilsdon Approved by: Josh Wilsdon --- CHANGES.md | 3 ++- lib/common.js | 38 +++++++++++++++++++++++++------------- lib/do_instance/do_list.js | 28 +++++++++++++++++----------- test/unit/common.test.js | 6 ++++-- 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 32fc097..d335f96 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 diff --git a/lib/common.js b/lib/common.js index 572417f..2347242 100644 --- a/lib/common.js +++ b/lib/common.js @@ -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 = {}; diff --git a/lib/do_instance/do_list.js b/lib/do_instance/do_list.js index 69fcae2..2619a3a 100644 --- a/lib/do_instance/do_list.js +++ b/lib/do_instance/do_list.js @@ -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 . */ 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'); diff --git a/test/unit/common.test.js b/test/unit/common.test.js index 9625d2e..25a79d3 100644 --- a/test/unit/common.test.js +++ b/test/unit/common.test.js @@ -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 });