joyent/node-triton#113 error help for usage errors

This builds on cmdln 4.x's "errHelp" facilities.
This commit is contained in:
Trent Mick 2016-06-08 14:13:16 -07:00
parent 42d5382c7e
commit fabe0a0841
76 changed files with 567 additions and 426 deletions

View File

@ -5,9 +5,31 @@ Known issues:
- `triton ssh ...` disables ssh ControlMaster to avoid issue #52.
## 4.12.1 (not yet released)
## 4.13.0 (not yet released)
(nothing yet)
- [#113] *Usage* errors now some "error help", including option or command
synopses. Some examples (the new thing is marked with `>`):
- Command synopses when argument errors:
$ triton create
triton instance create: error (Usage): incorrect number of args
> usage: triton instance create [OPTIONS] IMAGE PACKAGE
- Option synopsis with option errors:
$ triton image ls --bogus
triton image ls: error (Option): unknown option: "--bogus"
> usage: triton image ls [ --help | -h ] [ --all | -a ] [ -H ] [ -o field1,... ]
> [ --long | -l ] [ -s field1,... ] [ --json | -j ] ...
- Suggested command name misspellings:
$ triton in
triton: error (UnknownCommand): unknown command: "in"
> Did you mean this?
> info
> inst
## 4.12.0

View File

@ -342,7 +342,7 @@ CLI.prototype.fini = function fini(subcmd, err, cb) {
this._tritonapi.close();
delete this._tritonapi;
}
cb(err, subcmd);
cb();
};
@ -667,7 +667,7 @@ function main(argv) {
}
var cli = new CLI();
cli.main(argv, function (err, subcmd) {
cli.main(argv, function (err) {
var exitStatus = (err ? err.exitStatus || 1 : 0);
var showErr = (cli.showErr !== undefined ? cli.showErr : true);
@ -677,7 +677,7 @@ function main(argv) {
/* jsl:pass */
} else if (err.message !== undefined) {
/*
* If the err has `body.errors` (as some Triton/SDC APIs do per
* If the err has `body.errors`, as some Triton/SDC APIs do per
* // JSSTYLED
* https://github.com/joyent/eng/blob/master/docs/index.md#error-handling
* then append a one-line summary for each error object.
@ -692,27 +692,14 @@ function main(argv) {
});
}
console.error('%s%s: error%s: %s%s',
cli.name,
(subcmd ? ' ' + subcmd : ''),
console.error('%s: error%s: %s%s',
cmdln.nameFromErr(err),
(code ? format(' (%s)', code) : ''),
(cli.showErrStack ? err.stack : err.message),
bodyErrors);
// If this is a usage error, attempt to show some usage info.
if (['Usage', 'Option'].indexOf(code) !== -1 && subcmd) {
var help = cli.helpFromSubcmd(subcmd);
if (help && typeof (help) === 'string') {
// Would like a shorter synopsis. Attempt to
// parse it down, somewhat generally. Unfortunately this
// doesn't work for multi-level subcmds, like
// `triton rbac subcmd ...`.
var usageIdx = help.indexOf('\nUsage:');
if (usageIdx !== -1) {
help = help.slice(usageIdx);
}
console.error(help);
}
var errHelp = cmdln.errHelpFromErr(err);
if (errHelp) {
console.error(errHelp);
}
}
}
@ -728,6 +715,7 @@ function main(argv) {
});
}
//---- exports
module.exports = {

View File

@ -129,6 +129,8 @@ function jsonStream(arr, stream) {
*
* @param {Array} kvs - an array of key=value pairs
* @param {Array} valid (optional) - an array to validate pairs
*
* TODO: merge this with objFromKeyValueArgs !
*/
function kvToObj(kvs, valid) {
assert.arrayOfString(kvs, 'kvs');

View File

@ -58,13 +58,15 @@ do_get.options = [
help: 'JSON output.'
}
];
do_get.help = (
'Show account information\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} get\n'
+ '\n'
+ '{{options}}'
);
do_get.synopses = ['{{name}} {{cmd}}'];
do_get.help = [
'Show account information',
'',
'{{usage}}',
'',
'{{options}}'
].join('\n');
module.exports = do_get;

View File

@ -148,13 +148,17 @@ do_update.options = [
'JSON from stdin.'
}
];
do_update.synopses = [
'{{name}} {{cmd}} [FIELD=VALUE ...]',
'{{name}} {{cmd}} -f JSON-FILE'
];
do_update.help = [
/* BEGIN JSSTYLED */
'Update account information',
'',
'Usage:',
' {{name}} update [FIELD=VALUE ...]',
' {{name}} update -f JSON-FILE',
'{{usage}}',
'',
'{{options}}',

View File

@ -57,7 +57,7 @@ function do_cloudapi(subcmd, opts, args, callback) {
reqOpts.data = JSON.parse(opts.data);
} catch (parseErr) {
callback(new errors.TritonError(parseErr,
'given <data> is not valid JSON: ' + parseErr.message));
'given DATA is not valid JSON: ' + parseErr.message));
return;
}
}
@ -94,13 +94,13 @@ do_cloudapi.options = [
{
names: ['method', 'X'],
type: 'string',
helpArg: '<method>',
helpArg: 'METHOD',
help: 'Request method to use. Default is "GET".'
},
{
names: ['header', 'H'],
type: 'arrayOfString',
helpArg: '<header>',
helpArg: 'HEADER',
help: 'Headers to send with request.'
},
{
@ -111,19 +111,25 @@ do_cloudapi.options = [
{
names: ['data', 'd'],
type: 'string',
helpArg: '<data>',
helpArg: 'DATA',
help: 'Add POST data. This must be valid JSON.'
}
];
do_cloudapi.help = (
'Raw cloudapi request.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} cloudapi [-X <method>] [-H <header=value>] \\\n'
+ ' [-d <data>] <endpoint>\n'
+ '\n'
+ '{{options}}'
);
do_cloudapi.synopses = [
'{{name}} {{cmd}} [-X METHOD] [-H HEADER=VAL] [-d DATA] ENDPOINT'
];
do_cloudapi.help = [
'Raw cloudapi request.',
'',
'{{usage}}',
'',
'{{options}}',
'Examples:',
' {{name}} {{cmd}} /--ping',
' {{name}} {{cmd}} /my/machines'
].join('\n');
do_cloudapi.hidden = true;

View File

@ -75,15 +75,16 @@ do_datacenters.options = [
sortDefault: sortDefault
}));
do_datacenters.help = (
'Show datacenters in this cloud.\n'
+ 'A "cloud" is a set of related datacenters that share account\n'
+ 'information.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} datacenters\n'
+ '\n'
+ '{{options}}'
);
do_datacenters.synopses = ['{{name}} {{cmd}}'];
do_datacenters.help = [
'Show datacenters in this cloud.',
'A "cloud" is a set of related datacenters that share account',
'information.',
'',
'{{usage}}',
'',
'{{options}}'
].join('\n');
module.exports = do_datacenters;

View File

@ -198,7 +198,8 @@ do_env.options = [
}
];
// TODO: support env for docker usage.
do_env.synopses = ['{{name}} {{cmd}} [PROFILE]'];
do_env.help = [
/* BEGIN JSSTYLED */
'Emit shell commands to setup environment.',
@ -207,8 +208,7 @@ do_env.help = [
'and node-triton itself. By default this emits the environment for all',
'supported tools. Use options to be specific.',
'',
'Usage:',
' {{name}} env [PROFILE]',
'{{usage}}',
'',
'{{options}}',
'If no options are given, environment variables are emitted for all clients.',

View File

@ -28,7 +28,7 @@ function do_create(subcmd, opts, args, cb) {
return;
}
if (args.length === 0) {
cb(new errors.UsageError('missing <fwrule> argument'));
cb(new errors.UsageError('missing FWRULE argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('incorrect number of arguments'));
@ -79,17 +79,31 @@ do_create.options = [
{
names: ['description', 'D'],
type: 'string',
helpArg: '<desc>',
helpArg: 'DESC',
help: 'Description of the firewall rule.'
}
];
do_create.synopses = ['{{name}} {{cmd}} [OPTIONS] RULE-TEXT'];
do_create.help = [
/* BEGIN JSSTYLED */
'Create a firewall rule.',
'',
'Usage:',
' {{name}} create [<options>] <fwrule>',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Examples:',
' # Allow SSH access from any IP to all instances in a datacenter.',
' triton fwrule create -D "ssh" "FROM any TO all vms ALLOW tcp PORT 22"',
'',
' # Allow SSH access to a specific instance.',
' triton fwrule create \\',
' "FROM any TO vm ba2c95e9-1cdf-4295-8253-3fee371374d9 ALLOW tcp PORT 22"'
// TODO: link to
// https://github.com/joyent/sdc-fwrule/blob/master/docs/examples.md
// or docs.jo Cloud Firewall examples? What link? Ditto in parent.
/* END JSSTYLED */
].join('\n');
do_create.helpOpts = {

View File

@ -27,7 +27,7 @@ function do_delete(subcmd, opts, args, cb) {
}
if (args.length < 1) {
cb(new errors.UsageError('missing <fwrule-id> argument(s)'));
cb(new errors.UsageError('missing FWRULE argument(s)'));
return;
}
@ -96,13 +96,16 @@ do_delete.options = [
help: 'Skip confirmation of delete.'
}
];
do_delete.synopses = ['{{name}} {{cmd}} FWRULE [FWRULE ...]'];
do_delete.help = [
'Remove a firewall rule.',
'',
'Usage:',
' {{name}} delete [<options>] <fwrule-id> [<fwrule-id>...]',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where FWRULE is a firewall rule id (full UUID) or short id.'
].join('\n');
do_delete.aliases = ['rm'];

View File

@ -26,7 +26,7 @@ function do_disable(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('Missing <fwrule-id> argument(s)'));
cb(new errors.UsageError('missing FWRULE argument(s)'));
return;
}
@ -56,13 +56,16 @@ do_disable.options = [
help: 'Show this help.'
}
];
do_disable.synopses = ['{{name}} {{cmd}} FWRULE [FWRULE ...]'];
do_disable.help = [
'Disable a specific firewall rule.',
'',
'Usage:',
' {{name}} disable <fwrule-id> [<fwrule-id>...]',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where FWRULE is a firewall rule id (full UUID) or short id.'
].join('\n');
do_disable.completionArgtypes = ['tritonfwrule'];

View File

@ -26,7 +26,7 @@ function do_enable(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('Missing <fwrule-id> argument(s)'));
cb(new errors.UsageError('missing FWRULE argument(s)'));
return;
}
@ -56,13 +56,16 @@ do_enable.options = [
help: 'Show this help.'
}
];
do_enable.synopses = ['{{name}} {{cmd}} FWRULE [FWRULE ...]'];
do_enable.help = [
'Enable a specific firewall rule.',
'',
'Usage:',
' {{name}} enable <fwrule-id> [<fwrule-id>...]',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where FWRULE is a firewall rule id (full UUID) or short id.'
].join('\n');
do_enable.completionArgtypes = ['tritonfwrule'];

View File

@ -25,7 +25,7 @@ function do_get(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('missing <fwrule-id> argument'));
cb(new errors.UsageError('missing FWRULE argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('incorrect number of arguments'));
@ -64,13 +64,16 @@ do_get.options = [
help: 'JSON stream output.'
}
];
do_get.synopses = ['{{name}} {{cmd}} FWRULE'];
do_get.help = [
'Show a specific firewall rule.',
'',
'Usage:',
' {{name}} get <fwrule-id>',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where FWRULE is a firewall rule id (full UUID) or short id.'
].join('\n');
do_get.completionArgtypes = ['tritonfwrule', 'none'];

View File

@ -30,7 +30,7 @@ function do_instances(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('missing <fwrule-id> argument'));
cb(new errors.UsageError('missing FWRULE argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('incorrect number of arguments'));
@ -135,14 +135,16 @@ do_instances.options = [
sortDefault: SORT_DEFAULT
}));
do_instances.synopses = ['{{name}} {{cmd}} [OPTIONS] FWRULE'];
do_instances.help = [
/* BEGIN JSSTYLED */
'List instances a firewall rule is applied to.',
'List instances to which a firewall rule applies',
'',
'Usage:',
' {{name}} instances [<options>] <fwrule-id>',
'{{usage}}',
'',
'{{options}}',
'Where FWRULE is a firewall rule id (full UUID) or short id.',
'',
'Fields (most are self explanatory, "*" indicates a field added client-side',
'for convenience):',

View File

@ -84,11 +84,12 @@ do_list.options = [
sortDefault: SORT_DEFAULT
}));
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_list.help = [
'Show all firewall rules.',
'',
'Usage:',
' {{name}} list [<options>]',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -31,7 +31,7 @@ function do_update(subcmd, opts, args, cb) {
var tritonapi = this.top.tritonapi;
if (args.length === 0) {
cb(new errors.UsageError('missing <fwrule-id> argument'));
cb(new errors.UsageError('missing FWRULE argument'));
return;
}
@ -164,17 +164,20 @@ do_update.options = [
{
names: ['file', 'f'],
type: 'string',
helpArg: '<json-file>',
helpArg: 'JSON-FILE',
help: 'A file holding a JSON file of updates, or "-" to read ' +
'JSON from stdin.'
}
];
do_update.synopses = [
'{{name}} {{cmd}} FWRULE [FIELD=VALUE ...]',
'{{name}} {{cmd}} -f JSON-FILE FWRULE'
];
do_update.help = [
'Update a firewall rule',
'',
'Usage:',
' {{name}} update <fwrule-id> [FIELD=VALUE ...]',
' {{name}} update -f <json-file> <fwrule-id>',
'{{usage}}',
'',
'{{options}}',
@ -182,7 +185,8 @@ do_update.help = [
' ' + Object.keys(UPDATE_FWRULE_FIELDS).sort().map(function (f) {
return f + ' (' + UPDATE_FWRULE_FIELDS[f] + ')';
}).join('\n '),
''
'',
'Where FWRULE is a firewall rule id (full UUID) or short id.'
].join('\n');
do_update.completionArgtypes = ['tritonfwrule', 'tritonupdatefwrulefield'];

View File

@ -250,16 +250,20 @@ do_create.options = [
}
];
do_create.help = (
do_create.synopses = [
'{{name}} {{cmd}} [OPTIONS] INST IMAGE-NAME IMAGE-VERSION'
];
do_create.help = [
/* BEGIN JSSTYLED */
'Create a new instance.\n' +
'\n' +
'Usage:\n' +
' {{name}} create [<options>] INSTANCE IMAGE-NAME IMAGE-VERSION\n' +
'\n' +
'{{options}}'
'Create a new instance.',
'',
'{{usage}}',
'',
'{{options}}',
'Where "INST" is an instance name, id, or short id.'
/* END JSSTYLED */
);
].join('\n');
do_create.helpOpts = {
maxHelpCol: 20

View File

@ -125,19 +125,21 @@ function do_delete(subcmd, opts, args, cb) {
});
}
do_delete.synopses = ['{{name}} {{cmd}} [OPTIONS] IMAGE [IMAGE ...]'];
do_delete.help = [
/* BEGIN JSSTYLED */
'Delete one or more images.',
'',
'Usage:',
' {{name}} delete IMAGE [IMAGE...]',
'{{usage}}',
'',
'{{options}}',
'Where "IMAGE" is an image ID (a full UUID), an image name (selects the',
'Where "IMAGE" is an image id (a full UUID), an image name (selects the',
'latest, by "published_at", image with that name), an image "name@version"',
'(selects latest match by "published_at"), or an image short ID (ID prefix).'
/* END JSSTYLED */
].join('\n');
do_delete.options = [
{
names: ['help', 'h'],

View File

@ -50,22 +50,24 @@ do_get.options = [
help: 'JSON stream output.'
}
];
do_get.help = (
do_get.synopses = ['{{name}} {{cmd}} [OPTIONS] IMAGE'];
do_get.help = [
/* BEGIN JSSTYLED */
'Get an image.\n' +
'\n' +
'Usage:\n' +
' {{name}} get [<options>] ID|NAME\n' +
'\n' +
'{{options}}' +
'\n' +
'If there is more than one image with the given "NAME", the latest\n' +
'image (by "published_at") is returned.\n' +
'\n' +
'Note: Currently this dumps prettified JSON by default. That might change\n' +
'in the future. Use "-j" to explicitly get JSON output.\n'
'Get an image.',
'',
'{{usage}}',
'',
'{{options}}',
'Where "IMAGE" is an image id (a full UUID), an image name (selects the',
'latest, by "published_at", image with that name), an image "name@version"',
'(selects latest match by "published_at"), or an image short ID (ID prefix).',
'',
'Note: Currently this dumps prettified JSON by default. That might change',
'in the future. Use "-j" to explicitly get JSON output.'
/* END JSSTYLED */
);
].join('\n');
do_get.completionArgtypes = ['tritonimage', 'none'];

View File

@ -122,6 +122,8 @@ do_list.options = [
sortDefault: sortDefault
}));
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS] [FILTERS]'];
do_list.help = [
/* BEGIN JSSTYLED */
'List images.',
@ -130,8 +132,7 @@ do_list.help = [
'You must use `docker images` against the Docker service for this data center.',
'See <https://apidocs.joyent.com/docker>.',
'',
'Usage:',
' {{name}} list [<options>] [<filters>]',
'{{usage}}',
'',
'{{options}}',
'Filters:',

View File

@ -112,11 +112,12 @@ function do_wait(subcmd, opts, args, cb) {
});
}
do_wait.synopses = ['{{name}} {{cmd}} [-s STATES] IMAGE [IMAGE ...]'];
do_wait.help = [
'Wait for images to change to a particular state.',
'',
'Usage:',
' {{name}} wait [-s STATES] IMAGE [IMAGE ...]',
'{{usage}}',
'',
'{{options}}',
'Where "states" is a comma-separated list of target instance states,',

View File

@ -105,13 +105,15 @@ do_info.options = [
help: 'JSON output.'
}
];
do_info.help = (
'Print an account summary.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} info\n'
+ '\n'
+ '{{options}}'
);
do_info.synopses = ['{{name}} {{cmd}}'];
do_info.help = [
'Print an account summary.',
'',
'{{usage}}',
'',
'{{options}}'
].join('\n');
module.exports = do_info;

View File

@ -28,13 +28,15 @@ var sortDefault = 'id,time';
function do_audit(subcmd, opts, args, cb) {
var self = this;
if (opts.help) {
this.do_help('help', {}, [subcmd], cb);
return;
} else if (args.length !== 1) {
//XXX Support multiple machines.
return cb(new Error('incorrect args: ' + args));
} else if (args.length === 0) {
cb(new errors.UsageError('missing INST argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('too many arguments: ' + args));
return;
}
var columns = columnsDefault;
@ -102,14 +104,15 @@ do_audit.options = [
sortDefault: sortDefault
}));
do_audit.help = (
'List instance actions.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} audit <alias|id>\n'
+ '\n'
+ '{{options}}'
);
do_audit.synopses = ['{{name}} {{cmd}} [OPTIONS] INST'];
do_audit.help = [
'List instance actions.',
'',
'{{usage}}',
'',
'{{options}}',
'Where "INST" is an instance name, id, or short id.'
].join('\n');
do_audit.completionArgtypes = ['tritoninstance', 'none'];

View File

@ -498,16 +498,20 @@ do_create.options = [
}
];
do_create.help = (
do_create.synopses = ['{{name}} {{cmd}} [OPTIONS] IMAGE PACKAGE'];
do_create.help = [
/* BEGIN JSSTYLED */
'Create a new instance.\n' +
'\n' +
'Usage:\n' +
' {{name}} create [<options>] IMAGE PACKAGE\n' +
'\n' +
'{{options}}'
'Create a new instance.',
'',
'{{usage}}',
'',
'{{options}}',
'Where IMAGE is an image name, name@version, id, or short id (from ',
'`triton image list`) and PACKAGE is a package name, id, or short id',
'(from `triton package list`).'
/* END JSSTYLED */
);
].join('\n');
do_create.helpOpts = {
maxHelpCol: 16

View File

@ -26,7 +26,7 @@ function do_disable_firewall(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('Missing <inst> argument(s)'));
cb(new errors.UsageError('missing INST argument(s)'));
return;
}
@ -88,13 +88,16 @@ do_disable_firewall.options = [
help: 'Wait for the firewall to be disabled.'
}
];
do_disable_firewall.synopses = [
'{{name}} disable-firewall [OPTIONS] INST [INST ...]'
];
do_disable_firewall.help = [
'Disable the firewall of one or more instances.',
'',
'Usage:',
' {{name}} disable-firewall [<options>] <inst> [<inst>...]',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where "INST" is an instance name, id, or short id.'
].join('\n');
do_disable_firewall.completionArgtypes = ['tritoninstance'];

