'triton profile{,s}' all except 'triton profile -a'
This commit is contained in:
parent
403e4bd204
commit
bf21ac467a
@ -59,8 +59,7 @@ var OPTIONS = [
|
||||
type: 'string',
|
||||
env: 'TRITON_PROFILE',
|
||||
helpArg: 'NAME',
|
||||
help: 'Triton client profile to use.',
|
||||
hidden: true // TODO: hidden until profiles impl is complete
|
||||
help: 'Triton client profile to use.'
|
||||
},
|
||||
|
||||
{
|
||||
@ -145,8 +144,8 @@ function CLI() {
|
||||
},
|
||||
helpSubcmds: [
|
||||
'help',
|
||||
// TODO: hide until the command is fully implemented:
|
||||
// 'profiles',
|
||||
'profiles',
|
||||
'profile',
|
||||
{ group: 'Other Commands' },
|
||||
'info',
|
||||
'account',
|
||||
@ -259,6 +258,7 @@ CLI.prototype._applyProfileOverrides =
|
||||
// Meta
|
||||
CLI.prototype.do_completion = require('./do_completion');
|
||||
CLI.prototype.do_profiles = require('./do_profiles');
|
||||
CLI.prototype.do_profile = require('./do_profile');
|
||||
|
||||
// Other
|
||||
CLI.prototype.do_account = require('./do_account');
|
||||
|
167
lib/common.js
167
lib/common.js
@ -9,6 +9,12 @@
|
||||
*/
|
||||
|
||||
var assert = require('assert-plus');
|
||||
var child_process = require('child_process');
|
||||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var read = require('read');
|
||||
var tty = require('tty');
|
||||
var util = require('util'),
|
||||
format = util.format;
|
||||
|
||||
@ -16,10 +22,6 @@ var errors = require('./errors'),
|
||||
InternalError = errors.InternalError;
|
||||
|
||||
|
||||
// ---- globals
|
||||
|
||||
var p = console.log;
|
||||
|
||||
// ---- support stuff
|
||||
|
||||
function objCopy(obj, target) {
|
||||
@ -417,6 +419,158 @@ function getCliTableOptions(opts) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prompt a user for a y/n answer.
|
||||
*
|
||||
* cb('y') user entered in the affirmative
|
||||
* cb('n') user entered in the negative
|
||||
* cb(false) user ^C'd
|
||||
*
|
||||
* Dev Note: Borrowed from imgadm's common.js. If this starts showing issues,
|
||||
* we should consider using the npm 'read' module.
|
||||
*/
|
||||
function promptYesNo(opts_, cb) {
|
||||
assert.object(opts_, 'opts');
|
||||
assert.string(opts_.msg, 'opts.msg');
|
||||
assert.optionalString(opts_.default, 'opts.default');
|
||||
var opts = objCopy(opts_);
|
||||
|
||||
// Setup stdout and stdin to talk to the controlling terminal if
|
||||
// process.stdout or process.stdin is not a TTY.
|
||||
var stdout;
|
||||
if (opts.stdout) {
|
||||
stdout = opts.stdout;
|
||||
} else if (process.stdout.isTTY) {
|
||||
stdout = process.stdout;
|
||||
} else {
|
||||
opts.stdout_fd = fs.openSync('/dev/tty', 'r+');
|
||||
stdout = opts.stdout = new tty.WriteStream(opts.stdout_fd);
|
||||
}
|
||||
var stdin;
|
||||
if (opts.stdin) {
|
||||
stdin = opts.stdin;
|
||||
} else if (process.stdin.isTTY) {
|
||||
stdin = process.stdin;
|
||||
} else {
|
||||
opts.stdin_fd = fs.openSync('/dev/tty', 'r+');
|
||||
stdin = opts.stdin = new tty.ReadStream(opts.stdin_fd);
|
||||
}
|
||||
|
||||
stdout.write(opts.msg);
|
||||
stdin.setEncoding('utf8');
|
||||
stdin.setRawMode(true);
|
||||
stdin.resume();
|
||||
var input = '';
|
||||
stdin.on('data', onData);
|
||||
|
||||
function postInput() {
|
||||
stdin.setRawMode(false);
|
||||
stdin.pause();
|
||||
stdin.write('\n');
|
||||
stdin.removeListener('data', onData);
|
||||
}
|
||||
|
||||
function finish(rv) {
|
||||
if (opts.stdout_fd !== undefined) {
|
||||
stdout.end();
|
||||
delete opts.stdout_fd;
|
||||
}
|
||||
if (opts.stdin_fd !== undefined) {
|
||||
stdin.end();
|
||||
delete opts.stdin_fd;
|
||||
}
|
||||
cb(rv);
|
||||
}
|
||||
|
||||
function onData(ch) {
|
||||
ch = ch + '';
|
||||
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\u0004':
|
||||
// They've finished typing their answer
|
||||
postInput();
|
||||
var answer = input.toLowerCase();
|
||||
if (answer === '' && opts.default) {
|
||||
finish(opts.default);
|
||||
} else if (answer === 'yes' || answer === 'y') {
|
||||
finish('y');
|
||||
} else if (answer === 'no' || answer === 'n') {
|
||||
finish('n');
|
||||
} else {
|
||||
stdout.write('Please enter "y", "yes", "n" or "no".\n');
|
||||
promptYesNo(opts, cb);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case '\u0003':
|
||||
// Ctrl C
|
||||
postInput();
|
||||
finish(false);
|
||||
break;
|
||||
default:
|
||||
// More plaintext characters
|
||||
stdout.write(ch);
|
||||
input += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prompt and wait for <Enter> or Ctrl+C. Usage:
|
||||
*
|
||||
* common.promptEnter('Press <Enter> to re-edit, Ctrl+C to abort.',
|
||||
* function (err) {
|
||||
* if (err) {
|
||||
* // User hit Ctrl+C
|
||||
* } else {
|
||||
* // User hit Enter
|
||||
* }
|
||||
* }
|
||||
* );
|
||||
*/
|
||||
function promptEnter(prompt, cb) {
|
||||
read({
|
||||
prompt: prompt
|
||||
}, function (err, result, isDefault) {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Edit the given text in $EDITOR (defaulting to `vi`) and return the edited
|
||||
* text.
|
||||
*
|
||||
* This callback with `cb(err, updatedText, changed)` where `changed`
|
||||
* is a boolean true if the text was changed.
|
||||
*/
|
||||
function editInEditor(opts, cb) {
|
||||
assert.string(opts.text, 'opts.text');
|
||||
assert.optionalString(opts.filename, 'opts.filename');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
var tmpPath = path.resolve(os.tmpDir(),
|
||||
format('triton-%s-edit-%s', process.pid, opts.filename || 'text'));
|
||||
fs.writeFileSync(tmpPath, opts.text, 'utf8');
|
||||
|
||||
// TODO: want '-f' opt for vi? What about others?
|
||||
var editor = process.env.EDITOR || '/usr/bin/vi';
|
||||
var kid = child_process.spawn(editor, [tmpPath], {stdio: 'inherit'});
|
||||
kid.on('exit', function (code) {
|
||||
if (code) {
|
||||
return (cb(code));
|
||||
}
|
||||
var afterText = fs.readFileSync(tmpPath, 'utf8');
|
||||
fs.unlinkSync(tmpPath);
|
||||
cb(null, afterText, (afterText !== opts.text));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//---- exports
|
||||
|
||||
module.exports = {
|
||||
@ -434,6 +588,9 @@ module.exports = {
|
||||
normShortId: normShortId,
|
||||
uuidToShortId: uuidToShortId,
|
||||
slug: slug,
|
||||
getCliTableOptions: getCliTableOptions
|
||||
getCliTableOptions: getCliTableOptions,
|
||||
promptYesNo: promptYesNo,
|
||||
promptEnter: promptEnter,
|
||||
editInEditor: editInEditor
|
||||
};
|
||||
// vim: set softtabstop=4 shiftwidth=4:
|
||||
|
@ -307,6 +307,39 @@ function loadAllProfiles(opts) {
|
||||
return profiles;
|
||||
}
|
||||
|
||||
function deleteProfile(opts) {
|
||||
assert.string(opts.configDir, 'opts.configDir');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
|
||||
if (opts.name === 'env') {
|
||||
throw new Error('cannot delete "env" profile');
|
||||
}
|
||||
|
||||
var profilePath = path.resolve(opts.configDir, 'profiles.d',
|
||||
opts.name + '.json');
|
||||
fs.unlinkSync(profilePath);
|
||||
}
|
||||
|
||||
function saveProfileSync(opts) {
|
||||
assert.string(opts.configDir, 'opts.configDir');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
assert.object(opts.profile, 'opts.profile');
|
||||
|
||||
if (opts.name === 'env') {
|
||||
throw new Error('cannot save "env" profile');
|
||||
}
|
||||
|
||||
_validateProfile(opts.profile);
|
||||
|
||||
var toSave = common.objCopy(opts.profile);
|
||||
delete toSave.name;
|
||||
|
||||
var profilePath = path.resolve(opts.configDir, 'profiles.d',
|
||||
opts.name + '.json');
|
||||
fs.writeFileSync(profilePath, JSON.stringify(toSave, null, 4), 'utf8');
|
||||
console.log('Saved profile "%s"', opts.name);
|
||||
}
|
||||
|
||||
|
||||
//---- exports
|
||||
|
||||
@ -314,6 +347,8 @@ module.exports = {
|
||||
loadConfig: loadConfig,
|
||||
setConfigVar: setConfigVar,
|
||||
loadProfile: loadProfile,
|
||||
loadAllProfiles: loadAllProfiles
|
||||
loadAllProfiles: loadAllProfiles,
|
||||
deleteProfile: deleteProfile,
|
||||
saveProfileSync: saveProfileSync
|
||||
};
|
||||
// vim: set softtabstop=4 shiftwidth=4:
|
||||
|
410
lib/do_profile.js
Normal file
410
lib/do_profile.js
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Joyent Inc.
|
||||
*
|
||||
* `triton profile ...`
|
||||
*/
|
||||
|
||||
var assert = require('assert-plus');
|
||||
var format = require('util').format;
|
||||
var strsplit = require('strsplit');
|
||||
var vasync = require('vasync');
|
||||
|
||||
var common = require('./common');
|
||||
var errors = require('./errors');
|
||||
var mod_config = require('./config');
|
||||
|
||||
|
||||
|
||||
function _showProfile(opts, cb) {
|
||||
assert.object(opts.cli, 'opts.cli');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
assert.func(cb, 'cb');
|
||||
var cli = opts.cli;
|
||||
|
||||
try {
|
||||
var profile = mod_config.loadProfile({
|
||||
configDir: cli.configDir,
|
||||
name: opts.name
|
||||
});
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
if (profile.name === cli.tritonapi.profile.name) {
|
||||
cli._applyProfileOverrides(profile);
|
||||
profile.curr = true;
|
||||
} else {
|
||||
profile.curr = false;
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
console.log(JSON.stringify(profile));
|
||||
} else {
|
||||
Object.keys(profile).sort().forEach(function (key) {
|
||||
var val = profile[key];
|
||||
console.log('%s: %s', key, val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _currentProfile(opts, cb) {
|
||||
assert.object(opts.cli, 'opts.cli');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
assert.func(cb, 'cb');
|
||||
var cli = opts.cli;
|
||||
|
||||
try {
|
||||
var profile = mod_config.loadProfile({
|
||||
configDir: cli.configDir,
|
||||
name: opts.name
|
||||
});
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
if (cli.tritonapi.profile.name === profile.name) {
|
||||
console.log('"%s" is already the current profile', profile.name);
|
||||
return cb();
|
||||
}
|
||||
|
||||
mod_config.setConfigVar({
|
||||
configDir: cli.configDir,
|
||||
name: 'profile',
|
||||
value: profile.name
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
console.log('Set "%s" as current profile', profile.name);
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function _yamlishFromProfile(profile) {
|
||||
assert.object(profile, 'profile');
|
||||
|
||||
var keys = [];
|
||||
var skipKeys = ['curr', 'name'];
|
||||
Object.keys(profile).forEach(function (key) {
|
||||
if (skipKeys.indexOf(key) === -1) {
|
||||
keys.push(key);
|
||||
}
|
||||
});
|
||||
keys = keys.sort();
|
||||
|
||||
var lines = [];
|
||||
keys.forEach(function (key) {
|
||||
lines.push(format('%s: %s', key, profile[key]));
|
||||
});
|
||||
return lines.join('\n') + '\n';
|
||||
}
|
||||
|
||||
function _profileFromYamlish(yamlish) {
|
||||
assert.string(yamlish, 'yamlish');
|
||||
|
||||
var profile = {};
|
||||
var bools = ['insecure'];
|
||||
var lines = yamlish.split(/\n/g);
|
||||
lines.forEach(function (line) {
|
||||
var commentIdx = line.indexOf('#');
|
||||
if (commentIdx !== -1) {
|
||||
line = line.slice(0, commentIdx);
|
||||
}
|
||||
line = line.trim();
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
var parts = strsplit(line, ':', 2);
|
||||
var key = parts[0].trim();
|
||||
var value = parts[1].trim();
|
||||
if (bools.indexOf(key) !== -1) {
|
||||
value = common.boolFromString(value);
|
||||
}
|
||||
profile[key] = value;
|
||||
});
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
||||
function _editProfile(opts, cb) {
|
||||
assert.object(opts.cli, 'opts.cli');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
assert.func(cb, 'cb');
|
||||
var cli = opts.cli;
|
||||
|
||||
if (opts.name === 'env') {
|
||||
return cb(new errors.UsageError('cannot edit "env" profile'));
|
||||
}
|
||||
|
||||
try {
|
||||
var profile = mod_config.loadProfile({
|
||||
configDir: cli.configDir,
|
||||
name: opts.name
|
||||
});
|
||||
} catch (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
var filename = format('profile-%s.txt', profile.name);
|
||||
var origText = _yamlishFromProfile(profile);
|
||||
|
||||
function editAttempt(text) {
|
||||
common.editInEditor({
|
||||
text: text,
|
||||
filename: filename
|
||||
}, function (err, afterText, changed) {
|
||||
if (err) {
|
||||
return cb(new errors.TritonError(err));
|
||||
} else if (!changed) {
|
||||
console.log('No change to profile');
|
||||
return cb();
|
||||
}
|
||||
|
||||
try {
|
||||
var editedProfile = _profileFromYamlish(afterText);
|
||||
editedProfile.name = profile.name;
|
||||
|
||||
if (_yamlishFromProfile(editedProfile) === origText) {
|
||||
// This YAMLish is the closest to a canonical form we have.
|
||||
console.log('No change to profile');
|
||||
return cb();
|
||||
}
|
||||
|
||||
mod_config.saveProfileSync({
|
||||
configDir: cli.configDir,
|
||||
name: opts.name,
|
||||
profile: editedProfile
|
||||
});
|
||||
} catch (textErr) {
|
||||
console.error('Error with your changes: %s', textErr);
|
||||
common.promptEnter(
|
||||
'Press <Enter> to re-edit, Ctrl+C to abort.',
|
||||
function (aborted) {
|
||||
if (aborted) {
|
||||
console.log('\nAborting. ' +
|
||||
'No change made to profile');
|
||||
cb();
|
||||
} else {
|
||||
editAttempt(afterText);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
editAttempt(origText);
|
||||
}
|
||||
|
||||
|
||||
function _deleteProfile(opts, cb) {
|
||||
assert.object(opts.cli, 'opts.cli');
|
||||
assert.string(opts.name, 'opts.name');
|
||||
assert.bool(opts.force, 'opts.force');
|
||||
assert.func(cb, 'cb');
|
||||
var cli = opts.cli;
|
||||
|
||||
if (opts.name === 'env') {
|
||||
return cb(new errors.UsageError('cannot delete "env" profile'));
|
||||
}
|
||||
|
||||
try {
|
||||
var profile = mod_config.loadProfile({
|
||||
configDir: cli.configDir,
|
||||
name: opts.name
|
||||
});
|
||||
} catch (err) {
|
||||
if (opts.force) {
|
||||
cb();
|
||||
} else {
|
||||
cb(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile.name === cli.tritonapi.profile.name && !opts.force) {
|
||||
return cb(new errors.TritonError(
|
||||
'cannot delete the current profile (use --force to override)'));
|
||||
}
|
||||
|
||||
vasync.pipeline({funcs: [
|
||||
function confirm(_, next) {
|
||||
if (opts.force) {
|
||||
return next();
|
||||
}
|
||||
common.promptYesNo({
|
||||
msg: 'Delete profile "' + opts.name + '"? [y/n] '
|
||||
}, function (answer) {
|
||||
if (answer !== 'y') {
|
||||
console.error('Aborting');
|
||||
next(true); // early abort signal
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
function handleConfigVar(_, next) {
|
||||
if (profile.name === cli.tritonapi.profile.name) {
|
||||
_currentProfile({name: 'env', cli: cli}, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
|
||||
function deleteIt(_, next) {
|
||||
try {
|
||||
mod_config.deleteProfile({
|
||||
configDir: cli.configDir,
|
||||
name: opts.name
|
||||
});
|
||||
} catch (delErr) {
|
||||
return next(delErr);
|
||||
}
|
||||
console.log('Deleted profile "%s"', opts.name);
|
||||
next();
|
||||
}
|
||||
]}, function (err) {
|
||||
if (err === true) {
|
||||
err = null;
|
||||
}
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function _addProfile(opts, cb) {
|
||||
//XXX
|
||||
cb(new errors.InternalError('_addProfile not yet implemented'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
function do_profile(subcmd, opts, args, cb) {
|
||||
if (opts.help) {
|
||||
this.do_help('help', {}, [subcmd], cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// Which action?
|
||||
var actions = [];
|
||||
if (opts.add) { actions.push('add'); }
|
||||
if (opts.current) { actions.push('current'); }
|
||||
if (opts.edit) { actions.push('edit'); }
|
||||
if (opts['delete']) { actions.push('delete'); }
|
||||
var action;
|
||||
if (actions.length === 0) {
|
||||
action = 'show';
|
||||
} else if (actions.length > 1) {
|
||||
return cb(new errors.UsageError(
|
||||
'only one action option may be used at once'));
|
||||
} else {
|
||||
action = actions[0];
|
||||
}
|
||||
|
||||
// Arg count validation.
|
||||
if (args.length > 1) {
|
||||
return cb(new errors.UsageError('too many arguments'));
|
||||
} else if (args.length === 0 &&
|
||||
['current', 'delete'].indexOf(action) !== -1)
|
||||
{
|
||||
return cb(new errors.UsageError('NAME argument is required'));
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case 'show':
|
||||
_showProfile({
|
||||
cli: this,
|
||||
name: args[0] || this.tritonapi.config.profile,
|
||||
json: opts.json
|
||||
}, cb);
|
||||
break;
|
||||
case 'current':
|
||||
_currentProfile({cli: this, name: args[0]}, cb);
|
||||
break;
|
||||
case 'edit':
|
||||
_editProfile({
|
||||
cli: this,
|
||||
name: args[0] || this.tritonapi.config.profile
|
||||
}, cb);
|
||||
break;
|
||||
case 'delete':
|
||||
_deleteProfile({
|
||||
cli: this,
|
||||
name: args[0] || this.tritonapi.config.profile,
|
||||
force: Boolean(opts.force)
|
||||
}, cb);
|
||||
break;
|
||||
case 'add':
|
||||
_addProfile({cli: this, file: args[0]}, cb);
|
||||
break;
|
||||
default:
|
||||
return cb(new errors.InternalError('unknown action: ' + action));
|
||||
}
|
||||
}
|
||||
|
||||
do_profile.options = [
|
||||
{
|
||||
names: ['help', 'h'],
|
||||
type: 'bool',
|
||||
help: 'Show this help.'
|
||||
},
|
||||
{
|
||||
names: ['json', 'j'],
|
||||
type: 'bool',
|
||||
help: 'JSON output when showing a profile.'
|
||||
},
|
||||
{
|
||||
names: ['force', 'f'],
|
||||
type: 'bool',
|
||||
help: 'Force deletion.'
|
||||
},
|
||||
{
|
||||
group: 'Action Options'
|
||||
},
|
||||
{
|
||||
names: ['current', 'c'],
|
||||
type: 'bool',
|
||||
help: 'Switch to the named profile.'
|
||||
},
|
||||
{
|
||||
names: ['edit', 'e'],
|
||||
type: 'bool',
|
||||
help: 'Edit the named profile in your $EDITOR.'
|
||||
},
|
||||
{
|
||||
names: ['add', 'a'],
|
||||
type: 'bool',
|
||||
help: 'Add a new profile.'
|
||||
},
|
||||
{
|
||||
names: ['delete', 'd'],
|
||||
type: 'bool',
|
||||
help: 'Delete the named profile.'
|
||||
}
|
||||
];
|
||||
|
||||
do_profile.help = [
|
||||
'Show, add, edit and delete `triton` CLI profiles.',
|
||||
'',
|
||||
'A profile is a configured Triton CloudAPI endpoint. I.e. the',
|
||||
'url, account, key, etc. information required to call a CloudAPI.',
|
||||
'You can then switch between profiles with `triton -p PROFILE`',
|
||||
'or the TRITON_PROFILE environment variable.',
|
||||
'',
|
||||
'Usage:',
|
||||
' {{name}} profile [NAME] # show NAME or current profile',
|
||||
' {{name}} profile -e|--edit [NAME] # edit a profile in $EDITOR',
|
||||
' {{name}} profile -c|--current NAME # set NAME as current profile',
|
||||
' {{name}} profile -d|--delete NAME # delete a profile',
|
||||
' {{name}} profile -a|--add [FILE] # add a new profile',
|
||||
'',
|
||||
'{{options}}'
|
||||
].join('\n');
|
||||
|
||||
|
||||
module.exports = do_profile;
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Joyent Inc. All rights reserved.
|
||||
* Copyright (c) 2015 Joyent Inc.
|
||||
*
|
||||
* `triton profiles ...`
|
||||
*/
|
||||
@ -14,7 +14,7 @@ var sortDefault = 'name';
|
||||
var columnsDefault = 'name,curr,account,url';
|
||||
var columnsDefaultLong = 'name,curr,account,url,insecure,keyId';
|
||||
|
||||
function _listProfiles(opts, args, cb) {
|
||||
function _listProfiles(cli, opts, args, cb) {
|
||||
var columns = columnsDefault;
|
||||
if (opts.o) {
|
||||
columns = opts.o;
|
||||
@ -29,8 +29,8 @@ function _listProfiles(opts, args, cb) {
|
||||
var profiles;
|
||||
try {
|
||||
profiles = mod_config.loadAllProfiles({
|
||||
configDir: this.tritonapi.config._configDir,
|
||||
log: this.log
|
||||
configDir: cli.tritonapi.config._configDir,
|
||||
log: cli.log
|
||||
});
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
@ -39,8 +39,8 @@ function _listProfiles(opts, args, cb) {
|
||||
// Current profile: Set 'curr' field. Apply CLI overrides.
|
||||
for (i = 0; i < profiles.length; i++) {
|
||||
var profile = profiles[i];
|
||||
if (profile.name === this.tritonapi.profile.name) {
|
||||
this._applyProfileOverrides(profile);
|
||||
if (profile.name === cli.tritonapi.profile.name) {
|
||||
cli._applyProfileOverrides(profile);
|
||||
if (opts.json) {
|
||||
profile.curr = true;
|
||||
} else {
|
||||
@ -69,19 +69,19 @@ function _listProfiles(opts, args, cb) {
|
||||
cb();
|
||||
}
|
||||
|
||||
function _currentProfile(opts, args, cb) {
|
||||
function _currentProfile(cli, opts, args, cb) {
|
||||
var profile = mod_config.loadProfile({
|
||||
configDir: this.configDir,
|
||||
configDir: cli.configDir,
|
||||
name: opts.current
|
||||
});
|
||||
|
||||
if (this.tritonapi.profile.name === profile.name) {
|
||||
if (cli.tritonapi.profile.name === profile.name) {
|
||||
console.log('"%s" is already the current profile', profile.name);
|
||||
return cb();
|
||||
}
|
||||
|
||||
mod_config.setConfigVar({
|
||||
configDir: this.configDir,
|
||||
configDir: cli.configDir,
|
||||
name: 'profile',
|
||||
value: profile.name
|
||||
}, function (err) {
|
||||
@ -93,69 +93,18 @@ function _currentProfile(opts, args, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: finish the implementation
|
||||
//function _addProfile(profile, opts, cb) {
|
||||
//}
|
||||
//
|
||||
//function _editProfile(profile, opts, cb) {
|
||||
//}
|
||||
//
|
||||
//function _deleteProfile(profile, opts, cb) {
|
||||
//}
|
||||
|
||||
|
||||
function do_profiles(subcmd, opts, args, cb) {
|
||||
if (opts.help) {
|
||||
this.do_help('help', {}, [subcmd], cb);
|
||||
return;
|
||||
return this.do_help('help', {}, [subcmd], cb);
|
||||
} else if (args.length > 0) {
|
||||
return cb(new errors.UsageError('too many args'));
|
||||
}
|
||||
|
||||
// Which action?
|
||||
var actions = [];
|
||||
if (opts.add) { actions.push('add'); }
|
||||
if (opts.current) { actions.push('current'); }
|
||||
if (opts.edit) { actions.push('edit'); }
|
||||
if (opts['delete']) { actions.push('delete'); }
|
||||
var action;
|
||||
if (actions.length === 0) {
|
||||
action = 'list';
|
||||
} else if (actions.length > 1) {
|
||||
return cb(new errors.UsageError(
|
||||
'only one action option may be used at once'));
|
||||
if (opts.current) {
|
||||
_currentProfile(this, opts, args, cb);
|
||||
} else {
|
||||
action = actions[0];
|
||||
_listProfiles(this, opts, args, cb);
|
||||
}
|
||||
|
||||
// Arg count validation.
|
||||
switch (action) {
|
||||
//case 'add':
|
||||
// if (args.length === 1) {
|
||||
// name = args[0];
|
||||
// } else if (args.length > 1) {
|
||||
// return cb(new errors.UsageError('too many args'));
|
||||
// }
|
||||
// break;
|
||||
case 'list':
|
||||
case 'current':
|
||||
//case 'edit':
|
||||
//case 'delete':
|
||||
if (args.length > 0) {
|
||||
return cb(new errors.UsageError('too many args'));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error('unknown action: ' + action);
|
||||
}
|
||||
|
||||
var func = {
|
||||
list: _listProfiles,
|
||||
current: _currentProfile
|
||||
// TODO: finish the implementation
|
||||
//add: _addProfile,
|
||||
//edit: _editProfile,
|
||||
//'delete': _deleteProfile
|
||||
}[action].bind(this);
|
||||
func(opts, args, cb);
|
||||
}
|
||||
|
||||
do_profiles.options = [
|
||||
@ -163,41 +112,13 @@ do_profiles.options = [
|
||||
names: ['help', 'h'],
|
||||
type: 'bool',
|
||||
help: 'Show this help.'
|
||||
},
|
||||
{
|
||||
group: 'Action Options'
|
||||
},
|
||||
{
|
||||
names: ['current', 'c'],
|
||||
type: 'string',
|
||||
helpArg: 'NAME',
|
||||
help: 'Switch to the given profile.'
|
||||
}
|
||||
// TODO: finish the implementation
|
||||
//{
|
||||
// names: ['add', 'a'],
|
||||
// type: 'bool',
|
||||
// help: 'Add a new profile.'
|
||||
//},
|
||||
//{
|
||||
// names: ['edit', 'e'],
|
||||
// type: 'string',
|
||||
// helpArg: 'NAME',
|
||||
// help: 'Edit profile NAME in your $EDITOR.'
|
||||
//},
|
||||
//{
|
||||
// names: ['delete', 'd'],
|
||||
// type: 'string',
|
||||
// helpArg: 'NAME',
|
||||
// help: 'Delete profile NAME.'
|
||||
//}
|
||||
|
||||
].concat(common.getCliTableOptions({
|
||||
includeLong: true,
|
||||
sortDefault: sortDefault
|
||||
}));
|
||||
do_profiles.help = [
|
||||
'List and update `triton` CLI profiles.',
|
||||
'List `triton` CLI profiles.',
|
||||
'',
|
||||
'A profile is a configured Triton CloudAPI endpoint. I.e. the',
|
||||
'url, account, key, etc. information required to call a CloudAPI.',
|
||||
@ -207,17 +128,11 @@ do_profiles.help = [
|
||||
'The "CURR" column indicates which profile is the current one.',
|
||||
'',
|
||||
'Usage:',
|
||||
' {{name}} profiles # list profiles',
|
||||
' {{name}} profiles -c|--current NAME # set NAME as current profile',
|
||||
// TODO: finish the implementation
|
||||
//' {{name}} profiles -a|--add [NAME] # add a new profile',
|
||||
//' {{name}} profiles -e|--edit NAME # edit a profile in $EDITOR',
|
||||
//' {{name}} profiles -d|--delete NAME # delete a profile',
|
||||
' {{name}} profiles',
|
||||
'',
|
||||
'{{options}}'
|
||||
].join('\n');
|
||||
|
||||
do_profiles.hidden = true; // TODO: until -a,-e,-d are implemented
|
||||
|
||||
|
||||
module.exports = do_profiles;
|
||||
|
@ -24,10 +24,10 @@ var verror = require('verror'),
|
||||
* Base error. Instances will always have a string `message` and
|
||||
* a string `code` (a CamelCase string).
|
||||
*/
|
||||
function TritonError(options) {
|
||||
function _TritonBaseError(options) {
|
||||
assert.object(options, 'options');
|
||||
assert.string(options.message, 'options.message');
|
||||
assert.string(options.code, 'options.code');
|
||||
assert.optionalString(options.code, 'options.code');
|
||||
assert.optionalObject(options.cause, 'options.cause');
|
||||
assert.optionalNumber(options.statusCode, 'options.statusCode');
|
||||
var self = this;
|
||||
@ -43,7 +43,25 @@ function TritonError(options) {
|
||||
self[k] = options[k];
|
||||
});
|
||||
}
|
||||
util.inherits(TritonError, VError);
|
||||
util.inherits(_TritonBaseError, VError);
|
||||
|
||||
/*
|
||||
* A generic (i.e. a cop out) code-less error.
|
||||
*/
|
||||
function TritonError(cause, message) {
|
||||
if (message === undefined) {
|
||||
message = cause;
|
||||
cause = undefined;
|
||||
}
|
||||
assert.string(message);
|
||||
_TritonBaseError.call(this, {
|
||||
cause: cause,
|
||||
message: message,
|
||||
exitStatus: 1
|
||||
});
|
||||
}
|
||||
util.inherits(TritonError, _TritonBaseError);
|
||||
|
||||
|
||||
function InternalError(cause, message) {
|
||||
if (message === undefined) {
|
||||
@ -51,14 +69,14 @@ function InternalError(cause, message) {
|
||||
cause = undefined;
|
||||
}
|
||||
assert.string(message);
|
||||
TritonError.call(this, {
|
||||
_TritonBaseError.call(this, {
|
||||
cause: cause,
|
||||
message: message,
|
||||
code: 'InternalError',
|
||||
exitStatus: 1
|
||||
});
|
||||
}
|
||||
util.inherits(InternalError, TritonError);
|
||||
util.inherits(InternalError, _TritonBaseError);
|
||||
|
||||
|
||||
/**
|
||||
@ -70,14 +88,14 @@ function ConfigError(cause, message) {
|
||||
cause = undefined;
|
||||
}
|
||||
assert.string(message);
|
||||
TritonError.call(this, {
|
||||
_TritonBaseError.call(this, {
|
||||
cause: cause,
|
||||
message: message,
|
||||
code: 'Config',
|
||||
exitStatus: 1
|
||||
});
|
||||
}
|
||||
util.inherits(ConfigError, TritonError);
|
||||
util.inherits(ConfigError, _TritonBaseError);
|
||||
|
||||
|
||||
/**
|
||||
@ -89,28 +107,28 @@ function UsageError(cause, message) {
|
||||
cause = undefined;
|
||||
}
|
||||
assert.string(message);
|
||||
TritonError.call(this, {
|
||||
_TritonBaseError.call(this, {
|
||||
cause: cause,
|
||||
message: message,
|
||||
code: 'Usage',
|
||||
exitStatus: 1
|
||||
});
|
||||
}
|
||||
util.inherits(UsageError, TritonError);
|
||||
util.inherits(UsageError, _TritonBaseError);
|
||||
|
||||
|
||||
/**
|
||||
* An error signing a request.
|
||||
*/
|
||||
function SigningError(cause) {
|
||||
TritonError.call(this, {
|
||||
_TritonBaseError.call(this, {
|
||||
cause: cause,
|
||||
message: 'error signing request',
|
||||
code: 'Signing',
|
||||
exitStatus: 1
|
||||
});
|
||||
}
|
||||
util.inherits(SigningError, TritonError);
|
||||
util.inherits(SigningError, _TritonBaseError);
|
||||
|
||||
|
||||
/**
|
||||
@ -120,14 +138,14 @@ function SelfSignedCertError(cause, url) {
|
||||
var msg = format('could not access CloudAPI %s because it uses a ' +
|
||||
'self-signed TLS certificate and your current profile is not ' +
|
||||
'configured for insecure access', url);
|
||||
TritonError.call(this, {
|
||||
_TritonBaseError.call(this, {
|
||||
cause: cause,
|
||||
message: msg,
|
||||
code: 'SelfSignedCert',
|
||||
exitStatus: 1
|
||||
});
|
||||
}
|
||||
util.inherits(SelfSignedCertError, TritonError);
|
||||
util.inherits(SelfSignedCertError, _TritonBaseError);
|
||||
|
||||
|
||||
/**
|
||||
@ -140,7 +158,7 @@ function MultiError(errs) {
|
||||
var err = errs[i];
|
||||
lines.push(format(' error (%s): %s', err.code, err.message));
|
||||
}
|
||||
TritonError.call(this, {
|
||||
_TritonBaseError.call(this, {
|
||||
cause: errs[0],
|
||||
message: lines.join('\n'),
|
||||
code: 'MultiError',
|
||||
@ -148,7 +166,7 @@ function MultiError(errs) {
|
||||
});
|
||||
}
|
||||
MultiError.description = 'Multiple errors.';
|
||||
util.inherits(MultiError, TritonError);
|
||||
util.inherits(MultiError, _TritonBaseError);
|
||||
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
"mkdirp": "0.5.1",
|
||||
"node-uuid": "1.4.3",
|
||||
"once": "1.3.2",
|
||||
"read": "1.0.7",
|
||||
"restify-clients": "1.1.0",
|
||||
"restify-errors": "3.0.0",
|
||||
"smartdc-auth": "git+https://github.com/joyent/node-smartdc-auth.git#3be3c1e",
|
||||
|
Reference in New Issue
Block a user