From f66e50c770722c94f6d9bb7bdcbfe35ff6bdaed5 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 29 Dec 2015 14:32:27 -0800 Subject: [PATCH] joyent/node-triton#65 Fix 'triton profile(s)' handling when the user has no profiles yet --- CHANGES.md | 2 +- lib/config.js | 97 ++++++++++++++++++++++++++++++++--------------- lib/do_profile.js | 58 ++++++++++++++++++---------- 3 files changed, 105 insertions(+), 52 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 71ed1f4..6982746 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ ## 3.4.2 (not yet released) -(nothing yet) +- #65 Fix `triton profile(s)` handling when the user has no profiles yet. ## 3.4.1 diff --git a/lib/config.js b/lib/config.js index fa76451..bd56add 100644 --- a/lib/config.js +++ b/lib/config.js @@ -188,13 +188,26 @@ function validateProfile(profile, profilePath) { try { assert.string(profile.name, 'profile.name'); - assert.string(profile.url, 'profile.url'); - assert.string(profile.account, 'profile.account'); - assert.string(profile.keyId, 'profile.keyId'); - assert.optionalBool(profile.insecure, 'profile.insecure'); - assert.optionalString(profile.user, 'profile.user'); + assert.string(profile.url, + profile.name === 'env' ? 'TRITON_URL or SDC_URL' : 'profile.url'); + assert.string(profile.account, + profile.name === 'env' ? 'TRITON_ACCOUNT or SDC_ACCOUNT' + : 'profile.account'); + assert.string(profile.keyId, + profile.name === 'env' ? 'TRITON_KEY_ID or SDC_KEY_ID' + : 'profile.keyId'); + assert.optionalBool(profile.insecure, + profile.name === 'env' ? 'TRITON_INSECURE or SDC_INSECURE' + : 'profile.insecure'); + assert.optionalString(profile.user, + profile.name === 'env' ? 'TRITON_USER or SDC_USER' + : 'profile.user'); } catch (err) { - throw new errors.ConfigError(err.message); + var msg = format('invalid %sprofile%s: %s', + profile.name ? '"' + profile.name + '" ' : '', + profilePath ? ' from ' + profilePath: '', + err.message); + throw new errors.ConfigError(msg); } var bogusFields = []; @@ -219,7 +232,10 @@ function validateProfile(profile, profilePath) { * However, per the "Environment variable integration" comment in cli.js, we * do that manually. * - * @returns {Object} The 'env' profile. + * @returns {Object} The 'env' profile. If no relevant envvars are set, then + * this returns null. + * @throws {errors.ConfigError} If the profile defined by the environment is + * invalid. */ function _loadEnvProfile() { var envProfile = { @@ -233,6 +249,16 @@ function _loadEnvProfile() { } envProfile.url = process.env.TRITON_URL || process.env.SDC_URL; envProfile.keyId = process.env.TRITON_KEY_ID || process.env.SDC_KEY_ID; + + /* + * If none of the above envvars are defined, then there is no env profile. + */ + if (!envProfile.account && !envProfile.user && !envProfile.url && + !envProfile.keyId) + { + return null; + } + if (process.env.TRITON_TLS_INSECURE) { envProfile.insecure = common.boolFromString( process.env.TRITON_TLS_INSECURE); @@ -243,7 +269,7 @@ function _loadEnvProfile() { envProfile.insecure = common.boolFromString(process.env.SDC_TESTING); } - validateProfile(envProfile); + validateProfile(envProfile, 'environment variables'); return envProfile; } @@ -294,32 +320,38 @@ function loadAllProfiles(opts) { assert.string(opts.configDir, 'opts.configDir'); assert.object(opts.log, 'opts.log'); - var profiles = [ - _loadEnvProfile() - ]; + var profiles = []; + + var envProfile = _loadEnvProfile(); + if (envProfile) { + profiles.push(envProfile); + } var d = path.join(common.tildeSync(opts.configDir), 'profiles.d'); - var files = fs.readdirSync(d); - files.forEach(function (file) { - file = path.join(d, file); - var ext = path.extname(file); - if (ext !== '.json') - return; + if (fs.existsSync(d)) { + var files = fs.readdirSync(d); + files.forEach(function (file) { + file = path.join(d, file); + var ext = path.extname(file); + if (ext !== '.json') + return; - var name = path.basename(file).slice(0, - path.extname(file).length); - if (name.toLowerCase() === 'env') { - // Skip the special 'env'. - opts.log.warn({profilePath: file}, - 'invalid "env" profile; skipping'); - return; - } - try { - profiles.push(_profileFromPath(file, name)); - } catch (e) { - opts.log.warn({err: e, profilePath: file}, - 'error loading profile; skipping'); - } - }); + var name = path.basename(file).slice( + 0, - path.extname(file).length); + if (name.toLowerCase() === 'env') { + // Skip the special 'env'. + opts.log.warn({profilePath: file}, + 'invalid "env" profile; skipping'); + return; + } + try { + profiles.push(_profileFromPath(file, name)); + } catch (e) { + opts.log.warn({err: e, profilePath: file}, + 'error loading profile; skipping'); + } + }); + } return profiles; } @@ -353,6 +385,9 @@ function saveProfileSync(opts) { var profilePath = path.resolve(opts.configDir, 'profiles.d', name + '.json'); + if (!fs.existsSync(path.dirname(profilePath))) { + mkdirp.sync(path.dirname(profilePath)); + } fs.writeFileSync(profilePath, JSON.stringify(toSave, null, 4), 'utf8'); console.log('Saved profile "%s"', name); } diff --git a/lib/do_profile.js b/lib/do_profile.js index 2bfe646..aa39546 100644 --- a/lib/do_profile.js +++ b/lib/do_profile.js @@ -292,7 +292,18 @@ function _addProfile(opts, cb) { var context; var data; - vasync.pipeline({funcs: [ + vasync.pipeline({arg: {}, funcs: [ + function getExistingProfiles(ctx, next) { + try { + ctx.profiles = mod_config.loadAllProfiles({ + configDir: cli.configDir, + log: cli.log + }); + } catch (err) { + return next(err); + } + next(); + }, function gatherDataStdin(_, next) { if (opts.file !== '-') { return next(); @@ -327,7 +338,7 @@ function _addProfile(opts, cb) { } next(); }, - function gatherDataInteractive(_, next) { + function gatherDataInteractive(ctx, next) { if (opts.file) { return next(); } else if (!process.stdin.isTTY) { @@ -338,11 +349,6 @@ function _addProfile(opts, cb) { 'create profile: stdout is not a TTY')); } - var profiles = mod_config.loadAllProfiles({ - configDir: cli.configDir, - log: cli.log - }); - var fields = [ { desc: 'A profile name. A short string to identify a ' + 'CloudAPI endpoint to the `triton` CLI.', @@ -354,8 +360,8 @@ function _addProfile(opts, cb) { 'letter followed by lowercase letters, numbers ' + 'and "_", "." and "-".')); } - for (var i = 0; i < profiles.length; i++) { - if (profiles[i].name === value) { + for (var i = 0; i < ctx.profiles.length; i++) { + if (ctx.profiles[i].name === value) { return valCb(new Error(format( 'Profile "%s" already exists.', value))); } @@ -438,17 +444,9 @@ function _addProfile(opts, cb) { next(err); }); }, - function guardAlreadyExists(_, next) { - try { - var profiles = mod_config.loadAllProfiles({ - configDir: cli.configDir, - log: cli.log - }); - } catch (err) { - return next(err); - } - for (var i = 0; i < profiles.length; i++) { - if (data.name === profiles[i].name) { + function guardAlreadyExists(ctx, next) { + for (var i = 0; i < ctx.profiles.length; i++) { + if (data.name === ctx.profiles[i].name) { return next(new errors.TritonError(format( 'profile "%s" already exists', data.name))); } @@ -476,6 +474,26 @@ function _addProfile(opts, cb) { return next(err); } next(); + }, + function setCurrIfTheOnlyProfile(ctx, next) { + if (ctx.profiles.length !== 0) { + next(); + return; + } + + mod_config.setConfigVar({ + configDir: cli.configDir, + name: 'profile', + value: data.name + }, function (err) { + if (err) { + next(err); + return; + } + console.log('Set "%s" as current profile (because it is ' + + 'your only profile)', data.name); + next(); + }); } ]}, cb); }