View File

@ -26,7 +26,7 @@ function do_enable_firewall(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('Missing <inst> argument(s)'));
cb(new errors.UsageError('missing INST argument(s)'));
return;
}
@ -88,13 +88,16 @@ do_enable_firewall.options = [
help: 'Wait for the firewall to be enabled.'
}
];
do_enable_firewall.synopses = [
'{{name}} enable-firewall [OPTIONS] INST [INST ...]'
];
do_enable_firewall.help = [
'Enable the firewall of one or more instances.',
'',
'Usage:',
' {{name}} enable-firewall [<options>] <inst> [<inst>...]',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where "INST" is an instance name, id, or short id.'
].join('\n');
do_enable_firewall.completionArgtypes = ['tritoninstance'];

View File

@ -31,10 +31,10 @@ function do_fwrules(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('missing <inst> argument'));
cb(new errors.UsageError('missing INST argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('incorrect number of arguments'));
cb(new errors.UsageError('too many arguments: ' + args.join(' ')));
return;
}
@ -91,13 +91,15 @@ do_fwrules.options = [
sortDefault: SORT_DEFAULT
}));
do_fwrules.synopses = ['{{name}} {{cmd}} [OPTIONS] INST'];
do_fwrules.help = [
'Show firewall rules applied to an instance.',
'',
'Usage:',
' {{name}} fwrules [<options>] <inst>',
'{{usage}}',
'',
'{{options}}'
'{{options}}',
'Where "INST" is an instance name, id, or short id.'
].join('\n');
do_fwrules.completionArgtypes = ['tritoninstance', 'none'];

