parent
666b541e42
commit
54923c9ae3
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
## 4.4.1 (not yet released)
|
## 4.4.1 (not yet released)
|
||||||
|
|
||||||
(nothing yet)
|
- #83, #84 Fix running `triton` on Windows.
|
||||||
|
Note: Triton config is stored in "%APPDATA%/Joyent/Triton/..." on Windows,
|
||||||
|
"~/.triton/..." on other platforms.
|
||||||
|
|
||||||
|
|
||||||
## 4.4.0
|
## 4.4.0
|
||||||
|
@ -64,7 +64,8 @@ for example:
|
|||||||
Install Bash completion with
|
Install Bash completion with
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
triton completion > /usr/local/etc/bash_completion.d/triton
|
triton completion > /usr/local/etc/bash_completion.d/triton # Mac
|
||||||
|
triton completion > /etc/bash_completion.d/triton # Linux
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, if you don't have or don't want to use a "bash\_completion.d"
|
Alternatively, if you don't have or don't want to use a "bash\_completion.d"
|
||||||
@ -258,7 +259,7 @@ A basic example:
|
|||||||
|
|
||||||
This section defines all the vars in a TritonApi config. The baked in defaults
|
This section defines all the vars in a TritonApi config. The baked in defaults
|
||||||
are in "etc/defaults.json" and can be overriden for the CLI in
|
are in "etc/defaults.json" and can be overriden for the CLI in
|
||||||
"~/.triton/config.json".
|
"~/.triton/config.json" (on Windows: "%APPDATA%/Joyent/Triton/config.json").
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| ---- | ----------- |
|
| ---- | ----------- |
|
||||||
|
18
lib/cli.js
18
lib/cli.js
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Joyent, Inc.
|
* Copyright 2016 Joyent, Inc.
|
||||||
*
|
*
|
||||||
* The `triton` CLI class.
|
* The `triton` CLI class.
|
||||||
*/
|
*/
|
||||||
@ -34,7 +34,21 @@ var tritonapi = require('./tritonapi');
|
|||||||
|
|
||||||
var pkg = require('../package.json');
|
var pkg = require('../package.json');
|
||||||
|
|
||||||
var CONFIG_DIR = path.resolve(process.env.HOME, '.triton');
|
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 = [
|
var OPTIONS = [
|
||||||
{
|
{
|
||||||
|
@ -855,19 +855,24 @@ function deepEqual(a, b) {
|
|||||||
/**
|
/**
|
||||||
* Resolve "~/..." and "~" to an absolute path.
|
* Resolve "~/..." and "~" to an absolute path.
|
||||||
*
|
*
|
||||||
* Limitations: This does not handle "~user/...". This depends on the HOME
|
* Limitations:
|
||||||
* envvar being defined. A better alternative is the "tilde-expansion"
|
* - This does not handle "~user/...".
|
||||||
* module (used elsewhere in node-triton), but that doesn't have a sync
|
* - This depends on the HOME envvar being defined (%USERPROFILE% on Windows).
|
||||||
* option.
|
|
||||||
*/
|
*/
|
||||||
function tildeSync(s) {
|
function tildeSync(s) {
|
||||||
var home = process.env.HOME;
|
var envvar = (process.platform === 'win32' ? 'USERPROFILE' : 'HOME');
|
||||||
|
var home = process.env[envvar];
|
||||||
if (!home) {
|
if (!home) {
|
||||||
return s;
|
throw new Error(format('cannot determine home dir: %s environment ' +
|
||||||
} else if (s === '~') {
|
'variable is not defined', envvar));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s === '~') {
|
||||||
return home;
|
return home;
|
||||||
} else if (s.slice(0, 2) === '~/') {
|
} else if (s.slice(0, 2) === '~/' ||
|
||||||
return home + s.slice(1);
|
(process.platform === 'win32' && s.slice(0, 2) === '~'+path.sep))
|
||||||
|
{
|
||||||
|
return path.resolve(home, s.slice(2));
|
||||||
} else {
|
} else {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ var format = require('util').format;
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var strsplit = require('strsplit');
|
var strsplit = require('strsplit');
|
||||||
var sshpk = require('sshpk');
|
var sshpk = require('sshpk');
|
||||||
var tilde = require('tilde-expansion');
|
|
||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
|
|
||||||
var common = require('./common');
|
var common = require('./common');
|
||||||
|
@ -15,7 +15,6 @@ var format = require('util').format;
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var strsplit = require('strsplit');
|
var strsplit = require('strsplit');
|
||||||
var tabula = require('tabula');
|
var tabula = require('tabula');
|
||||||
var tilde = require('tilde-expansion');
|
|
||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
|
|
||||||
var common = require('../common');
|
var common = require('../common');
|
||||||
|
@ -8,7 +8,6 @@ var assert = require('assert-plus');
|
|||||||
var format = require('util').format;
|
var format = require('util').format;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var sshpk = require('sshpk');
|
var sshpk = require('sshpk');
|
||||||
var tilde = require('tilde-expansion');
|
|
||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
|
|
||||||
var common = require('../common');
|
var common = require('../common');
|
||||||
@ -168,29 +167,28 @@ function _createProfile(opts, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try as a local path.
|
// Try as a local path.
|
||||||
tilde(value, function (keyPath) {
|
var keyPath = common.tildeSync(value);
|
||||||
fs.stat(keyPath, function (statErr, stats) {
|
fs.stat(keyPath, function (statErr, stats) {
|
||||||
if (statErr || !stats.isFile()) {
|
if (statErr || !stats.isFile()) {
|
||||||
return valCb(new Error(format(
|
return valCb(new Error(format(
|
||||||
'"%s" is neither a valid fingerprint, ' +
|
'"%s" is neither a valid fingerprint, ' +
|
||||||
'nor an existing file', value)));
|
'nor an existing file', value)));
|
||||||
|
}
|
||||||
|
fs.readFile(keyPath, function (readErr, keyData) {
|
||||||
|
if (readErr) {
|
||||||
|
return valCb(readErr);
|
||||||
|
}
|
||||||
|
var keyType = (keyPath.slice(-4) === '.pub'
|
||||||
|
? 'ssh' : 'pem');
|
||||||
|
try {
|
||||||
|
var key = sshpk.parseKey(keyData, keyType);
|
||||||
|
} catch (keyErr) {
|
||||||
|
return valCb(keyErr);
|
||||||
}
|
}
|
||||||
fs.readFile(keyPath, function (readErr, keyData) {
|
|
||||||
if (readErr) {
|
|
||||||
return valCb(readErr);
|
|
||||||
}
|
|
||||||
var keyType = (keyPath.slice(-4) === '.pub'
|
|
||||||
? 'ssh' : 'pem');
|
|
||||||
try {
|
|
||||||
var key = sshpk.parseKey(keyData, keyType);
|
|
||||||
} catch (keyErr) {
|
|
||||||
return valCb(keyErr);
|
|
||||||
}
|
|
||||||
|
|
||||||
var newVal = key.fingerprint('md5').toString();
|
var newVal = key.fingerprint('md5').toString();
|
||||||
console.log('Fingerprint: %s', newVal);
|
console.log('Fingerprint: %s', newVal);
|
||||||
valCb(null, newVal);
|
valCb(null, newVal);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ var format = require('util').format;
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var strsplit = require('strsplit');
|
var strsplit = require('strsplit');
|
||||||
var sshpk = require('sshpk');
|
var sshpk = require('sshpk');
|
||||||
var tilde = require('tilde-expansion');
|
|
||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
|
|
||||||
var common = require('../common');
|
var common = require('../common');
|
||||||
|
@ -14,9 +14,9 @@ var assert = require('assert-plus');
|
|||||||
var format = require('util').format;
|
var format = require('util').format;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var strsplit = require('strsplit');
|
var strsplit = require('strsplit');
|
||||||
var tilde = require('tilde-expansion');
|
|
||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
|
|
||||||
|
var common = require('./common');
|
||||||
var errors = require('./errors');
|
var errors = require('./errors');
|
||||||
|
|
||||||
|
|
||||||
@ -184,38 +184,37 @@ function _addMetadataFromJsonStr(ilk, metadata, s, from, cb) {
|
|||||||
|
|
||||||
function _addMetadataFromFile(ilk, metadata, file, cb) {
|
function _addMetadataFromFile(ilk, metadata, file, cb) {
|
||||||
assert.string(ilk, 'ilk');
|
assert.string(ilk, 'ilk');
|
||||||
tilde(file, function (metaPath) {
|
var metaPath = common.tildeSync(file);
|
||||||
fs.stat(metaPath, function (statErr, stats) {
|
fs.stat(metaPath, function (statErr, stats) {
|
||||||
if (statErr || !stats.isFile()) {
|
if (statErr || !stats.isFile()) {
|
||||||
cb(new errors.TritonError(format(
|
cb(new errors.TritonError(format(
|
||||||
'"%s" is not an existing file', file)));
|
'"%s" is not an existing file', file)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.readFile(metaPath, 'utf8', function (readErr, data) {
|
||||||
|
if (readErr) {
|
||||||
|
cb(readErr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fs.readFile(metaPath, 'utf8', function (readErr, data) {
|
/*
|
||||||
if (readErr) {
|
* The file is either a JSON object (first non-space
|
||||||
cb(readErr);
|
* char is '{'), or newline-separated key=value
|
||||||
return;
|
* pairs.
|
||||||
}
|
*/
|
||||||
/*
|
var dataTrim = data.trim();
|
||||||
* The file is either a JSON object (first non-space
|
if (dataTrim.length && dataTrim[0] === '{') {
|
||||||
* char is '{'), or newline-separated key=value
|
_addMetadataFromJsonStr(ilk, metadata, dataTrim, file, cb);
|
||||||
* pairs.
|
} else {
|
||||||
*/
|
var lines = dataTrim.split(/\r?\n/g).filter(
|
||||||
var dataTrim = data.trim();
|
function (line) { return line.trim(); });
|
||||||
if (dataTrim.length && dataTrim[0] === '{') {
|
vasync.forEachPipeline({
|
||||||
_addMetadataFromJsonStr(ilk, metadata, dataTrim, file, cb);
|
inputs: lines,
|
||||||
} else {
|
func: function oneLine(line, next) {
|
||||||
var lines = dataTrim.split(/\r?\n/g).filter(
|
_addMetadataFromKvStr(
|
||||||
function (line) { return line.trim(); });
|
ilk, metadata, line, file, next);
|
||||||
vasync.forEachPipeline({
|
}
|
||||||
inputs: lines,
|
}, cb);
|
||||||
func: function oneLine(line, next) {
|
}
|
||||||
_addMetadataFromKvStr(
|
|
||||||
ilk, metadata, line, file, next);
|
|
||||||
}
|
|
||||||
}, cb);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -266,20 +265,19 @@ function _addMetadataFromKfStr(ilk, metadata, s, from, cb) {
|
|||||||
function _addMetadatumFromFile(ilk, metadata, key, file, from, cb) {
|
function _addMetadatumFromFile(ilk, metadata, key, file, from, cb) {
|
||||||
assert.string(ilk, 'ilk');
|
assert.string(ilk, 'ilk');
|
||||||
|
|
||||||
tilde(file, function (filePath) {
|
var filePath = common.tildeSync(file);
|
||||||
fs.stat(filePath, function (statErr, stats) {
|
fs.stat(filePath, function (statErr, stats) {
|
||||||
if (statErr || !stats.isFile()) {
|
if (statErr || !stats.isFile()) {
|
||||||
cb(new errors.TritonError(format(
|
cb(new errors.TritonError(format(
|
||||||
'%s path "%s" is not an existing file', ilk, file)));
|
'%s path "%s" is not an existing file', ilk, file)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.readFile(filePath, 'utf8', function (readErr, content) {
|
||||||
|
if (readErr) {
|
||||||
|
cb(readErr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fs.readFile(filePath, 'utf8', function (readErr, content) {
|
_addMetadatum(ilk, metadata, key, content, from, cb);
|
||||||
if (readErr) {
|
|
||||||
cb(readErr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_addMetadatum(ilk, metadata, key, content, from, cb);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
16
lib/rbac.js
16
lib/rbac.js
@ -17,7 +17,6 @@ var mkdirp = require('mkdirp');
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var rimraf = require('rimraf');
|
var rimraf = require('rimraf');
|
||||||
var sshpk = require('sshpk');
|
var sshpk = require('sshpk');
|
||||||
var tilde = require('tilde-expansion');
|
|
||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
|
|
||||||
var common = require('./common');
|
var common = require('./common');
|
||||||
@ -785,14 +784,13 @@ function executeRbacUpdatePlan(ctx, cb) {
|
|||||||
c.user);
|
c.user);
|
||||||
vasync.pipeline({arg: {}, funcs: [
|
vasync.pipeline({arg: {}, funcs: [
|
||||||
function vars(ctx2, next2) {
|
function vars(ctx2, next2) {
|
||||||
tilde('~', function (s) {
|
ctx2.homeDir = common.tildeSync('~');
|
||||||
ctx2.homeDir = s;
|
ctx2.keyName = format('%s user %s',
|
||||||
ctx2.keyName = format('%s user %s',
|
c.currProfile.name, c.user);
|
||||||
c.currProfile.name, c.user);
|
ctx2.keyPath = path.resolve(ctx2.homeDir, '.ssh',
|
||||||
ctx2.keyPath = format('%s/.ssh/%s-user-%s.id_rsa',
|
format('%s-user-%s.id_rsa',
|
||||||
ctx2.homeDir, c.currProfile.name, c.user);
|
c.currProfile.name, c.user));
|
||||||
next2();
|
next2();
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function rmOldPrivKey(ctx2, next2) {
|
function rmOldPrivKey(ctx2, next2) {
|
||||||
rimraf(ctx2.keyPath, next2);
|
rimraf(ctx2.keyPath, next2);
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
"smartdc-auth": "2.2.3",
|
"smartdc-auth": "2.2.3",
|
||||||
"strsplit": "1.0.0",
|
"strsplit": "1.0.0",
|
||||||
"tabula": "1.7.0",
|
"tabula": "1.7.0",
|
||||||
"tilde-expansion": "0.0.0",
|
|
||||||
"vasync": "1.6.3",
|
"vasync": "1.6.3",
|
||||||
"verror": "1.6.0",
|
"verror": "1.6.0",
|
||||||
"wordwrap": "1.0.0"
|
"wordwrap": "1.0.0"
|
||||||
|
Reference in New Issue
Block a user