From 1f123975ae8e3794ca9e255ed6c3c85e71f19529 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 25 Aug 2015 12:14:16 -0700 Subject: [PATCH] update cmdln, move 'profile' command out to separate file --- .gitignore | 2 - TODO.md | 135 ------------------------- TODO.txt | 66 ++++++++++++ bin/triton | 7 +- lib/cli.js | 275 +++++--------------------------------------------- lib/common.js | 17 ++-- package.json | 24 ++--- 7 files changed, 121 insertions(+), 405 deletions(-) delete mode 100644 TODO.md create mode 100644 TODO.txt diff --git a/.gitignore b/.gitignore index f4c0c89..62c5db9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ /node_modules /tmp -/docs/*.json -/docs/*.html /build diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 6ff376c..0000000 --- a/TODO.md +++ /dev/null @@ -1,135 +0,0 @@ -# first - -- Adding/removing DCs. Want this to work reasonably mainly to support dogfooding - with internal DCs. Also to allow this to be a general tool for *SDC*, - with default values for JPC, but not restricted to. Also allow the right thing - to happen if JPC adds new DCs. - - - Don't use "all" catch all DC. Use "joyent" alias for the default set. - - Add DC aliases (starting a generic aliasing). - - Show the aliases in `sdc dcs` - - support aliases in the command lookups. Method to get the DCs for the current - profile - XXX START HERE - - changing dcs: - sdc dcs add us-beta-4 https://beta4-cloudapi.joyent.us - sdc dcs set-url us-beta-4 https://beta4-cloudapi.joyent.us - sdc dcs rm us-beta-4 - Note: If having config.dcs override this means that any DC change means - that user doesn't "see" DC changes by new node-sdc versions. - - Impl 'sdc config' to edit these easily on the CLI. - sdc config alias.dc. ... - sdc config alias.image. ... - -- machines: - - short default output - - 'cdate' short created, just the date - - 'img' is 'name/version' - - 'sid' is the short id prefix - - long '-l' output, -H, -o, -s - - get image defaults and fill those in - -- few more commands? provision (create-machine?) - - -- uuid caching -- UUID prefix support -- profile command (adding profile, edit, etc.) -- `sdc config` command similar to git config - - -# account vs user vs subuser vs role - -See MANTA-2401 and scrum discussion from 14 Aug 2014.. -Suggestion: use "account" and "user" since "since those are the documented -tools for the abstractions and that's what smartdc uses." -Envvars: SDC_ACCOUNT and SDC_USER. - - -# later (in no particular order) - -- adding a dc: - sdc dcs -a us-beta-4 https://beta4-cloudapi.joyent.us - or -- signing: should sigstr include more than just the date? How about the request - path??? Not according to the cloudapi docs. -- restify-client and bunyan-light without dtrace-provider -- Get node-smartdc-auth to take a log option. Perhaps borrow from imgapi.js' - cliSigner et al. -- node-smartdc-auth: Support a path to a priv key for "keyId" arg. Or a separate - alternative arg. Copy this from imgapi.cliSigner. - sign: cloudapi.cliSigner({ - keyId: , - user: , - log: , - }), -- the error reporting for a signing error sucks: - getAccount: err { message: 'error signing request', - code: 'Signing', - exitStatus: 1 } - e.g. when the KEY_ID is nonsense. Does imgapi's auth have better error - reporting? -- how to add/exclude DCs? -- cmdln.js support for bash tab completion -- node-smartdc installs joyentcloud and warns about deprecation on stderr. -- bunyan logging setup: - - one output stream to a file at trace level: - /var/log/joyentcloud/$timestamp.log - - periodically keep the number of those files down. This is hard. Do it - at startup? Yah should be fine. - - another "raw" stream to stderr at WARN at above (maybe INFO?) - where we console.error just the minimal fields that we want to show - joyentcloud: warn: $msg - Not sure about other fields. -- plugin support, e.g. allow 3rd-party node-joyentcloud-foo npm modules that would - add a "joyentcloud foo" subcmd. Reasonable? -- windows testing - -# ideas - -- `sdc whatsnew` grabs current images and packages and compares to last time - it was called to short new images/packages. Perhaps for other resources too. - - - -# notes on `sdc provision` (in progress) - -- Lame: I <# that our packages are separate for kvm vs smartos usage. Do they - have conflicting data? -- Q: "package" or "instance-type"? Probably package for now. - -Need: dc (if profile has multiple, have a settable preferred dc for provisions), -image (uuid, name to get latest, have a settable preferred?), package (settable -preferred, settable preferred ram). - -What about using "same as last time" or a way to say that? - -Want interactive asking for missing params if TTY? -f to avoid. - - $ sdc provision ... - Datacenter [us-west-1]: - ... - -Name: AWS equiv is 'aws-cli ec2 run-instances' -http://docs.aws.amazon.com/cli/latest/reference/ec2/run-instances.html -E.g.: - - aws ec2 run-instances --image-id ami-c3b8d6aa --count 1 --instance-type t1.micro --key-name MyKeyPair --security-groups MySecurityGroup - - sdc create-machine ... - sdc provision ... - sdc provision -i IMAGE -p PACKAGE - shortcut? - sdc provision IMAGE:PKG ? - sdc provision IMAGE PKG ? - sdc provision image=IMAGE package=PKG ? no - - sdc provision -i IMAGE -p PKG -c 3 --name 'test%d' # printf codes for the count - sdc provision -d east -i base -p g3-standard-1 -n shirley # -d|--dc - -Clarify what IMAGE can be. "Name" matching is first against one's own private -images, then against public ones. UUID. UUID prefix. "Name/version" matching. -Image alias (`sdc config alias.bob $uuid`, though for git that alias is for -*commands*. Perhaps `sdc alias image.bob $uuid`. Dunno. Later.). - -Similar matching for PKG. diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..1699d69 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,66 @@ + +# today + +triton + +triton -v # bunyan trace logging + +triton vms|containers|machines # list machines +triton vm|container|machine ID|ALIAS # get machine + # -1 for unique match + +triton images # list +triton packages # list +triton image IMAGE +triton package PACKAGE + +triton provision # triton create-machine ?? + +triton create -p PKG [...] IMG +triton create -i IMG -p PKG [-n NAME] [...] + # example: triton create base64 -p t4-standard-1g + + + + + + +# maybe today + +triton config defaultPkg t4-standard-1g + +triton login|ssh VM # kexec? + +triton delete VM|IMAGE # substring matching? too dangerous + triton delete --vm VM + triton delete --image IMAGE + + + +# profiles + +triton profile # list all profiles +triton profile NAME # show NAME profile +triton profile -a NAME # sets it as active +triton profile -n|--new # ??? + +For today: only the implicit 'env' profile. + + + +# config + +~/.triton/ + config.json + {"currProfile": "east3b"} + east3b/ # profile + PROFILE2/ + ... + + + +# another day + +triton config get|set|list # see 'npm config' + +triton --shell # or whatever, repl \ No newline at end of file diff --git a/bin/triton b/bin/triton index 1ae7edd..5c4caaf 100755 --- a/bin/triton +++ b/bin/triton @@ -10,5 +10,10 @@ var cmdln = require('cmdln'); var CLI = require('../lib/cli'); if (require.main === module) { - cmdln.main(CLI, process.argv, {showCode: true}); + var cli = new CLI(); + cmdln.main(cli, { + argv: process.argv, + showCode: true, + showNoCommandErr: false + }); } diff --git a/lib/cli.js b/lib/cli.js index 863e8f9..9e63f29 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,34 +1,34 @@ /* - * Copyright (c) 2014 Joyent Inc. All rights reserved. + * Copyright (c) 2015 Joyent Inc. All rights reserved. * - * The 'sdc' CLI class. + * The `triton` CLI class. */ -var p = console.log; -var e = console.error; -var util = require('util'), - format = util.format; +var assert = require('assert-plus'); +var bunyan = require('bunyan'); var child_process = require('child_process'), spawn = child_process.spawn, exec = child_process.exec; -var fs = require('fs'); - -var assert = require('assert-plus'); -var async = require('async'); -var bunyan = require('bunyan'); var cmdln = require('cmdln'), Cmdln = cmdln.Cmdln; +var fs = require('fs'); +var util = require('util'), + format = util.format; +var vasync = require('vasync'); var common = require('./common'); var errors = require('./errors'); -var SDC = require('./sdc'); +//XXX +//var SDC = require('./sdc'); //---- globals +var p = console.log; + var pkg = require('../package.json'); -var name = 'sdc'; +var name = 'triton'; var log = bunyan.createLogger({ name: name, serializers: bunyan.stdSerializers, @@ -70,250 +70,24 @@ CLI.prototype.init = function (opts, args, callback) { } this.opts = opts; if (opts.verbose) { - process.env.DEBUG = 1; //TODO This is a lame req of cmdln.main(). log.level('trace'); log.src = true; } - this.__defineGetter__('sdc', function () { - if (self._sdc === undefined) { - self._sdc = new SDC({log: log, profile: opts.profile}); - } - return self._sdc; - }); + //XXX + //this.__defineGetter__('sdc', function () { + // if (self._sdc === undefined) { + // self._sdc = new SDC({log: log, profile: opts.profile}); + // } + // return self._sdc; + //}); // Cmdln class handles `opts.help`. Cmdln.prototype.init.apply(this, arguments); }; -CLI.prototype.do_config = function (subcmd, opts, args, callback) { - if (opts.help) { - this.do_help('help', {}, [subcmd], callback); - return; - } - - var action; - var actions = []; - if (opts.add) actions.push('add'); - if (opts['delete']) actions.push('delete'); - if (opts.edit) actions.push('edit'); - if (actions.length === 0) { - action = 'show'; - } else if (actions.length > 1) { - return callback(new errors.UsageError( - 'cannot specify more than one action: ' + actions.join(', '))); - } else { - action = actions[0]; - } - var numArgs = { - - } - - if (action === 'show') { - var c = common.objCopy(this.sdc.config); - delete c._defaults; - delete c._user; - if (args.length > 1) { - return callback(new errors.UsageError('too many args')); - } else if (args.length === 1) { - var lookups = args[0].split(/\./g); - for (var i = 0; i < lookups.length; i++) { - c = c[lookups[i]]; - if (c === undefined) { - return callback(new errors.UsageError( - 'no such config var: ' + args[0])); - } - } - } - if (typeof(c) === 'string') { - console.log(c) - } else { - console.log(JSON.stringify(c, null, 4)); - } - } else if (action === 'add') { - if (args.length !== 2) - return callback(new errors.UsageError('incorrect number of args')); - XXX - } else if (action === 'delete') { - if (args.length !== 1) - return callback(new errors.UsageError('incorrect number of args')); - XXX - } else if (action === 'edit') { - if (args.length !== 0) - return callback(new errors.UsageError('incorrect number of args')); - XXX - } - - callback(); -}; -CLI.prototype.do_config.options = [ - { - names: ['help', 'h'], - type: 'bool', - help: 'Show this help.' - }, - { - names: ['add', 'a'], - type: 'bool', - help: 'Add a config var.' - }, - { - names: ['delete', 'd'], - type: 'bool', - help: 'Delete a config var.' - }, - { - names: ['edit', 'e'], - type: 'bool', - help: 'Edit config in $EDITOR.' - } -]; -CLI.prototype.do_config.help = ( - 'Show and edit the `sdc` CLI config.\n' - + '\n' - + 'Usage:\n' - + ' {{name}} config # show config\n' - + ' {{name}} config # show particular config var\n' - + ' {{name}} config -a # add/set a config var\n' - + ' {{name}} config -d # delete a config var\n' - + ' {{name}} config -e # edit config in $EDITOR\n' - + '\n' - + '{{options}}' -); - - -CLI.prototype.do_profile = function (subcmd, opts, args, callback) { - if (opts.help) { - this.do_help('help', {}, [subcmd], callback); - return; - } else if (args.length > 1) { - return callback(new Error('too many args: ' + args)); - } - - var profs = common.deepObjCopy(this.sdc.profiles); - var currProfileName = this.sdc.profile.name; - for (var i = 0; i < profs.length; i++) { - profs[i].curr = (profs[i].name === currProfileName ? '*' : ' '); - profs[i].dcs = (profs[i].dcs ? profs[i].dcs : ['all']) - .join(','); - } - if (opts.json) { - p(JSON.stringify(profs, null, 4)); - } else { - common.tabulate(profs, { - columns: 'curr,name,dcs,user,keyId', - sort: 'name,user', - validFields: 'curr,name,dcs,user,keyId' - }); - } - callback(); -}; -CLI.prototype.do_profile.options = [ - { - names: ['help', 'h'], - type: 'bool', - help: 'Show this help.' - }, - { - names: ['json', 'j'], - type: 'bool', - help: 'JSON output.' - } -]; -CLI.prototype.do_profile.help = ( - 'Create, update or inpect joyent CLI profiles.\n' - + '\n' - + 'Usage:\n' - + ' {{name}} profile\n' - + '\n' - + '{{options}}' -); - - -CLI.prototype.do_dcs = function (subcmd, opts, args, callback) { - var self = this; - if (opts.help) { - this.do_help('help', {}, [subcmd], callback); - return; - } - - var action = args[0] || 'list'; - var name; - var url; - switch (action) { - case 'list': - if (args.length !== 0) { - return callback(new errors.UsageError('too many args: ' + args)); - } - var dcs = self.sdc.config.dc; - var dcsArray = Object.keys(dcs).map( - function (n) { return {name: n, url: dcs[n]}; }); - if (self.sdc.config.dcAlias) { - Object.keys(self.sdc.config.dcAlias).forEach(function (alias) { - dcsArray.push( - {alias: alias, names: self.sdc.config.dcAlias[alias]}); - }); - } - if (opts.json) { - p(JSON.stringify(dcsArray, null, 4)); - } else { - for (var i = 0; i < dcsArray.length; i++) { - var d = dcsArray[i]; - d.name = (d.name ? d.name : d.alias + '*'); - d.url = d.url || d.names.join(', '); - } - common.tabulate(dcsArray, { - columns: 'name,url', - sort: 'alias,name', - validFields: 'name,url,alias,names' - }); - } - callback(); - break; - case 'rm': - if (args.length !== 2) { - return callback(new errors.UsageError( - 'incorrect number of args: ' + args)); - } - name = args[1]; - XXX - break; - case 'add': - if (args.length !== 3) { - return callback(new errors.UsageError( - 'incorrect number of args: ' + args)); - } - name = args[1]; - url = args[2]; - XXX - break; - default: - return callback(new errors.UsageError('unknown dcs command: ' + args)) - } -}; -CLI.prototype.do_dcs.options = [ - { - names: ['help', 'h'], - type: 'bool', - help: 'Show this help.' - }, - { - names: ['json', 'j'], - type: 'bool', - help: 'JSON output.' - } -]; -CLI.prototype.do_dcs.help = ( - 'List, add or remove datacenters.\n' - + '\n' - + 'Usage:\n' - + ' {{name}} dcs # list DCs (and DC aliases marked with "*")\n' - + ' {{name}} dcs add # add an SDC cloudapi endpoint\n' - + ' {{name}} dcs rm # remove a DC\n' - + '\n' - + '{{options}}' -); +CLI.prototype.do_profile = require('./do_profile'); CLI.prototype.do_provision = function (subcmd, opts, args, callback) { @@ -406,7 +180,6 @@ CLI.prototype.do_provision.help = ( + '\n' + '{{options}}' ); -CLI.prototype.do_provision.aliases = ['create-machine']; CLI.prototype.do_machines = function (subcmd, opts, args, callback) { @@ -536,6 +309,12 @@ CLI.prototype.do_machine_audit.help = ( ); +//---- mainline + +if (require.main === module) { + var cli = new CLI(); + cmdln.main(cli, {showNoCommandErr: false}); +} //---- exports diff --git a/lib/common.js b/lib/common.js index 9381993..24e47fb 100755 --- a/lib/common.js +++ b/lib/common.js @@ -1,23 +1,26 @@ #!/usr/bin/env node /** - * Copyright (c) 2014 Joyent Inc. All rights reserved. + * Copyright (c) 2015 Joyent Inc. All rights reserved. */ -var p = console.log; + var assert = require('assert-plus'); -var async = require('async'); -var backoff = require('backoff'); -var fs = require('fs'); -var once = require('once'); var sprintf = require('extsprintf').sprintf; var util = require('util'), format = util.format; -var verror = require('verror'); var errors = require('./errors'), InternalError = errors.InternalError; +// ---- globals + +var p = console.log; + + + +// ---- support stuff + function objCopy(obj, target) { if (target === undefined) { target = {}; diff --git a/package.json b/package.json index 997f558..1fe95f1 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,23 @@ { "name": "triton", - "description": "Joyent Triton tool and client (http://www.joyent.com/products/compute-service)", + "description": "Joyent Triton tool and client (https://www.joyent.com/triton)", "version": "1.0.0", "author": "Joyent (joyent.com)", "private": true, "dependencies": { - "async": "0.2.9", - "assert-plus": "0.1.4", - "backoff": "2.3.0", - "bunyan": "0.22.0", - "cmdln": "1.3.1", - "dashdash": "1.3.2", + "assert-plus": "0.1.5", + "backoff": "2.4.1", + "bunyan": "1.4.0", + "cmdln": "3.2.1", + "dashdash": "1.10.0", "extsprintf": "1.0.2", - "mkdirp": "0.3.5", - "node-uuid": "1.4.1", - "once": "1.3.0", - "restify": "git+ssh://git@github.com:mcavage/node-restify.git#9bab8b7f", + "mkdirp": "0.5.1", + "node-uuid": "1.4.3", + "once": "1.3.2", + "restify-clients": "1.0.0", "smartdc-auth": "git+ssh://git@github.com:joyent/node-smartdc-auth.git#9f21966", - "verror": "1.3.7" + "vasync": "*", + "verror": "1.6.0" }, "engines": { "node": ">=0.10"