diff --git a/CHANGES.md b/CHANGES.md index e63eb97..a0410af 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,9 @@ Known issues: ## not yet released +- [joyent/node-triton#137] Improve the handling for the getting started case + when a user may not have envvars or a profile setup. + - [joyent/node-triton#158] tritonapi image cache never expires - [joyent/node-triton#153] Bump restify-clients dep. Thanks, github.com/tomgco. diff --git a/README.md b/README.md index 0ef9051..f8d67e6 100644 --- a/README.md +++ b/README.md @@ -46,20 +46,66 @@ Have the URL handy as you'll need it in the next step. ### Installation -1. Install [node.js](http://nodejs.org/). -2. `npm install -g triton` +Install [node.js](http://nodejs.org/), then: + + npm install -g triton Verify that it is installed and on your PATH: $ triton --version - Triton CLI 1.0.0 + Triton CLI 4.15.0 + https://github.com/joyent/node-triton -Configure the proper environmental variables that correspond to the API endpoint and account, -for example: +To use `triton`, you'll need to configure it to talk to a Triton DataCenter +API endpoint (called CloudAPI). Commonly that is done using a Triton profile: - SDC_URL=https://us-east-3b.api.joyent.com - SDC_ACCOUNT=dave.eddy@joyent.com - SDC_KEY_ID=04:0c:22:25:c9:85:d8:e4:fa:27:0d:67:94:68:9e:e9 + $ triton profile create + A profile name. A short string to identify a CloudAPI endpoint to the + `triton` CLI. + name: sw1 + + The CloudAPI endpoint URL. + url: https://us-sw-1.api.joyent.com + + Your account login name. + account: bob + + Available SSH keys: + 1. 2048-bit RSA key with fingerprint 4e:e7:56:9a:b0:91:31:3e:23:8d:f8:62:12:58:a2:ec + * [in homedir] bob-20160704 id_rsa + + The fingerprint of the SSH key you want to use, or its index in the list + above. If the key you want to use is not listed, make sure it is either saved + in your SSH keys directory or loaded into the SSH agent. + keyId: 1 + + Saved profile "sw1". + + WARNING: Docker uses TLS-based authentication with a different security model + from SSH keys. As a result, the Docker client cannot currently support + encrypted (password protected) keys or SSH agents. If you continue, the + Triton CLI will attempt to format a copy of your SSH *private* key as an + unencrypted TLS cert and place the copy in ~/.triton/docker for use by the + Docker client. + Continue? [y/n] y + Setting up profile "sw1" to use Docker. + Setup profile "sw1" to use Docker (v1.12.3). Try this: + eval "$(triton env --docker sw1)" + docker info + + Set "sw1" as current profile (because it is your only profile). + +Or instead of using profiles, you can set the required environment variables +(`triton` defaults to an "env" profile that uses these environment variables if +no profile is set). For example: + + TRITON_URL=https://us-sw-1.api.joyent.com + TRITON_ACCOUNT=bob + TRITON_KEY_ID=SHA256:j2WoSeOWhFy69BQ0uCR3FAySp9qCZTSCEyT2vRKcL+s + +For compatibility with the older [sdc-* tools from +node-smartdc](https://github.com/joyent/node-smartdc), `triton` also supports +`SDC_URL`, `SDC_ACCOUNT`, etc. environment variables. ### Bash completion diff --git a/lib/cli.js b/lib/cli.js index 4df29d9..7373186 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -25,6 +25,7 @@ var path = require('path'); var vasync = require('vasync'); var common = require('./common'); +var constants = require('./constants'); var mod_config = require('./config'); var errors = require('./errors'); var lib_tritonapi = require('./tritonapi'); @@ -35,21 +36,6 @@ var lib_tritonapi = require('./tritonapi'); var packageJson = require('../package.json'); -var CONFIG_DIR; -if (process.platform === 'win32') { - /* - * For better or worse we are using APPDATA (i.e. the *Roaming* AppData - * dir) over LOCALAPPDATA (non-roaming). The former is meant for "user" - * data, the latter for "machine" data. - * - * TODO: We should likely separate out the *cache* subdir to - * machine-specific data dir. - */ - CONFIG_DIR = path.resolve(process.env.APPDATA, 'Joyent', 'Triton'); -} else { - CONFIG_DIR = path.resolve(process.env.HOME, '.triton'); -} - var OPTIONS = [ { @@ -262,7 +248,8 @@ CLI.prototype.init = function (opts, args, callback) { } if (opts.version) { - console.log(this.name, packageJson.version); + console.log('Triton CLI', packageJson.version); + console.log(packageJson.homepage); callback(false); return; } @@ -274,7 +261,7 @@ CLI.prototype.init = function (opts, args, callback) { opts.url = format('https://%s.api.joyent.com', opts.J); } - this.configDir = CONFIG_DIR; + this.configDir = constants.CLI_CONFIG_DIR; this.__defineGetter__('config', function getConfig() { if (self._config === undefined) { @@ -292,26 +279,48 @@ CLI.prototype.init = function (opts, args, callback) { this.__defineGetter__('profile', function getProfile() { if (self._profile === undefined) { - self._profile = mod_config.loadProfile({ - configDir: self.configDir, - name: self.profileName - }); + try { + self._profile = mod_config.loadProfile({ + configDir: self.configDir, + name: self.profileName + }); + } catch (pErr) { + /* + * Let's be nice for the getting started use case where we + * defaulted to 'env' profile (e.g. the user has never created + * one) and the minimal envvars aren't set. I.e. The user just + * installed and ran `triton ls` or some other command. + */ + if (pErr.code === 'Config' && self.profileName === 'env' && + !opts.profile && !self.config.profile) + { + /* BEGIN JSSTYLED */ + pErr.message += '\n' + + ' No profile information could be loaded.\n' + + ' Use "triton profile create" to create a profile or provide\n' + + ' the required "CloudAPI options" described in "triton --help".\n' + + ' See https://github.com/joyent/node-triton#setup for more help.'; + /* END JSSTYLED */ + } + throw pErr; + } self._applyProfileOverrides(self._profile); self.log.trace({profile: self._profile}, 'loaded profile'); } return self._profile; }); - try { - self.tritonapi = lib_tritonapi.createClient({ - log: self.log, - profile: self.profile, - config: self.config - }); - } catch (createErr) { - callback(createErr); - return; - } + this.__defineGetter__('tritonapi', function getTritonapi() { + if (self._tritonapi === undefined) { + self._tritonapi = lib_tritonapi.createClient({ + log: self.log, + profile: self.profile, + config: self.config + }); + self.log.trace('created tritonapi'); + } + return self._tritonapi; + }); if (process.env.TRITON_COMPLETE) { /* @@ -338,9 +347,9 @@ CLI.prototype.init = function (opts, args, callback) { CLI.prototype.fini = function fini(subcmd, err, cb) { this.log.trace({err: err, subcmd: subcmd}, 'cli fini'); - if (this.tritonapi) { - this.tritonapi.close(); - delete this.tritonapi; + if (this._tritonapi) { + this._tritonapi.close(); + delete this._tritonapi; } cb(); }; @@ -732,7 +741,6 @@ function main(argv) { //---- exports module.exports = { - CONFIG_DIR: CONFIG_DIR, CLI: CLI, main: main }; diff --git a/lib/do_profile/do_create.js b/lib/do_profile/do_create.js index 72ae4c6..2431ac4 100644 --- a/lib/do_profile/do_create.js +++ b/lib/do_profile/do_create.js @@ -314,7 +314,7 @@ function _createProfile(opts, cb) { next(err); return; } - console.log('Set "%s" as current profile (because it is ' + + console.log('\nSet "%s" as current profile (because it is ' + 'your only profile).', data.name); next(); }); diff --git a/lib/do_profile/do_list.js b/lib/do_profile/do_list.js index 1597fbf..35ece1d 100644 --- a/lib/do_profile/do_list.js +++ b/lib/do_profile/do_list.js @@ -80,7 +80,9 @@ function _listProfiles(cli, opts, args, cb) { if (!haveCurr) { if (profiles.length === 0) { process.stderr.write('\nWarning: There is no current profile. ' - + 'Use "triton profile create" to create one.\n'); + + 'Use "triton profile create" to create one,\n' + + 'or set the required "SDC_*/TRITON_*" environment ' + + 'variables: see "triton --help".\n'); } else { process.stderr.write('\nWarning: There is no current profile. ' + 'Use "triton profile set-current ..."\n' diff --git a/lib/do_profile/profilecommon.js b/lib/do_profile/profilecommon.js index 9a53ef9..f428b38 100644 --- a/lib/do_profile/profilecommon.js +++ b/lib/do_profile/profilecommon.js @@ -166,7 +166,8 @@ function profileDockerSetup(opts, cb) { 'Docker client.')); common.promptYesNo({msg: 'Continue? [y/n] '}, function (answer) { if (answer !== 'y') { - console.error('Aborting'); + console.error('Skipping Docker setup (you can run ' + + '"triton profile docker-setup" later).'); next(true); } else { next(); diff --git a/package.json b/package.json index 8e85e13..c96f85b 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "description": "Joyent Triton CLI and client (https://www.joyent.com/triton)", "version": "4.15.0", "author": "Joyent (joyent.com)", + "homepage": "https://github.com/joyent/node-triton", "dependencies": { "assert-plus": "0.2.0", "backoff": "2.4.1", @@ -24,7 +25,7 @@ "sshpk": "1.10.1", "sshpk-agent": "1.4.2", "strsplit": "1.0.0", - "tabula": "1.7.0", + "tabula": "1.9.0", "vasync": "1.6.3", "verror": "1.6.0", "which": "1.2.4",