View File

@ -43,22 +43,24 @@ do_get.options = [
help: 'JSON output.'
}
];
do_get.help = (
do_get.synopses = ['{{name}} {{cmd}} [OPTIONS] INST'];
do_get.help = [
/* BEGIN JSSTYLED */
'Get an instance.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} get <alias|id>\n'
+ '\n'
+ '{{options}}'
+ '\n'
+ 'A *deleted* instance may still respond with the instance object. In that\n'
+ 'case a the instance will be print *and* an error will be raised.\n'
+ '\n'
+ 'Currently this dumps prettified JSON by default. That might change\n'
+ 'in the future. Use "-j" to explicitly get JSON output.\n'
'Get an instance.',
'',
'{{usage}}',
'',
'{{options}}',
'',
'Where "INST" is an instance name, id, or short id.',
'',
'A *deleted* instance may still respond with the instance object. In that',
'case a the instance will be print *and* an error will be raised.',
'',
'Currently this dumps prettified JSON by default. That might change',
'in the future. Use "-j" to explicitly get JSON output.'
/* END JSSTYLED */
);
].join('\n');
do_get.completionArgtypes = ['tritoninstance', 'none'];

View File

@ -20,10 +20,10 @@ function do_ip(subcmd, opts, args, cb) {
this.do_help('help', {}, [subcmd], cb);
return;
} else if (args.length === 0) {
cb(new errors.UsageError('missing <inst> argument'));
cb(new errors.UsageError('missing INST argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('too many arguments: ' + args));
cb(new errors.UsageError('too many arguments: ' + args.join(' ')));
return;
}
@ -54,14 +54,16 @@ do_ip.options = [
}
];
do_ip.synopses = ['{{name}} {{cmd}} INST'];
do_ip.help = [
/* BEGIN JSSTYLED */
'Print the primaryIp of the given instance.',
'Print the primary IP of the given instance.',
'',
'Usage:',
' {{name}} ip <inst>',
'{{usage}}',
'',
'{{options}}',
'Where "INST" is an instance name, id, or short id.',
'For example: ssh root@$(triton ip my-instance)'
].join('\n');

View File

@ -159,12 +159,13 @@ do_list.options = [
sortDefault: sortDefault
}));
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS] [FILTERS...]'];
do_list.help = [
/* BEGIN JSSTYLED */
'List instances.',
'',
'Usage:',
' {{name}} list [<filters>...]',
'{{usage}}',
'',
'{{options}}',
'Filters:',

View File

@ -114,7 +114,7 @@ do_create.options = [
{
names: ['name', 'n'],
type: 'string',
helpArg: '<snapname>',
helpArg: 'SNAPNAME',
help: 'An optional name for a snapshot.'
},
{
@ -123,11 +123,13 @@ do_create.options = [
help: 'Wait for the creation to complete.'
}
];
do_create.synopses = ['{{name}} {{cmd}} [OPTIONS] INST'];
do_create.help = [
'Create a snapshot of an instance.',
'',
'Usage:',
' {{name}} create [<options>] <inst>',
'{{usage}}',
'',
'{{options}}',
'Snapshot do not work for instances of type "kvm".'

View File

@ -27,7 +27,7 @@ function do_delete(subcmd, opts, args, cb) {
}
if (args.length < 2) {
cb(new errors.UsageError('missing <inst> and <snapname> argument(s)'));
cb(new errors.UsageError('missing INST and SNAPNAME argument(s)'));
return;
}
@ -139,11 +139,13 @@ do_delete.options = [
help: 'Wait for the deletion to complete.'
}
];
do_delete.synopses = ['{{name}} {{cmd}} [OPTIONS] INST SNAPNAME [SNAPNAME...]'];
do_delete.help = [
'Remove a snapshot from an instance.',
'',
'Usage:',
' {{name}} delete [<options>] <inst> <snapname> [<snapname>...]',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -25,7 +25,7 @@ function do_get(subcmd, opts, args, cb) {
}
if (args.length < 2) {
cb(new errors.UsageError('missing <inst> and/or <snapname> arguments'));
cb(new errors.UsageError('missing INST and/or SNAPNAME arguments'));
return;
} else if (args.length > 2) {
cb(new errors.UsageError('incorrect number of arguments'));
@ -55,7 +55,6 @@ function do_get(subcmd, opts, args, cb) {
});
}
do_get.options = [
{
names: ['help', 'h'],
@ -68,11 +67,13 @@ do_get.options = [
help: 'JSON stream output.'
}
];
do_get.synopses = ['{{name}} {{cmd}} [OPTIONS] INST SNAPNAME'];
do_get.help = [
'Show a specific snapshot of an instance.',
'',
'Usage:',
' {{name}} get <inst> <snapname>',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -30,7 +30,7 @@ function do_list(subcmd, opts, args, cb) {
}
if (args.length === 0) {
cb(new errors.UsageError('missing <inst> argument'));
cb(new errors.UsageError('missing INST argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('incorrect number of arguments'));
@ -84,11 +84,12 @@ do_list.options = [
sortDefault: SORT_DEFAULT
}));
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS] INST'];
do_list.help = [
'Show all of an instance\'s snapshots.',
'',
'Usage:',
' {{name}} list [<options>] <inst>',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -31,7 +31,7 @@ function SnapshotCLI(top) {
'delete'
],
helpBody: 'Instances can be rolled back to a snapshot using\n' +
'`triton instance start --snapshot=<snapname>`.'
'`triton instance start --snapshot=SNAPNAME`.'
});
}
util.inherits(SnapshotCLI, Cmdln);

View File

@ -14,6 +14,7 @@ var path = require('path');
var spawn = require('child_process').spawn;
var common = require('../common');
var errors = require('../errors');
function do_ssh(subcmd, opts, args, callback) {
@ -23,7 +24,7 @@ function do_ssh(subcmd, opts, args, callback) {
this.do_help('help', {}, [subcmd], callback);
return;
} else if (args.length === 0) {
callback(new Error('invalid args: ' + args));
callback(new errors.UsageError('missing INST arg'));
return;
}
@ -94,23 +95,23 @@ do_ssh.options = [
help: 'Show this help.'
}
];
do_ssh.synopses = ['{{name}} ssh [-h] INST [SSH-ARGUMENTS]'];
do_ssh.help = [
/* BEGIN JSSTYLED */
'SSH to the primary IP of an instance',
'',
'Usage:',
' {{name}} ssh [-h] [-M] <inst> [<ssh-arguments>]',
'{{usage}}',
'',
'{{options}}',
'Where <inst> is the name, short ID or ID of a given instance. Note that',
'the <inst> argument must come before any `ssh` options or arguments.',
'Where INST is the name, id, or short id of an instance. Note that',
'the INST argument must come before any `ssh` options or arguments.',
'',
'There is a known issue with SSH connection multiplexing (a.k.a. ',
'ControlMaster, mux) where stdout/stderr is lost. As a workaround, `ssh`',
'is spawned with options disabling ControlMaster. See ',
'<https://github.com/joyent/node-triton/issues/52> for details. If you ',
'want to use ControlMaster, an alternative is:',
' ssh root@$(triton ip <inst>)'
' ssh root@$(triton ip INST)'
/* END JSSTYLED */
].join('\n');

View File

@ -91,20 +91,21 @@ do_delete.options = [
}
];
do_delete.synopses = [
'{{name}} {{cmd}} INST [NAME ...]',
'{{name}} {{cmd}} --all INST # delete all tags'
];
do_delete.help = [
/* BEGIN JSSTYLED */
'Delete one or more instance tags.',
'',
'Usage:',
' {{name}} delete <inst> [<name> ...]',
' {{name}} delete --all <inst> # delete all tags',
'{{usage}}',
'',
'{{options}}',
'Where <inst> is an instance id, name, or shortid and <name> is a tag name.',
'Where INST is an instance id, name, or shortid and NAME is a tag name.',
'',
'Changing instance tags is asynchronous. Use "--wait" to not return until',
'the changes are completed.'
/* END JSSTYLED */
].join('\n');
do_delete.aliases = ['rm'];

View File

@ -53,16 +53,15 @@ do_get.options = [
}
];
do_get.synopses = ['{{name}} {{cmd}} INST NAME'];
do_get.help = [
/* BEGIN JSSTYLED */
'Get an instance tag.',
'',
'Usage:',
' {{name}} get <inst> <name>',
'{{usage}}',
'',
'{{options}}',
'Where <inst> is an instance id, name, or shortid and <name> is a tag name.'
/* END JSSTYLED */
'Where INST is an instance id, name, or shortid and NAME is a tag name.'
].join('\n');
// TODO: When have 'tritoninstancetag' completion, add that in.

