This repository has been archived on 2020-01-20. You can view files and clone it, but cannot push or open issues or pull requests.
node-spearhead/lib/cli.js

324 lines
9.1 KiB
JavaScript
Raw Normal View History

2014-02-07 23:21:24 +02:00
/*
* Copyright (c) 2015 Joyent Inc. All rights reserved.
2014-02-07 23:21:24 +02:00
*
* The `triton` CLI class.
2014-02-07 23:21:24 +02:00
*/
var assert = require('assert-plus');
var bunyan = require('bunyan');
2014-02-07 23:21:24 +02:00
var child_process = require('child_process'),
spawn = child_process.spawn,
exec = child_process.exec;
var cmdln = require('cmdln'),
Cmdln = cmdln.Cmdln;
var fs = require('fs');
2015-09-04 01:12:08 +03:00
var mkdirp = require('mkdirp');
var util = require('util'),
format = util.format;
2015-08-26 19:59:12 +03:00
var path = require('path');
var vasync = require('vasync');
2014-02-07 23:21:24 +02:00
var common = require('./common');
2014-02-08 10:15:26 +02:00
var errors = require('./errors');
var TritonApi = require('./tritonapi');
2014-02-07 23:21:24 +02:00
//---- globals
var p = console.log;
2014-02-07 23:21:24 +02:00
var pkg = require('../package.json');
var name = 'triton';
2014-02-07 23:21:24 +02:00
var log = bunyan.createLogger({
name: name,
serializers: bunyan.stdSerializers,
stream: process.stderr,
level: 'warn'
});
var OPTIONS = [
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.'
},
{
name: 'version',
type: 'bool',
help: 'Print version and exit.'
},
{
names: ['verbose', 'v'],
type: 'bool',
help: 'Verbose/debug output.'
},
// XXX disable profile selection for now
//{names: ['profile', 'p'], type: 'string', env: 'TRITON_PROFILE',
// helpArg: 'NAME', help: 'TritonApi client profile to use.'}
{
group: 'CloudApi Options'
},
// XXX SDC_USER support. I don't grok the node-smartdc/README.md discussion
// of SDC_USER.
{
names: ['account', 'a'],
type: 'string',
env: 'SDC_ACCOUNT',
help: 'TritonApi account (login name)',
helpArg: 'ACCOUNT'
},
// XXX
//{
// names: ['subuser', 'user'],
// type: 'string',
// env: 'MANTA_SUBUSER',
// help: 'Manta User (login name)',
// helpArg: 'USER'
//},
//{
// names: ['role'],
// type: 'arrayOfString',
// env: 'MANTA_ROLE',
// help: 'Assume a role. Use multiple times or once with a list',
// helpArg: 'ROLE,ROLE,...'
//},
{
names: ['keyId', 'k'],
type: 'string',
env: 'SDC_KEY_ID',
help: 'SSH key fingerprint',
helpArg: 'FINGERPRINT'
},
{
names: ['url', 'u'],
type: 'string',
env: 'SDC_URL',
help: 'CloudApi URL',
helpArg: 'URL'
},
{
names: ['J'],
type: 'string',
hidden: true,
help: 'Joyent Public Cloud (JPC) datacenter name. This is ' +
'a shortcut to the "https://$dc.api.joyent.com" ' +
'cloudapi URL.'
},
{
names: ['insecure', 'i'],
type: 'bool',
help: 'Do not validate SSL certificate',
'default': false,
env: 'SDC_TLS_INSECURE' // Deprecated SDC_TESTING supported below.
}
];
2014-02-07 23:21:24 +02:00
//---- CLI class
function CLI() {
Cmdln.call(this, {
name: pkg.name,
desc: pkg.description,
options: OPTIONS,
2014-02-07 23:21:24 +02:00
helpOpts: {
includeEnv: true,
2015-08-26 07:34:47 +03:00
minHelpCol: 30
},
helpSubcmds: [
'help',
{ group: 'Other Commands' },
'info',
'account',
'keys',
2015-08-27 02:56:18 +03:00
'services',
2015-08-27 02:59:28 +03:00
'datacenters',
2015-08-26 01:15:02 +03:00
{ group: 'Instances (aka VMs/Machines/Containers)' },
2015-08-26 06:53:48 +03:00
'create-instance',
2015-08-26 01:15:02 +03:00
'instances',
2015-08-26 03:27:46 +03:00
'instance',
2015-08-26 01:15:02 +03:00
'instance-audit',
2015-08-26 04:09:32 +03:00
'start-instance',
'stop-instance',
'reboot-instance',
2015-08-26 08:25:26 +03:00
'delete-instance',
2015-08-26 22:16:01 +03:00
'wait-instance',
2015-08-26 06:25:00 +03:00
'ssh',
{ group: 'Images' },
'images',
2015-08-26 01:47:29 +03:00
'image',
{ group: 'Packages' },
'packages',
2015-08-26 23:40:50 +03:00
'package',
{ group: 'Networks' },
2015-08-27 00:09:50 +03:00
'networks',
'network'
]
2014-02-07 23:21:24 +02:00
});
}
util.inherits(CLI, Cmdln);
CLI.prototype.init = function (opts, args, callback) {
var self = this;
if (opts.version) {
p(this.name, pkg.version);
callback(false);
return;
}
this.opts = opts;
if (opts.verbose) {
log.level('trace');
log.src = true;
this.showErrStack = true;
2014-02-07 23:21:24 +02:00
}
this.__defineGetter__('tritonapi', function () {
if (self._tritonapi === undefined) {
2015-08-26 19:59:12 +03:00
var userConfigPath = require('./config').DEFAULT_USER_CONFIG_PATH;
var dir = path.dirname(userConfigPath);
var cacheDir = path.join(dir, 'cache');
reduce mkdir noise in log for every 'triton ...' run E.g.: $ triton -v inst 1c7f40f6-a253-49f3-94d6-8f0656440696 2>&1 | bunyan [2015-08-31T17:57:02.682Z] INFO: triton/24726 on danger0.local (/Users/trentm/joy/node-triton/lib/cli.js:188): failed to make dir /Users/trentm/.triton (err.code=EEXIST) Error: EEXIST, file already exists '/Users/trentm/.triton' at Object.fs.mkdirSync (fs.js:654:18) at /Users/trentm/joy/node-triton/lib/cli.js:186:24 at Array.forEach (native) at CLI.triton (/Users/trentm/joy/node-triton/lib/cli.js:184:29) at CLI.do_instance (/Users/trentm/joy/node-triton/lib/do_instance.js:16:9) at CLI.dispatch (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:664:13) at /Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:425:18 at CLI.init (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:477:5) at CLI.init (/Users/trentm/joy/node-triton/lib/cli.js:221:26) at CLI.main (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:404:10) [2015-08-31T17:57:02.684Z] INFO: triton/24726 on danger0.local (/Users/trentm/joy/node-triton/lib/cli.js:188): failed to make dir /Users/trentm/.triton/cache (err.code=EEXIST) Error: EEXIST, file already exists '/Users/trentm/.triton/cache' at Object.fs.mkdirSync (fs.js:654:18) at /Users/trentm/joy/node-triton/lib/cli.js:186:24 at Array.forEach (native) at CLI.triton (/Users/trentm/joy/node-triton/lib/cli.js:184:29) at CLI.do_instance (/Users/trentm/joy/node-triton/lib/do_instance.js:16:9) at CLI.dispatch (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:664:13) at /Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:425:18 at CLI.init (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:477:5) at CLI.init (/Users/trentm/joy/node-triton/lib/cli.js:221:26) at CLI.main (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:404:10) ...
2015-08-31 21:16:56 +03:00
if (!fs.existsSync(cacheDir)) {
2015-08-26 19:59:12 +03:00
try {
2015-09-04 01:12:08 +03:00
mkdirp.sync(cacheDir);
2015-08-26 19:59:12 +03:00
} catch (e) {
reduce mkdir noise in log for every 'triton ...' run E.g.: $ triton -v inst 1c7f40f6-a253-49f3-94d6-8f0656440696 2>&1 | bunyan [2015-08-31T17:57:02.682Z] INFO: triton/24726 on danger0.local (/Users/trentm/joy/node-triton/lib/cli.js:188): failed to make dir /Users/trentm/.triton (err.code=EEXIST) Error: EEXIST, file already exists '/Users/trentm/.triton' at Object.fs.mkdirSync (fs.js:654:18) at /Users/trentm/joy/node-triton/lib/cli.js:186:24 at Array.forEach (native) at CLI.triton (/Users/trentm/joy/node-triton/lib/cli.js:184:29) at CLI.do_instance (/Users/trentm/joy/node-triton/lib/do_instance.js:16:9) at CLI.dispatch (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:664:13) at /Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:425:18 at CLI.init (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:477:5) at CLI.init (/Users/trentm/joy/node-triton/lib/cli.js:221:26) at CLI.main (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:404:10) [2015-08-31T17:57:02.684Z] INFO: triton/24726 on danger0.local (/Users/trentm/joy/node-triton/lib/cli.js:188): failed to make dir /Users/trentm/.triton/cache (err.code=EEXIST) Error: EEXIST, file already exists '/Users/trentm/.triton/cache' at Object.fs.mkdirSync (fs.js:654:18) at /Users/trentm/joy/node-triton/lib/cli.js:186:24 at Array.forEach (native) at CLI.triton (/Users/trentm/joy/node-triton/lib/cli.js:184:29) at CLI.do_instance (/Users/trentm/joy/node-triton/lib/do_instance.js:16:9) at CLI.dispatch (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:664:13) at /Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:425:18 at CLI.init (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:477:5) at CLI.init (/Users/trentm/joy/node-triton/lib/cli.js:221:26) at CLI.main (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:404:10) ...
2015-08-31 21:16:56 +03:00
log.info({err: e}, 'failed to make dir %s', cacheDir);
2015-08-26 19:59:12 +03:00
}
reduce mkdir noise in log for every 'triton ...' run E.g.: $ triton -v inst 1c7f40f6-a253-49f3-94d6-8f0656440696 2>&1 | bunyan [2015-08-31T17:57:02.682Z] INFO: triton/24726 on danger0.local (/Users/trentm/joy/node-triton/lib/cli.js:188): failed to make dir /Users/trentm/.triton (err.code=EEXIST) Error: EEXIST, file already exists '/Users/trentm/.triton' at Object.fs.mkdirSync (fs.js:654:18) at /Users/trentm/joy/node-triton/lib/cli.js:186:24 at Array.forEach (native) at CLI.triton (/Users/trentm/joy/node-triton/lib/cli.js:184:29) at CLI.do_instance (/Users/trentm/joy/node-triton/lib/do_instance.js:16:9) at CLI.dispatch (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:664:13) at /Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:425:18 at CLI.init (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:477:5) at CLI.init (/Users/trentm/joy/node-triton/lib/cli.js:221:26) at CLI.main (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:404:10) [2015-08-31T17:57:02.684Z] INFO: triton/24726 on danger0.local (/Users/trentm/joy/node-triton/lib/cli.js:188): failed to make dir /Users/trentm/.triton/cache (err.code=EEXIST) Error: EEXIST, file already exists '/Users/trentm/.triton/cache' at Object.fs.mkdirSync (fs.js:654:18) at /Users/trentm/joy/node-triton/lib/cli.js:186:24 at Array.forEach (native) at CLI.triton (/Users/trentm/joy/node-triton/lib/cli.js:184:29) at CLI.do_instance (/Users/trentm/joy/node-triton/lib/do_instance.js:16:9) at CLI.dispatch (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:664:13) at /Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:425:18 at CLI.init (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:477:5) at CLI.init (/Users/trentm/joy/node-triton/lib/cli.js:221:26) at CLI.main (/Users/trentm/joy/node-triton/node_modules/cmdln/lib/cmdln.js:404:10) ...
2015-08-31 21:16:56 +03:00
}
2015-08-26 19:59:12 +03:00
// XXX support keyId being a priv or pub key path, a la imgapi-cli
// XXX Add TRITON_* envvars.
var envProfile = {
name: 'env',
account: opts.account,
url: opts.url,
keyId: opts.keyId,
insecure: opts.insecure
};
2015-08-31 22:23:20 +03:00
// If --insecure not given, look at envvar(s) for that.
var specifiedInsecureOpt = opts._order.filter(
function (opt) { return opt.key === 'insecure'; }).length > 0;
if (!specifiedInsecureOpt && process.env.SDC_TESTING) {
envProfile.insecure = common.boolFromString(
process.env.SDC_TESTING,
2015-08-31 22:23:20 +03:00
false, '"SDC_TESTING" envvar');
}
if (opts.J) {
envProfile.url = format('https://%s.api.joyent.com', opts.J);
}
log.trace({envProfile: envProfile}, 'envProfile');
self._tritonapi = new TritonApi({
2015-08-26 19:59:12 +03:00
log: log,
profileName: opts.profile,
envProfile: envProfile,
configPath: userConfigPath,
cacheDir: cacheDir
2015-08-26 19:59:12 +03:00
});
}
return self._tritonapi;
});
2014-02-07 23:21:24 +02:00
// Cmdln class handles `opts.help`.
Cmdln.prototype.init.apply(this, arguments);
};
2015-09-04 10:09:19 +03:00
// Meta
CLI.prototype.do_completion = require('./do_completion');
//CLI.prototype.do_profile = require('./do_profile');
2014-02-07 23:21:24 +02:00
2015-09-04 10:09:19 +03:00
// Other
CLI.prototype.do_account = require('./do_account');
2015-08-27 02:56:18 +03:00
CLI.prototype.do_services = require('./do_services');
2015-08-27 02:59:28 +03:00
CLI.prototype.do_datacenters = require('./do_datacenters');
2015-08-26 07:16:41 +03:00
CLI.prototype.do_info = require('./do_info');
2015-08-26 07:40:32 +03:00
CLI.prototype.do_keys = require('./do_keys');
// Images
2015-08-26 00:25:30 +03:00
CLI.prototype.do_images = require('./do_images');
2015-08-26 01:47:29 +03:00
CLI.prototype.do_image = require('./do_image');
2015-08-26 00:25:30 +03:00
2015-08-26 01:15:02 +03:00
// Instances (aka VMs/containers/machines)
2015-08-26 03:27:46 +03:00
CLI.prototype.do_instance = require('./do_instance');
2015-08-26 01:15:02 +03:00
CLI.prototype.do_instances = require('./do_instances');
2015-08-26 06:53:48 +03:00
CLI.prototype.do_create_instance = require('./do_create_instance');
2015-08-26 01:15:02 +03:00
CLI.prototype.do_instance_audit = require('./do_instance_audit');
CLI.prototype.do_stop_instance = require('./do_startstop_instance')('stop');
CLI.prototype.do_start_instance = require('./do_startstop_instance')('start');
CLI.prototype.do_reboot_instance = require('./do_startstop_instance')('reboot');
2015-08-26 19:18:40 +03:00
CLI.prototype.do_delete_instance = require('./do_startstop_instance')('delete');
2015-08-26 22:16:01 +03:00
CLI.prototype.do_wait_instance = require('./do_wait_instance');
2015-08-26 06:25:00 +03:00
CLI.prototype.do_ssh = require('./do_ssh');
2014-02-07 23:21:24 +02:00
// Packages
CLI.prototype.do_packages = require('./do_packages');
CLI.prototype.do_package = require('./do_package');
2015-08-26 23:40:50 +03:00
// Networks
CLI.prototype.do_networks = require('./do_networks');
2015-08-27 00:09:50 +03:00
CLI.prototype.do_network = require('./do_network');
2015-08-26 23:40:50 +03:00
// Hidden commands
CLI.prototype.do_cloudapi = require('./do_cloudapi');
CLI.prototype.do_badger = require('./do_badger');
2014-02-07 23:21:24 +02:00
2015-07-26 08:45:20 +03:00
//---- mainline
function main(argv) {
if (!argv) {
argv = process.argv;
}
var cli = new CLI();
cli.main(argv, function (err, subcmd) {
var exitStatus = (err ? err.exitStatus || 1 : 0);
var showErr = (cli.showErr !== undefined ? cli.showErr : true);
if (err && showErr) {
var code = (err.body ? err.body.code : err.code);
if (code === 'NoCommand') {
/* jsl:pass */
} else if (err.message !== undefined) {
console.error('%s%s: error%s: %s',
cli.name,
(subcmd ? ' ' + subcmd : ''),
(code ? format(' (%s)', code) : ''),
(cli.showErrStack ? err.stack : err.message));
// 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) {
// Would like a shorter synopsis. Attempt to
// parse it down, somewhat generally.
var usageIdx = help.indexOf('\nUsage:');
if (usageIdx !== -1) {
help = help.slice(usageIdx);
}
console.error(help);
}
}
}
}
process.exit(exitStatus);
});
}
2014-02-07 23:21:24 +02:00
//---- exports
module.exports = {
CLI: CLI,
main: main
};