clistyle: add support for account keys, expand subcommand tests,
some trivial bug fixes.
This commit is contained in:
parent
f4246b5faf
commit
96216c6e61
@ -212,7 +212,7 @@ function CLI() {
|
|||||||
{ group: 'Other Commands' },
|
{ group: 'Other Commands' },
|
||||||
'info',
|
'info',
|
||||||
'account',
|
'account',
|
||||||
'keys',
|
'key',
|
||||||
'services',
|
'services',
|
||||||
'datacenters'
|
'datacenters'
|
||||||
],
|
],
|
||||||
@ -337,6 +337,9 @@ CLI.prototype.do_account = require('./do_account');
|
|||||||
CLI.prototype.do_services = require('./do_services');
|
CLI.prototype.do_services = require('./do_services');
|
||||||
CLI.prototype.do_datacenters = require('./do_datacenters');
|
CLI.prototype.do_datacenters = require('./do_datacenters');
|
||||||
CLI.prototype.do_info = require('./do_info');
|
CLI.prototype.do_info = require('./do_info');
|
||||||
|
|
||||||
|
// Account keys
|
||||||
|
CLI.prototype.do_key = require('./do_key');
|
||||||
CLI.prototype.do_keys = require('./do_keys');
|
CLI.prototype.do_keys = require('./do_keys');
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
|
@ -351,14 +351,103 @@ CloudApi.prototype.getAccount = function getAccount(opts, cb) {
|
|||||||
this._passThrough(endpoint, opts, cb);
|
this._passThrough(endpoint, opts, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List account's SSH keys.
|
* List account's SSH keys.
|
||||||
*
|
*
|
||||||
|
* @param {Object} opts (object)
|
||||||
* @param {Function} callback of the form `function (err, keys, res)`
|
* @param {Function} callback of the form `function (err, keys, res)`
|
||||||
*/
|
*/
|
||||||
CloudApi.prototype.listKeys = function listKeys(opts, cb) {
|
CloudApi.prototype.listKeys = function listKeys(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
var endpoint = format('/%s/keys', this.account);
|
var endpoint = format('/%s/keys', this.account);
|
||||||
this._passThrough(endpoint, opts, cb);
|
this._passThrough(endpoint, {}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an account's SSH key.
|
||||||
|
*
|
||||||
|
* @param {Object} opts (object)
|
||||||
|
* - {String} fingerprint (required*) The SSH key fingerprint. One of
|
||||||
|
* 'fingerprint' or 'name' is required.
|
||||||
|
* - {String} name (required*) The SSH key name. One of 'fingerprint'
|
||||||
|
* or 'name' is required.
|
||||||
|
* @param {Function} callback of the form `function (err, res)`
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.getKey = function getKey(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.optionalString(opts.fingerprint, 'opts.fingerprint');
|
||||||
|
assert.optionalString(opts.name, 'opts.name');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var identifier = opts.fingerprint || opts.name;
|
||||||
|
assert.ok(identifier, 'one of "fingerprint" or "name" is require');
|
||||||
|
|
||||||
|
var endpoint = format('/%s/keys/%s', this.account,
|
||||||
|
encodeURIComponent(identifier));
|
||||||
|
this._passThrough(endpoint, {}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create/upload a new account SSH public key.
|
||||||
|
*
|
||||||
|
* @param {Object} opts (object)
|
||||||
|
* - {String} key (required) The SSH public key content.
|
||||||
|
* - {String} name (optional) A name for the key. If not given, the
|
||||||
|
* key fingerprint will be used.
|
||||||
|
* @param {Function} callback of the form `function (err, res)`
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.createKey = function createKey(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.string(opts.key, 'opts.key');
|
||||||
|
assert.optionalString(opts.name, 'opts.name');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
name: opts.name,
|
||||||
|
key: opts.key
|
||||||
|
};
|
||||||
|
|
||||||
|
this._request({
|
||||||
|
method: 'POST',
|
||||||
|
path: format('/%s/keys', this.account),
|
||||||
|
data: data
|
||||||
|
}, function (err, req, res, body) {
|
||||||
|
cb(err, body, res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an account's SSH key.
|
||||||
|
*
|
||||||
|
* @param {Object} opts (object)
|
||||||
|
* - {String} fingerprint (required*) The SSH key fingerprint. One of
|
||||||
|
* 'fingerprint' or 'name' is required.
|
||||||
|
* - {String} name (required*) The SSH key name. One of 'fingerprint'
|
||||||
|
* or 'name' is required.
|
||||||
|
* @param {Function} callback of the form `function (err, res)`
|
||||||
|
*/
|
||||||
|
CloudApi.prototype.deleteKey = function deleteKey(opts, cb) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.optionalString(opts.fingerprint, 'opts.fingerprint');
|
||||||
|
assert.optionalString(opts.name, 'opts.name');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
var identifier = opts.fingerprint || opts.name;
|
||||||
|
assert.ok(identifier, 'one of "fingerprint" or "name" is require');
|
||||||
|
|
||||||
|
this._request({
|
||||||
|
method: 'DELETE',
|
||||||
|
path: format('/%s/keys/%s', this.account,
|
||||||
|
encodeURIComponent(identifier))
|
||||||
|
}, function (err, req, res) {
|
||||||
|
cb(err, res);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
140
lib/do_key/do_add.js
Normal file
140
lib/do_key/do_add.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton key add ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var format = require('util').format;
|
||||||
|
var fs = require('fs');
|
||||||
|
var sshpk = require('sshpk');
|
||||||
|
var vasync = require('vasync');
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
var errors = require('../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_add(subcmd, opts, args, cb) {
|
||||||
|
assert.optionalString(opts.name, 'opts.name');
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
cb(new errors.UsageError('missing FILE argument'));
|
||||||
|
return;
|
||||||
|
} else if (args.length > 1) {
|
||||||
|
cb(new errors.UsageError('incorrect number of arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = args[0];
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
|
function gatherDataStdin(ctx, next) {
|
||||||
|
if (filePath !== '-') {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdin = '';
|
||||||
|
process.stdin.resume();
|
||||||
|
process.stdin.on('data', function (chunk) {
|
||||||
|
stdin += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.on('end', function () {
|
||||||
|
ctx.data = stdin;
|
||||||
|
ctx.from = '<stdin>';
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function gatherDataFile(ctx, next) {
|
||||||
|
if (!filePath || filePath === '-') {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.data = fs.readFileSync(filePath);
|
||||||
|
ctx.from = filePath;
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
function validateData(ctx, next) {
|
||||||
|
try {
|
||||||
|
sshpk.parseKey(ctx.data, 'ssh', ctx.from);
|
||||||
|
} catch (keyErr) {
|
||||||
|
next(keyErr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
function createIt(ctx, next) {
|
||||||
|
var createOpts = {
|
||||||
|
userId: opts.userId,
|
||||||
|
key: ctx.data.toString('utf8')
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opts.name) {
|
||||||
|
createOpts.name = opts.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.tritonapi.cloudapi.createKey(createOpts, function (err, key) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.name) {
|
||||||
|
console.log('Added key "%s" (%s)',
|
||||||
|
key.name, key.fingerprint);
|
||||||
|
} else {
|
||||||
|
console.log('Added key %s', key.fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]}, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_add.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON stream output.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['name', 'n'],
|
||||||
|
type: 'string',
|
||||||
|
helpArg: 'NAME',
|
||||||
|
help: 'An optional name for an added key.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
do_add.help = [
|
||||||
|
'Add an SSH key to an account.',
|
||||||
|
'',
|
||||||
|
'Usage:',
|
||||||
|
' {{name}} key add FILE [<options>]',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'',
|
||||||
|
'Where "FILE" must be a file path to an SSH public key, ',
|
||||||
|
'or "-" to pass the public key in on stdin.'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
module.exports = do_add;
|
120
lib/do_key/do_delete.js
Normal file
120
lib/do_key/do_delete.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton key delete ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var format = require('util').format;
|
||||||
|
var fs = require('fs');
|
||||||
|
var sshpk = require('sshpk');
|
||||||
|
var vasync = require('vasync');
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
var errors = require('../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_delete(subcmd, opts, args, cb) {
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
cb(new errors.UsageError('missing KEY argument(s)'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
vasync.pipeline({funcs: [
|
||||||
|
function confirm(_, next) {
|
||||||
|
if (opts.yes) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg;
|
||||||
|
if (args.length === 1) {
|
||||||
|
msg = 'Delete key "' + args[0] + '"? [y/n] ';
|
||||||
|
} else {
|
||||||
|
msg = format('Delete %d keys (%s)? [y/n] ',
|
||||||
|
args.length, args.join(', '));
|
||||||
|
}
|
||||||
|
|
||||||
|
common.promptYesNo({msg: msg}, function (answer) {
|
||||||
|
if (answer !== 'y') {
|
||||||
|
console.error('Aborting');
|
||||||
|
next(true); // early abort signal
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function deleteThem(_, next) {
|
||||||
|
vasync.forEachPipeline({
|
||||||
|
inputs: args,
|
||||||
|
func: function deleteOne(id, nextId) {
|
||||||
|
var delOpts = {
|
||||||
|
fingerprint: id
|
||||||
|
};
|
||||||
|
|
||||||
|
cli.tritonapi.cloudapi.deleteKey(delOpts, function (err) {
|
||||||
|
if (err) {
|
||||||
|
nextId(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Deleted key "%s"', id);
|
||||||
|
nextId();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, next);
|
||||||
|
}
|
||||||
|
]}, function (err) {
|
||||||
|
if (err === true) {
|
||||||
|
err = null;
|
||||||
|
}
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_delete.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON stream output.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['yes', 'y'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Answer yes to confirmation to delete.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
do_delete.help = [
|
||||||
|
'Remove an SSH key from an account.',
|
||||||
|
'',
|
||||||
|
'Usage:',
|
||||||
|
' {{name}} key delete FILE [<options>]',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'',
|
||||||
|
'Where "KEY" is an SSH key "name" or "fingerprint".'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
do_delete.aliases = ['rm'];
|
||||||
|
|
||||||
|
module.exports = do_delete;
|
80
lib/do_key/do_get.js
Normal file
80
lib/do_key/do_get.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton key get ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
var errors = require('../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_get(subcmd, opts, args, cb) {
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
cb(new errors.UsageError('missing KEY argument'));
|
||||||
|
return;
|
||||||
|
} else if (args.length > 1) {
|
||||||
|
cb(new errors.UsageError('incorrect number of arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = args[0];
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
cli.tritonapi.cloudapi.getKey({
|
||||||
|
// Currently `cloudapi.getUserKey` isn't picky about the `name` being
|
||||||
|
// passed in as the `opts.fingerprint` arg.
|
||||||
|
fingerprint: id
|
||||||
|
}, function onKey(err, key) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
console.log(JSON.stringify(key));
|
||||||
|
} else {
|
||||||
|
console.log(common.chomp(key.key));
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_get.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON stream output.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
do_get.help = [
|
||||||
|
'Show a specific SSH key in an account.',
|
||||||
|
'',
|
||||||
|
'Usage:',
|
||||||
|
' {{name}} key get KEY # show account\'s KEY',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'Where "KEY" is an SSH key "name" or "fingerprint".'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
module.exports = do_get;
|
79
lib/do_key/do_list.js
Normal file
79
lib/do_key/do_list.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton key list ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
var errors = require('../errors');
|
||||||
|
|
||||||
|
|
||||||
|
function do_list(subcmd, opts, args, cb) {
|
||||||
|
assert.func(cb, 'cb');
|
||||||
|
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
cb(new errors.UsageError('incorrect number of arguments'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cli = this.top;
|
||||||
|
|
||||||
|
cli.tritonapi.cloudapi.listKeys({}, function onKeys(err, keys) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.json) {
|
||||||
|
console.log(JSON.stringify(keys));
|
||||||
|
} else {
|
||||||
|
keys.forEach(function (key) {
|
||||||
|
console.log(common.chomp(key.key));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_list.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON stream output.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
do_list.help = [
|
||||||
|
'Show all of an account\'s SSH keys.',
|
||||||
|
'',
|
||||||
|
'Usage:',
|
||||||
|
' {{name}} key list [<options>]',
|
||||||
|
'',
|
||||||
|
'{{options}}',
|
||||||
|
'',
|
||||||
|
'By default this lists just the key content for each key -- in other',
|
||||||
|
'words, content appropriate for a "~/.ssh/authorized_keys" file.',
|
||||||
|
'Use `triton keys -j` to see all fields.'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
do_list.aliases = ['ls'];
|
||||||
|
|
||||||
|
module.exports = do_list;
|
48
lib/do_key/index.js
Normal file
48
lib/do_key/index.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Joyent, Inc.
|
||||||
|
*
|
||||||
|
* `triton key ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Cmdln = require('cmdln').Cmdln;
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---- CLI class
|
||||||
|
|
||||||
|
function KeyCLI(top) {
|
||||||
|
this.top = top;
|
||||||
|
|
||||||
|
Cmdln.call(this, {
|
||||||
|
name: top.name + ' key',
|
||||||
|
desc: 'Account SSH key commands.',
|
||||||
|
helpSubcmds: [
|
||||||
|
'help',
|
||||||
|
{ group: 'Key Resources' },
|
||||||
|
'add',
|
||||||
|
'list',
|
||||||
|
'get',
|
||||||
|
'delete'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(KeyCLI, Cmdln);
|
||||||
|
|
||||||
|
KeyCLI.prototype.init = function init(opts, args, cb) {
|
||||||
|
this.log = this.top.log;
|
||||||
|
Cmdln.prototype.init.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyCLI.prototype.do_add = require('./do_add');
|
||||||
|
KeyCLI.prototype.do_get = require('./do_get');
|
||||||
|
KeyCLI.prototype.do_list = require('./do_list');
|
||||||
|
KeyCLI.prototype.do_delete = require('./do_delete');
|
||||||
|
|
||||||
|
module.exports = KeyCLI;
|
@ -7,62 +7,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 Joyent, Inc.
|
* Copyright 2015 Joyent, Inc.
|
||||||
*
|
*
|
||||||
* `triton keys ...`
|
* `triton keys ...` bwcompat shortcut for `triton keys list ...`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var common = require('./common');
|
function do_keys(subcmd, opts, args, callback) {
|
||||||
var errors = require('./errors');
|
var subcmdArgv = ['node', 'triton', 'key', 'list'].concat(args);
|
||||||
|
this.dispatch('key', subcmdArgv, callback);
|
||||||
|
|
||||||
function do_keys(subcmd, opts, args, cb) {
|
|
||||||
if (opts.help) {
|
|
||||||
this.do_help('help', {}, [subcmd], cb);
|
|
||||||
return;
|
|
||||||
} else if (args.length !== 0) {
|
|
||||||
cb(new errors.UsageError('invalid args: ' + args));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tritonapi.cloudapi.listKeys(function (err, keys) {
|
do_keys.help = [
|
||||||
if (err) {
|
'A shortcut for "triton key list".',
|
||||||
cb(err);
|
'',
|
||||||
return;
|
'Usage:',
|
||||||
}
|
' {{name}} key ...'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
if (opts.json) {
|
do_keys.hidden = true;
|
||||||
common.jsonStream(keys);
|
|
||||||
} else {
|
|
||||||
keys.forEach(function (key) {
|
|
||||||
console.log(common.chomp(key.key));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
do_keys.options = [
|
|
||||||
{
|
|
||||||
names: ['help', 'h'],
|
|
||||||
type: 'bool',
|
|
||||||
help: 'Show this help.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
names: ['json', 'j'],
|
|
||||||
type: 'bool',
|
|
||||||
help: 'JSON output.'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
do_keys.help = (
|
|
||||||
'Show account SSH keys.\n'
|
|
||||||
+ '\n'
|
|
||||||
+ 'Usage:\n'
|
|
||||||
+ ' {{name}} keys [<options>]\n'
|
|
||||||
+ '\n'
|
|
||||||
+ '{{options}}'
|
|
||||||
+ '\n'
|
|
||||||
+ 'By default this lists just the key content for each key -- in other\n'
|
|
||||||
+ 'words, content appropriate for a "~/.ssh/authorized_keys" file.\n'
|
|
||||||
+ 'Use `triton keys -j` to see all fields.\n'
|
|
||||||
);
|
|
||||||
|
|
||||||
module.exports = do_keys;
|
module.exports = do_keys;
|
||||||
|
@ -156,7 +156,7 @@ do_list.options = [
|
|||||||
|
|
||||||
do_list.help = [
|
do_list.help = [
|
||||||
/* BEGIN JSSTYLED */
|
/* BEGIN JSSTYLED */
|
||||||
'List packgaes.',
|
'List packages.',
|
||||||
'',
|
'',
|
||||||
'Usage:',
|
'Usage:',
|
||||||
' {{name}} package list [<filters>]',
|
' {{name}} package list [<filters>]',
|
||||||
|
@ -22,8 +22,6 @@ do_profiles.help = [
|
|||||||
' {{name}} profiles ...'
|
' {{name}} profiles ...'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
do_profiles.aliases = ['imgs'];
|
|
||||||
|
|
||||||
do_profiles.hidden = true;
|
do_profiles.hidden = true;
|
||||||
|
|
||||||
module.exports = do_profiles;
|
module.exports = do_profiles;
|
||||||
|
95
test/integration/cli-keys.test.js
Normal file
95
test/integration/cli-keys.test.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Joyent, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Integration tests for `triton key ...`
|
||||||
|
*/
|
||||||
|
|
||||||
|
var h = require('./helpers');
|
||||||
|
var test = require('tape');
|
||||||
|
var backoff = require('backoff');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- Globals
|
||||||
|
|
||||||
|
var KEY_PATH = 'data/id_rsa.pub';
|
||||||
|
var KEY_SIG = '66:ca:1c:09:75:99:35:69:be:91:08:25:03:c0:17:c0';
|
||||||
|
var KEY_EMAIL = 'test@localhost.local';
|
||||||
|
var MAX_CHECK_KEY_TRIES = 10;
|
||||||
|
|
||||||
|
// --- Tests
|
||||||
|
|
||||||
|
test('triton key', function (tt) {
|
||||||
|
tt.test(' triton key add', function (t) {
|
||||||
|
h.triton('key add ' + KEY_PATH, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton key add'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(stdout.match('Added key "' + KEY_SIG + '"'));
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton key get', function (t) {
|
||||||
|
h.triton('key get ' + KEY_SIG, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton key get'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(stdout.match(KEY_EMAIL));
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton key list', function (t) {
|
||||||
|
h.triton('key list', function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton key list'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
// there should always be at least two keys -- the original
|
||||||
|
// account's key, and the test key these tests added
|
||||||
|
var keys = stdout.split('\n');
|
||||||
|
t.ok(keys.length > 2, 'triton key list expected key num');
|
||||||
|
|
||||||
|
var testKeys = keys.filter(function (key) {
|
||||||
|
return key.match(KEY_EMAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
// this test is a tad dodgy, since it's plausible that there might
|
||||||
|
// be other test keys with different signatures lying around
|
||||||
|
t.equal(testKeys.length, 1, 'triton key list test key found');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' triton key delete', function (t) {
|
||||||
|
var cmd = 'key delete ' + KEY_SIG + ' --yes';
|
||||||
|
h.triton(cmd, function (err, stdout, stderr) {
|
||||||
|
if (h.ifErr(t, err, 'triton key delete'))
|
||||||
|
return t.end();
|
||||||
|
|
||||||
|
t.ok(stdout.match('Deleted key "' + KEY_SIG + '"'));
|
||||||
|
|
||||||
|
// verify key is gone, which sometimes takes a while
|
||||||
|
var call = backoff.call(function checkKey(next) {
|
||||||
|
h.triton('key get ' + KEY_SIG, function (err2) {
|
||||||
|
next(!err2);
|
||||||
|
});
|
||||||
|
}, function (err3) {
|
||||||
|
h.ifErr(t, err3, 'triton key delete did not remove key');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
call.failAfter(MAX_CHECK_KEY_TRIES);
|
||||||
|
call.start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -22,9 +22,13 @@ var common = require('../../lib/common');
|
|||||||
var subs = [
|
var subs = [
|
||||||
['info'],
|
['info'],
|
||||||
['profile'],
|
['profile'],
|
||||||
['profiles'],
|
['profile list', 'profile ls', 'profiles'],
|
||||||
|
['profile get'],
|
||||||
|
['profile set-current'],
|
||||||
|
['profile create'],
|
||||||
|
['profile edit'],
|
||||||
|
['profile delete', 'profile rm'],
|
||||||
['account', 'whoami'],
|
['account', 'whoami'],
|
||||||
['keys'],
|
|
||||||
['services'],
|
['services'],
|
||||||
['datacenters'],
|
['datacenters'],
|
||||||
['create-instance', 'create'],
|
['create-instance', 'create'],
|
||||||
@ -37,12 +41,35 @@ var subs = [
|
|||||||
['delete-instance', 'delete'],
|
['delete-instance', 'delete'],
|
||||||
['wait-instance', 'wait'],
|
['wait-instance', 'wait'],
|
||||||
['ssh'],
|
['ssh'],
|
||||||
['images', 'imgs'],
|
|
||||||
['image', 'img'],
|
|
||||||
['packages', 'pkgs'],
|
|
||||||
['package', 'pkg'],
|
|
||||||
['networks'],
|
['networks'],
|
||||||
['network']
|
['network'],
|
||||||
|
['key'],
|
||||||
|
['key add'],
|
||||||
|
['key list', 'key ls', 'keys'],
|
||||||
|
['key get'],
|
||||||
|
['key delete', 'key rm'],
|
||||||
|
['image', 'img'],
|
||||||
|
['image get'],
|
||||||
|
['image list', 'images', 'imgs'],
|
||||||
|
['package', 'pkg'],
|
||||||
|
['package get'],
|
||||||
|
['package list', 'packages', 'pkgs'],
|
||||||
|
['rbac'],
|
||||||
|
['rbac info'],
|
||||||
|
['rbac apply'],
|
||||||
|
['rbac users'],
|
||||||
|
['rbac user'],
|
||||||
|
['rbac keys'],
|
||||||
|
['rbac key'],
|
||||||
|
['rbac policies'],
|
||||||
|
['rbac policy'],
|
||||||
|
['rbac roles'],
|
||||||
|
['rbac role'],
|
||||||
|
['rbac instance-role-tags'],
|
||||||
|
['rbac image-role-tags'],
|
||||||
|
['rbac network-role-tags'],
|
||||||
|
['rbac package-role-tags'],
|
||||||
|
['rbac role-tags']
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Tests
|
// --- Tests
|
||||||
@ -58,8 +85,11 @@ test('triton subcommands', function (ttt) {
|
|||||||
// triton help <subcmd>
|
// triton help <subcmd>
|
||||||
// triton <subcmd> -h
|
// triton <subcmd> -h
|
||||||
subcmds.forEach(function (subcmd) {
|
subcmds.forEach(function (subcmd) {
|
||||||
tt.test(f(' triton help %s', subcmd), function (t) {
|
var helpArgs = subcmd.split(' ');
|
||||||
h.triton(['help', subcmd], function (err, stdout, stderr) {
|
helpArgs.splice(helpArgs.length - 1, 0, 'help');
|
||||||
|
|
||||||
|
tt.test(f(' triton %s', helpArgs.join(' ')), function (t) {
|
||||||
|
h.triton(helpArgs, function (err, stdout, stderr) {
|
||||||
if (h.ifErr(t, err, 'no error'))
|
if (h.ifErr(t, err, 'no error'))
|
||||||
return t.end();
|
return t.end();
|
||||||
t.equal(stderr, '', 'stderr produced');
|
t.equal(stderr, '', 'stderr produced');
|
||||||
@ -69,8 +99,10 @@ test('triton subcommands', function (ttt) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
tt.test(f(' triton %s -h', subcmd), function (t) {
|
var flagArgs = subcmd.split(' ').concat('-h');
|
||||||
h.triton([subcmd, '-h'], function (err, stdout, stderr) {
|
|
||||||
|
tt.test(f(' triton %s', flagArgs.join(' ')), function (t) {
|
||||||
|
h.triton(flagArgs, function (err, stdout, stderr) {
|
||||||
if (h.ifErr(t, err, 'no error'))
|
if (h.ifErr(t, err, 'no error'))
|
||||||
return t.end();
|
return t.end();
|
||||||
t.equal(stderr, '', 'stderr produced');
|
t.equal(stderr, '', 'stderr produced');
|
||||||
|
1
test/integration/data/id_rsa.pub
Normal file
1
test/integration/data/id_rsa.pub
Normal file
@ -0,0 +1 @@
|
|||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNr4q9zMKtylAZGr17fjtUfH2gS+6Gx4m3TJ1H5QwC97JCmtTgke/PBRSEacbXjsYlBjJ9DifNpIbrZrP9hOGhknPDyC3EaRUe/TCUCVGRiHFspurxZAiHvfENCQcvDaVcu9/tO3QyGjDSoYSaQ6NNvl8+yPZ6+mGtXeMnXlCWEvhy/fe3yNVp0isvSIinB2paI+pQqmytJ8omCGShdLqq4/Lvw/zbROe6gEb78+mvwqS+fqYDuPjGc5DXZATqM8rjOSKSPllzNILh9MbR3cHDBfhVi77jL9P8FfQv1U1SZBTbZj78xFcBRnGxrxWE0H7FcXldK5vJvafqWgxZZpgb test@localhost.local
|
Reference in New Issue
Block a user