View File

@ -49,19 +49,18 @@ do_list.options = [
}
];
do_list.synopses = ['{{name}} {{cmd}} INST'];
do_list.help = [
/* BEGIN JSSTYLED */
'List instance tags.',
'',
'Usage:',
' {{name}} list <inst>',
'{{usage}}',
'',
'{{options}}',
'Where <inst> is an instance id, name, or shortid.',
'Where INST is an instance id, name, or shortid.',
'',
'Note: Currently this dumps prettified JSON by default. That might change',
'in the future. Use "-j" to explicitly get JSON output.'
/* END JSSTYLED */
].join('\n');
do_list.aliases = ['ls'];

View File

@ -108,17 +108,19 @@ do_replace_all.options = [
}
];
do_replace_all.synopses = [
'{{name}} {{cmd}} INST [NAME=VALUE ...]',
'{{name}} {{cmd}} INST -f FILE # tags from file'
];
do_replace_all.help = [
/* BEGIN JSSTYLED */
'Replace all tags on the given instance.',
'',
'Usage:',
' {{name}} replace-all <inst> [<name>=<value> ...]',
' {{name}} replace-all <inst> -f <file> # tags from file',
'{{usage}}',
'',
'{{options}}',
'Where <inst> is an instance id, name, or shortid; <name> is a tag name;',
'and <value> is a tag value (bool and numeric "value" are converted to ',
'Where INST is an instance id, name, or shortid; NAME is a tag name;',
'and VALUE is a tag value (bool and numeric "value" are converted to ',
'that type).',
'',
'Currently this dumps prettified JSON by default. That might change in the',
@ -126,7 +128,6 @@ do_replace_all.help = [
'',
'Changing instance tags is asynchronous. Use "--wait" to not return until',
'the changes are completed.'
/* END JSSTYLED */
].join('\n');
do_replace_all.completionArgtypes = ['tritoninstance', 'file'];

View File

@ -109,17 +109,20 @@ do_set.options = [
}
];
do_set.synopses = [
'{{name}} set INST [NAME=VALUE ...]',
'{{name}} set INST -f FILE # tags from file'
];
do_set.help = [
/* BEGIN JSSTYLED */
'Set one or more instance tags.',
'',
'Usage:',
' {{name}} set <inst> [<name>=<value> ...]',
' {{name}} set <inst> -f <file> # tags from file',
'{{usage}}',
'',
'{{options}}',
'Where <inst> is an instance id, name, or shortid; <name> is a tag name;',
'and <value> is a tag value (bool and numeric "value" are converted to ',
'Where INST is an instance id, name, or shortid; NAME is a tag name;',
'and VALUE is a tag value (bool and numeric "value" are converted to ',
'that type).',
'',
'Currently this dumps prettified JSON by default. That might change in the',

View File

@ -21,7 +21,7 @@ function do_wait(subcmd, opts, args, cb) {
if (opts.help) {
return this.do_help('help', {}, [subcmd], cb);
} else if (args.length < 1) {
return cb(new errors.UsageError('missing INSTANCE arg(s)'));
return cb(new errors.UsageError('missing INST arg(s)'));
}
var ids = args;
var states = [];
@ -110,16 +110,19 @@ function do_wait(subcmd, opts, args, cb) {
});
}
do_wait.synopses = ['{{name}} {{cmd}} [-s STATES] INST [INST ...]'];
do_wait.help = [
/* BEGIN JSSTYLED */
'Wait on instances changing state.',
'',
'Usage:',
' {{name}} wait [-s STATES] INSTANCE [INSTANCE ...]',
'{{usage}}',
'',
'{{options}}',
'Where "states" is a comma-separated list of target instance states,',
'by default "running,failed". In other words, "triton inst wait foo0" will',
'wait for instance "foo0" to complete provisioning.'
'Where "INST" is an instance name, id, or short id; and "STATES" is a',
'comma-separated list of target instance states, by default "running,failed".',
'In other words, "triton inst wait foo0" will wait for instance "foo0" to',
'complete provisioning.'
/* END JSSTYLED */
].join('\n');
do_wait.options = [
{

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright 2015 Joyent, Inc.
* Copyright 2016 Joyent, Inc.
*
* Shared support for:
* `triton instance start ...`
@ -18,9 +18,7 @@ var assert = require('assert-plus');
var vasync = require('vasync');
var common = require('../common');
var f = require('util').format;
var errors = require('../errors');
function perror(err) {
@ -40,18 +38,20 @@ function gen_do_ACTION(opts) {
function do_ACTION(subcmd, _opts, args, callback) {
return _doTheAction.call(this, action, subcmd, _opts, args, callback);
}
do_ACTION.name = 'do_' + action;
if (opts.aliases) {
do_ACTION.aliases = opts.aliases;
}
do_ACTION.synopses = ['{{name}} ' + action + ' [OPTIONS] INST [INST ...]'];
do_ACTION.help = [
f('%s one or more instances.', common.capitalize(action)),
f(''),
f('Usage:'),
f(' {{name}} %s <alias|id> ...', action),
f(''),
f('{{options}}')
common.capitalize(action) + ' one or more instances.',
'',
'{{usage}}',
'',
'{{options}}',
'Where "INST" is an instance name, id, or short id.'
].join('\n');
do_ACTION.options = [
{
@ -72,7 +72,8 @@ function gen_do_ACTION(opts) {
do_ACTION.options.push({
names: ['snapshot'],
type: 'string',
help: 'Name of snapshot to start machine with.'
help: 'Name of snapshot with which to start the instance.',
helpArg: 'SNAPNAME'
});
}
@ -112,7 +113,7 @@ function _doTheAction(action, subcmd, opts, args, callback) {
this.do_help('help', {}, [subcmd], callback);
return;
} else if (args.length < 1) {
callback(new Error('invalid args: ' + args));
callback(new errors.UsageError('missing INST arg(s)'));
return;
}

View File

@ -125,11 +125,13 @@ do_add.options = [
help: 'An optional name for an added key.'
}
];
do_add.synopses = ['{{name}} {{cmd}} [OPTIONS] FILE'];
do_add.help = [
'Add an SSH key to an account.',
'',
'Usage:',
' {{name}} add [<options>] FILE',
'{{usage}}',
'',
'{{options}}',
'Where "FILE" must be a file path to an SSH public key, ',

View File

@ -104,11 +104,13 @@ do_delete.options = [
help: 'Answer yes to confirmation to delete.'
}
];
do_delete.synopses = ['{{name}} {{cmd}} [OPTIONS] KEY [KEY ...]'];
do_delete.help = [
'Remove an SSH key from an account.',
'',
'Usage:',
' {{name}} delete [<options>] KEY [KEY...]',
'{{usage}}',
'',
'{{options}}',
'Where "KEY" is an SSH key "name" or "fingerprint".'

View File

@ -67,11 +67,13 @@ do_get.options = [
help: 'JSON stream output.'
}
];
do_get.synopses = ['{{name}} {{cmd}} KEY'];
do_get.help = [
'Show a specific SSH key in an account.',
'',
'Usage:',
' {{name}} get KEY',
'{{usage}}',
'',
'{{options}}',
'Where "KEY" is an SSH key "name" or "fingerprint".'

View File

@ -90,11 +90,11 @@ do_list.options = [
}
]);
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_list.help = [
'Show all of an account\'s SSH keys.',
'',
'Usage:',
' {{name}} list [<options>]',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -51,14 +51,17 @@ do_get.options = [
help: 'JSON output.'
}
];
do_get.help = (
'Show a network.\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} get <id|name>\n'
+ '\n'
+ '{{options}}'
);
do_get.synopses = ['{{name}} {{cmd}} NETWORK'];
do_get.help = [
'Show a network.',
'',
'{{usage}}',
'',
'{{options}}',
'Where NETWORK is a network id (full UUID), name, or short id.'
].join('\n');
do_get.completionArgtypes = ['tritonnetwork', 'none'];

View File

@ -85,17 +85,17 @@ do_list.options = [
sortDefault: sortDefault
}));
do_list.synopses = ['{{name}} {{cmd}}'];
do_list.help = [
'List available networks.',
'',
'Usage:',
' {{name}} list',
'{{usage}}',
'',
'{{options}}',
'Fields (most are self explanatory, the client adds some for convenience):',
' vlan A shorter alias for "vlan_id".',
' shortid A short ID prefix.',
'',
'{{options}}'
' shortid A short ID prefix.'
].join('\n');
do_list.aliases = ['ls'];

View File

@ -50,21 +50,23 @@ do_get.options = [
help: 'JSON stream output.'
}
];
do_get.help = (
do_get.synopses = ['{{name}} {{cmd}} [OPTIONS] PACKAGE'];
do_get.help = [
/* BEGIN JSSTYLED */
'Get a package.\n' +
'\n' +
'Usage:\n' +
' {{name}} get [<options>] ID|NAME\n' +
'\n' +
'{{options}}' +
'\n' +
'The given "NAME" must be a unique match.\n' +
'\n' +
'Note: Currently this dumps prettified JSON by default. That might change\n' +
'in the future. Use "-j" to explicitly get JSON output.\n'
'Get a package.',
'',
'{{usage}}',
'',
'{{options}}',
'',
'Where PACKAGE is a package id (full UUID), exact name, or short id.',
'',
'Note: Currently this dumps prettified JSON by default. That might change',
'in the future. Use "-j" to explicitly get JSON output.'
/* END JSSTYLED */
);
].join('\n');
do_get.completionArgtypes = ['tritonpackage', 'none'];

View File

@ -28,10 +28,10 @@ var validFilters = [
];
// columns default without -o
var columnsDefault = 'shortid,name,default,memory,swap,disk,vcpus';
var columnsDefault = 'shortid,name,memory,swap,disk,vcpus';
// columns default with -l
var columnsDefaultLong = 'id,name,default,memory,swap,disk,vcpus';
var columnsDefaultLong = 'id,name,memory,swap,disk,vcpus,description';
// sort default with -s
var sortDefault = '_groupPlus,memory';
@ -154,12 +154,12 @@ do_list.options = [
}
]);
do_list.synopses = ['{{name}} {{cmd}} [FILTERS]'];
do_list.help = [
/* BEGIN JSSTYLED */
'List packages.',
'',
'Usage:',
' {{name}} list [<filters>]',
'{{usage}}',
'',
'{{options}}',
'Filters:',
@ -177,7 +177,7 @@ do_list.help = [
' zero is shown as "-" in tabular output.',
'',
'Examples:',
' {{name}} packages memory=8192 # list packages with 8G RAM'
' {{name}} list memory=8192 # list packages with 8G RAM'
/* END JSSTYLED */
].join('\n');

View File

@ -23,7 +23,11 @@ function PackageCLI(top) {
name: top.name + ' package',
/* BEGIN JSSTYLED */
desc: [
'List and get Triton packages.'
'List and get Triton packages.',
'',
'A package is a collection of attributes -- for example disk quota,',
'amount of RAM -- used when creating an instance. They have a name',
'and ID for identification.'
].join('\n'),
/* END JSSTYLED */
helpOpts: {

View File

@ -366,11 +366,11 @@ do_create.options = [
];
do_create.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_create.help = [
'Create a Triton CLI profile.',
'',
'Usage:',
' {{name}} create <options>',
'{{usage}}',
'',
'{{options}}',
'',

View File

@ -120,11 +120,11 @@ do_delete.options = [
}
];
do_delete.synopses = ['{{name}} {{cmd}} PROFILE'];
do_delete.help = [
'Delete a Triton CLI profile.',
'',
'Usage:',
' {{name}} delete NAME',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -33,12 +33,12 @@ do_docker_setup.options = [
}
];
do_docker_setup.synopses = ['{{name}} {{cmd}} [PROFILE]'];
do_docker_setup.help = [
/* BEGIN JSSTYLED */
'Setup for using Docker with the current Triton CLI profile.',
'',
'Usage:',
' {{name}} docker-setup [PROFILE]',
'{{usage}}',
'',
'{{options}}',
'A Triton datacenter can act as a virtual Docker Engine, where the entire',
@ -54,5 +54,6 @@ do_docker_setup.help = [
/* END JSSTYLED */
].join('\n');
do_docker_setup.completionArgtypes = ['tritonprofile', 'none'];
module.exports = do_docker_setup;

View File

@ -154,11 +154,11 @@ do_edit.options = [
}
];
do_edit.synopses = ['{{name}} {{cmd}} [PROFILE]'];
do_edit.help = [
'Edit a Triton CLI profile in your $EDITOR.',
'',
'Usage:',
' {{name}} edit [NAME]',
'{{usage}}',
'',
'{{options}}'
].join('\n');

View File

@ -74,15 +74,16 @@ do_get.options = [
}
];
do_get.synopses = ['{{name}} {{cmd}} [PROFILE]'];
do_get.help = [
'Get a Triton CLI profile.',
'',
'Usage:',
' {{name}} get [NAME]',
'{{usage}}',
'',
'{{options}}',
'If NAME is not specified, the current profile is shown.'
].join('\n');
do_get.completionArgtypes = ['tritonprofile', 'none'];
module.exports = do_get;

View File

@ -112,22 +112,21 @@ do_list.options = [
includeLong: true,
sortDefault: sortDefault
}));
do_list.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_list.help = [
/* BEGIN JSSTYLED */
'List Triton CLI profiles.',
'',
'{{usage}}',
'',
'{{options}}',
'A profile is a configured Triton CloudAPI endpoint and associated info.',
'I.e. the URL, account name, SSH key fingerprint, etc. information required',
'to call a CloudAPI endpoint in a Triton datacenter. You can then switch',
'between profiles with `triton -p PROFILE`, the TRITON_PROFILE environment',
'variable, or by setting your current profile.',
'',
'The "CURR" column indicates which profile is the current one.',
'',
'Usage:',
' {{name}} list',
'',
'{{options}}'
'The "CURR" column indicates which profile is the current one.'
/* END JSSTYLED */
].join('\n');

