update cmdln, move 'profile' command out to separate file
This commit is contained in:
parent
7d706a0358
commit
1f123975ae
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
||||
/node_modules
|
||||
/tmp
|
||||
/docs/*.json
|
||||
/docs/*.html
|
||||
/build
|
||||
|
135
TODO.md
135
TODO.md
@ -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.<alias> <dc-name-1> <dc-name-2> ...
|
||||
sdc config alias.image.<alias> <image-uuid> ...
|
||||
|
||||
- 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: <KEY-ID>,
|
||||
user: <USER>,
|
||||
log: <BUNYAN-LOGGER>,
|
||||
}),
|
||||
- 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]: <prompt>
|
||||
...
|
||||
|
||||
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.
|
66
TODO.txt
Normal file
66
TODO.txt
Normal file
@ -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
|
@ -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
|
||||
});
|
||||
}
|
||||
|
275
lib/cli.js
275
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 <name> # show particular config var\n'
|
||||
+ ' {{name}} config -a <name> <value> # add/set a config var\n'
|
||||
+ ' {{name}} config -d <name> # 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 <name> <url> # add an SDC cloudapi endpoint\n'
|
||||
+ ' {{name}} dcs rm <name> # 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
|
||||
|
||||
|
@ -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 = {};
|
||||
|
24
package.json
24
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"
|
||||
|
Reference in New Issue
Block a user