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' }, | ||||
|             'info', | ||||
|             'account', | ||||
|             'keys', | ||||
|             'key', | ||||
|             'services', | ||||
|             'datacenters' | ||||
|         ], | ||||
| @ -337,6 +337,9 @@ CLI.prototype.do_account = require('./do_account'); | ||||
| CLI.prototype.do_services = require('./do_services'); | ||||
| CLI.prototype.do_datacenters = require('./do_datacenters'); | ||||
| CLI.prototype.do_info = require('./do_info'); | ||||
| 
 | ||||
| // Account keys
 | ||||
| CLI.prototype.do_key = require('./do_key'); | ||||
| CLI.prototype.do_keys = require('./do_keys'); | ||||
| 
 | ||||
| // Images
 | ||||
|  | ||||
| @ -351,14 +351,103 @@ CloudApi.prototype.getAccount = function getAccount(opts, cb) { | ||||
|     this._passThrough(endpoint, opts, cb); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * List account's SSH keys. | ||||
|  * | ||||
|  * @param {Object} opts (object) | ||||
|  * @param {Function} callback of the form `function (err, keys, res)` | ||||
|  */ | ||||
| CloudApi.prototype.listKeys = function listKeys(opts, cb) { | ||||
|     assert.object(opts, 'opts'); | ||||
|     assert.func(cb, 'cb'); | ||||
| 
 | ||||
|     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. | ||||
|  * | ||||
|  * `triton keys ...` | ||||
|  * `triton keys ...` bwcompat shortcut for `triton keys list ...`. | ||||
|  */ | ||||
| 
 | ||||
| var common = require('./common'); | ||||
| var errors = require('./errors'); | ||||
| 
 | ||||
| 
 | ||||
| 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) { | ||||
|         if (err) { | ||||
|             cb(err); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (opts.json) { | ||||
|             common.jsonStream(keys); | ||||
|         } else { | ||||
|             keys.forEach(function (key) { | ||||
|                 console.log(common.chomp(key.key)); | ||||
|             }); | ||||
|         } | ||||
|         cb(); | ||||
|     }); | ||||
| function do_keys(subcmd, opts, args, callback) { | ||||
|     var subcmdArgv = ['node', 'triton', 'key', 'list'].concat(args); | ||||
|     this.dispatch('key', subcmdArgv, callback); | ||||
| } | ||||
| 
 | ||||
| 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' | ||||
| ); | ||||
| do_keys.help = [ | ||||
|     'A shortcut for "triton key list".', | ||||
|     '', | ||||
|     'Usage:', | ||||
|     '    {{name}} key ...' | ||||
| ].join('\n'); | ||||
| 
 | ||||
| do_keys.hidden = true; | ||||
| 
 | ||||
| module.exports = do_keys; | ||||
|  | ||||
| @ -156,7 +156,7 @@ do_list.options = [ | ||||
| 
 | ||||
| do_list.help = [ | ||||
|     /* BEGIN JSSTYLED */ | ||||
|     'List packgaes.', | ||||
|     'List packages.', | ||||
|     '', | ||||
|     'Usage:', | ||||
|     '    {{name}} package list [<filters>]', | ||||
|  | ||||
| @ -22,8 +22,6 @@ do_profiles.help = [ | ||||
|     '    {{name}} profiles ...' | ||||
| ].join('\n'); | ||||
| 
 | ||||
| do_profiles.aliases = ['imgs']; | ||||
| 
 | ||||
| do_profiles.hidden = true; | ||||
| 
 | ||||
| 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 = [ | ||||
|     ['info'], | ||||
|     ['profile'], | ||||
|     ['profiles'], | ||||
|     ['profile list', 'profile ls', 'profiles'], | ||||
|     ['profile get'], | ||||
|     ['profile set-current'], | ||||
|     ['profile create'], | ||||
|     ['profile edit'], | ||||
|     ['profile delete', 'profile rm'], | ||||
|     ['account', 'whoami'], | ||||
|     ['keys'], | ||||
|     ['services'], | ||||
|     ['datacenters'], | ||||
|     ['create-instance', 'create'], | ||||
| @ -37,12 +41,35 @@ var subs = [ | ||||
|     ['delete-instance', 'delete'], | ||||
|     ['wait-instance', 'wait'], | ||||
|     ['ssh'], | ||||
|     ['images', 'imgs'], | ||||
|     ['image', 'img'], | ||||
|     ['packages', 'pkgs'], | ||||
|     ['package', 'pkg'], | ||||
|     ['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
 | ||||
| @ -58,8 +85,11 @@ test('triton subcommands', function (ttt) { | ||||
|             // triton help <subcmd>
 | ||||
|             // triton <subcmd> -h
 | ||||
|             subcmds.forEach(function (subcmd) { | ||||
|                 tt.test(f('    triton help %s', subcmd), function (t) { | ||||
|                     h.triton(['help', subcmd], function (err, stdout, stderr) { | ||||
|                 var helpArgs = subcmd.split(' '); | ||||
|                 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')) | ||||
|                             return t.end(); | ||||
|                         t.equal(stderr, '', 'stderr produced'); | ||||
| @ -69,8 +99,10 @@ test('triton subcommands', function (ttt) { | ||||
|                     }); | ||||
|                 }); | ||||
| 
 | ||||
|                 tt.test(f('    triton %s -h', subcmd), function (t) { | ||||
|                     h.triton([subcmd, '-h'], function (err, stdout, stderr) { | ||||
|                 var flagArgs = subcmd.split(' ').concat('-h'); | ||||
| 
 | ||||
|                 tt.test(f('    triton %s', flagArgs.join(' ')), function (t) { | ||||
|                     h.triton(flagArgs, function (err, stdout, stderr) { | ||||
|                         if (h.ifErr(t, err, 'no error')) | ||||
|                             return t.end(); | ||||
|                         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