View File

@ -13,8 +13,12 @@ function do_set_current(subcmd, opts, args, cb) {
if (opts.help) {
this.do_help('help', {}, [subcmd], cb);
return;
} else if (args.length !== 1) {
return cb(new errors.UsageError('NAME argument is required'));
} else if (args.length === 0) {
cb(new errors.UsageError('missing NAME argument'));
return;
} else if (args.length > 1) {
cb(new errors.UsageError('too many arguments: ' + args.join(' ')));
return;
}
profilecommon.setCurrentProfile({cli: this.top, name: args[0]}, cb);
@ -28,11 +32,11 @@ do_set_current.options = [
}
];
do_set_current.synopses = ['{{name}} {{cmd}} PROFILE'];
do_set_current.help = [
'Set the current Triton CLI profile.',
'',
'Usage:',
' {{name}} set-current NAME',
'{{usage}}',
'',
'{{options}}',
'NAME is the name of an existing profile, or "-" to switch to the',

View File

@ -416,6 +416,8 @@ function profileDockerSetup(opts, cb) {
* `docker-compose` versions). It is debatable if we want to
* play this game. E.g. someone moving from Docker 1.8 to newer,
* *but not re-setting up envvars* may start hitting timeouts.
*
* TODO: consider using `docker-compose` version on PATH?
*/
if (!arg.dockerVersion) {
setup.env.DOCKER_CLIENT_TIMEOUT = '300';

View File

@ -203,12 +203,13 @@ do_apply.options = [
}
];
do_apply.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_apply.help = [
/* BEGIN JSSTYLED */
'Apply an RBAC configuration.',
'',
'Usage:',
' {{name}} apply [<options>]',
'{{usage}}',
'',
'{{options}}',
'If "--file FILE" is not specified, this defaults to using "./rbac.json".',

View File

@ -211,12 +211,13 @@ do_info.options = [
}
];
do_info.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_info.help = [
/* BEGIN JSSTYLED */
'Show current RBAC state.',
'',
'Usage:',
' {{name}} info [<options>]',
'{{usage}}',
'',
'{{options}}',
'List RBAC users, roles and policies and. This summary does not show all',

View File

@ -303,20 +303,23 @@ do_key.options = [
help: 'Delete the named key.'
}
];
do_key.synopses = [
'{{name}} {{cmd}} USER KEY # show USER\'s KEY',
'{{name}} {{cmd}} -d|--delete USER [KEY...] # delete USER\'s KEY',
'{{name}} {{cmd}} -a|--add [-n NAME] USER FILE # add an SSH key'
];
do_key.help = [
/* BEGIN JSSTYLED */
'Show, upload, and delete RBAC user SSH keys.',
'',
'Usage:',
' {{name}} key USER KEY # show USER\'s KEY',
' {{name}} key -d|--delete USER [KEY...] # delete USER\'s KEY',
' {{name}} key -a|--add [-n NAME] USER FILE',
' # Add a new role. FILE must be a file path to an SSH public',
' # key or "-" to pass the public key in on stdin.',
'{{usage}}',
'',
'{{options}}',
'Where "USER" is a full RBAC user "id", "login" name or a "shortid"; and',
'KEY is an SSH key "name" or "fingerprint".'
'KEY is an SSH key "name" or "fingerprint". FILE must be a file path to',
'an SSH public key or "-" to pass the public key in on stdin.'
/* END JSSTYLED */
].join('\n');

View File

@ -84,19 +84,18 @@ do_keys.options = [
}
]);
do_keys.help = (
/* BEGIN JSSTYLED */
'List RBAC user SSH keys.\n' +
'\n' +
'Usage:\n' +
' {{name}} keys [<options>] USER\n' +
'\n' +
'{{options}}' +
'\n' +
'Where "USER" is an RBAC user login or id (a UUID).\n'
/* END JSSTYLED */
);
do_keys.synopses = ['{{name}} {{cmd}} [OPTIONS] USER'];
do_keys.help = [
/* BEGIN JSSTYLED */
'List RBAC user SSH keys.',
'',
'{{usage}}',
'',
'{{options}}',
'Where "USER" is an RBAC user login or id (a UUID).'
/* END JSSTYLED */
].join('\n');
module.exports = do_keys;

View File

@ -82,20 +82,17 @@ do_policies.options = [
sortDefault: sortDefault
}));
do_policies.help = (
/* BEGIN JSSTYLED */
'List RBAC policies.\n' +
'\n' +
'Usage:\n' +
' {{name}} policies [<options>]\n' +
'\n' +
'{{options}}' +
'\n' +
'Fields (most are self explanatory, the client adds some for convenience):\n' +
' nrules The number of rules in this policy.\n'
/* END JSSTYLED */
);
do_policies.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_policies.help = [
'List RBAC policies.',
'',
'{{usage}}',
'',
'{{options}}',
'Fields (most are self explanatory, the client adds some for convenience):',
' nrules The number of rules in this policy.'
].join('\n');
module.exports = do_policies;

View File

@ -512,16 +512,24 @@ do_policy.options = [
help: 'Delete the named policy.'
}
];
do_policy.synopses = [
'{{name}} {{cmd}} POLICY # show policy POLICY',
'{{name}} {{cmd}} -e|--edit POLICY # edit policy POLICY in $EDITOR',
'{{name}} {{cmd}} -d|--delete [POLICY...] # delete policy POLICY',
'{{name}} {{cmd}} -a|--add [FILE] # add a new policy'
];
do_policy.help = [
/* BEGIN JSSTYLED */
'Show, add, edit and delete RBAC policies.',
'',
'Usage:',
' {{name}} policy POLICY # show policy POLICY',
' {{name}} policy -e|--edit POLICY # edit policy POLICY in $EDITOR',
' {{name}} policy -d|--delete [POLICY...] # delete policy POLICY',
' {{name}} {{cmd}} POLICY # show policy POLICY',
' {{name}} {{cmd}} -e|--edit POLICY # edit policy POLICY in $EDITOR',
' {{name}} {{cmd}} -d|--delete [POLICY...] # delete policy POLICY',
'',
' {{name}} policy -a|--add [FILE]',
' {{name}} {{cmd}} -a|--add [FILE]',
' # Add a new policy. FILE must be a file path to a JSON file',
' # with the policy data or "-" to pass the policy in on stdin.',
' # Or exclude FILE to interactively add.',

View File

@ -104,13 +104,16 @@ do_reset.options = [
}
];
do_reset.synopses = [
'{{name}} {{cmd}} [OPTIONS]'
];
do_reset.help = [
/* BEGIN JSSTYLED */
'Reset RBAC state for this account.',
'**Warning: This will delete all RBAC info for this account.**',
'',
'Usage:',
' {{name}} reset [<options>]',
'{{usage}}',
'',
'{{options}}',
'Warning: Currently, RBAC state updates can take a few seconds to appear',

View File

@ -490,16 +490,24 @@ do_role.options = [
help: 'Delete the named role.'
}
];
do_role.synopses = [
'{{name}} {{cmd}} ROLE # show role ROLE',
'{{name}} {{cmd}} -e|--edit ROLE # edit role ROLE in $EDITOR',
'{{name}} {{cmd}} -d|--delete [ROLE...] # delete role ROLE',
'{{name}} {{cmd}} -a|--add [FILE] # add a new role'
];
do_role.help = [
/* BEGIN JSSTYLED */
'Show, add, edit and delete RBAC roles.',
'',
'Usage:',
' {{name}} role ROLE # show role ROLE',
' {{name}} role -e|--edit ROLE # edit role ROLE in $EDITOR',
' {{name}} role -d|--delete [ROLE...] # delete role ROLE',
' {{name}} {{cmd}} ROLE # show role ROLE',
' {{name}} {{cmd}} -e|--edit ROLE # edit role ROLE in $EDITOR',
' {{name}} {{cmd}} -d|--delete [ROLE...] # delete role ROLE',
'',
' {{name}} role -a|--add [FILE]',
' {{name}} {{cmd}} -a|--add [FILE]',
' # Add a new role. FILE must be a file path to a JSON file',
' # with the role data or "-" to pass the role in on stdin.',
' # Or exclude FILE to interactively add.',

View File

@ -508,7 +508,8 @@ function makeRoleTagsFunc(cfg) {
}
};
func.name = cfg.funcName;
func.name = cfg.funcName; // XXX
func.subcmd = cfg.funcName;
func.options = [
{
@ -564,24 +565,31 @@ function makeRoleTagsFunc(cfg) {
helpCol: 23
};
func.synopses = [
'{{name}} {{cmd}} $argName # list role tags',
'{{name}} {{cmd}} -a ROLE[,ROLE...] $argName # add',
'{{name}} {{cmd}} -s ROLE[,ROLE...] $argName # set/replace',
'{{name}} {{cmd}} -e $argName # edit in $EDITOR',
'{{name}} {{cmd}} -d ROLE[,ROLE...] $argName # delete',
'{{name}} {{cmd}} -D $argName # delete all'
];
func.synopses = func.synopses.map(function (synopsis) {
var key = 'argName';
return synopsis.replace(new RegExp('\\$' + key, 'g'), cfg[key]);
});
func.help = [
/* BEGIN JSSTYLED */
'List and manage role tags for the given $resourceType.',
'',
'Usage:',
' {{name}} $cmdName $argName # list role tags',
' {{name}} $cmdName -a ROLE[,ROLE...] $argName # add',
' {{name}} $cmdName -s ROLE[,ROLE...] $argName # set/replace',
' {{name}} $cmdName -e $argName # edit in $EDITOR',
' {{name}} $cmdName -d ROLE[,ROLE...] $argName # delete',
' {{name}} $cmdName -D $argName # delete all',
'{{usage}}',
'',
'{{options}}',
'Where "ROLE" is a role tag name (see `triton rbac roles`) and',
'$argName is $helpArgIs.'
/* END JSSTYLED */
].join('\n');
['resourceType', 'cmdName', 'argName', 'helpArgIs'].forEach(function (key) {
['resourceType', 'cmdName', 'helpArgIs'].forEach(function (key) {
func.help = func.help.replace(new RegExp('\\$' + key, 'g'), cfg[key]);
});

View File

@ -97,20 +97,20 @@ do_roles.options = [
sortDefault: sortDefault
}));
do_roles.help = (
do_roles.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_roles.help = [
/* BEGIN JSSTYLED */
'List RBAC roles.\n' +
'\n' +
'Usage:\n' +
' {{name}} roles [<options>]\n' +
'\n' +
'{{options}}' +
'\n' +
'Fields (most are self explanatory, the client adds some for convenience):\n' +
' members Non-default members (not in the "default_members")\n' +
'List RBAC roles.',
'',
'{{usage}}',
'',
'{{options}}',
'Fields (most are self explanatory, the client adds some for convenience):',
' members Non-default members (not in the "default_members")',
' are shown in magenta.\n'
/* END JSSTYLED */
);
].join('\n');

View File

@ -497,16 +497,24 @@ do_user.options = [
help: 'Delete the named user.'
}
];
do_user.synopses = [
'{{name}} {{cmd}} [OPTIONS] USER # show user USER',
'{{name}} {{cmd}} -e|--edit USER # edit user USER in $EDITOR',
'{{name}} {{cmd}} -d|--delete [USER...] # delete user USER',
'{{name}} {{cmd}} -a|--add [FILE] # add a user'
];
do_user.help = [
/* BEGIN JSSTYLED */
'Show, add, edit and delete RBAC users.',
'',
'Usage:',
' {{name}} user [<options>] USER # show user USER',
' {{name}} user -e|--edit USER # edit user USER in $EDITOR',
' {{name}} user -d|--delete [USER...] # delete user USER',
' {{name}} {{cmd}} [OPTIONS] USER # show user USER',
' {{name}} {{cmd}} -e|--edit USER # edit user USER in $EDITOR',
' {{name}} {{cmd}} -d|--delete [USER...] # delete user USER',
'',
' {{name}} user -a|--add [FILE]',
' {{name}} {{cmd}} -a|--add [FILE]',
' # Add a new user. FILE must be a file path to a JSON file',
' # with the user data or "-" to pass the user in on stdin.',
' # Or exclude FILE to interactively add.',

View File

@ -84,22 +84,19 @@ do_users.options = [
sortDefault: sortDefault
}));
do_users.help = (
/* BEGIN JSSTYLED */
'List RBAC users.\n' +
'\n' +
'Usage:\n' +
' {{name}} users [<options>]\n' +
'\n' +
'Fields (most are self explanatory, the client adds some for convenience):\n' +
' name "firstName lastName"\n' +
' cdate Just the date portion of "created"\n' +
' udate Just the date portion of "updated"\n' +
'\n' +
'{{options}}'
/* END JSSTYLED */
);
do_users.synopses = ['{{name}} {{cmd}} [OPTIONS]'];
do_users.help = [
'List RBAC users.',
'',
'{{usage}}',
'',
'{{options}}',
'Fields (most are self explanatory, the client adds some for convenience):',
' name "firstName lastName"',
' cdate Just the date portion of "created"',
' udate Just the date portion of "updated"'
].join('\n');
module.exports = do_users;

View File

@ -77,13 +77,14 @@ do_services.options = [
sortDefault: sortDefault
}));
do_services.help = (
'Show services information\n'
+ '\n'
+ 'Usage:\n'
+ ' {{name}} services\n'
+ '\n'
+ '{{options}}'
);
do_services.synopses = ['{{name}} {{cmd}}'];
do_services.help = [
'Show services information',
'',
'{{usage}}',
'',
'{{options}}'
].join('\n');
module.exports = do_services;

View File

@ -10,6 +10,7 @@
* Error classes that the joyent CLI may produce.
*/
var cmdln = require('cmdln');
var util = require('util'),
format = util.format;
var assert = require('assert-plus');
@ -145,25 +146,6 @@ function ConfigError(cause, message) {
util.inherits(ConfigError, _TritonBaseVError);
/**
* CLI usage error
*/
function UsageError(cause, message) {
if (message === undefined) {
message = cause;
cause = undefined;
}
assert.string(message);
_TritonBaseVError.call(this, {
cause: cause,
message: message,
code: 'Usage',
exitStatus: 2
});
}
util.inherits(UsageError, _TritonBaseVError);
/**
* Error in setting up (typically in profile update/creation).
*/
@ -299,7 +281,7 @@ module.exports = {
TritonError: TritonError,
InternalError: InternalError,
ConfigError: ConfigError,
UsageError: UsageError,
UsageError: cmdln.UsageError,
SetupError: SetupError,
SigningError: SigningError,
SelfSignedCertError: SelfSignedCertError,

View File

@ -28,7 +28,6 @@ var vasync = require('vasync');
var cloudapi = require('./cloudapi2');
var common = require('./common');
var errors = require('./errors');
var loadConfigSync = require('./config').loadConfigSync;
// ---- globals

View File

@ -1,14 +1,14 @@
{
"name": "triton",
"description": "Joyent Triton CLI and client (https://www.joyent.com/triton)",
"version": "4.12.1",
"version": "4.13.0",
"author": "Joyent (joyent.com)",
"dependencies": {
"assert-plus": "0.2.0",
"backoff": "2.4.1",
"bigspinner": "3.1.0",
"bunyan": "1.5.1",
"cmdln": "3.5.4",
"cmdln": "4.1.1",
"extsprintf": "1.0.2",
"lomstream": "1.1.0",
"mkdirp": "0.5.1",