improvements for using node-triton as a module
This commit is contained in:
parent
a40e0d6f8c
commit
21164320c7
13
CHANGES.md
13
CHANGES.md
@ -1,8 +1,17 @@
|
|||||||
# node-triton changelog
|
# node-triton changelog
|
||||||
|
|
||||||
## 3.3.1 (not yet released)
|
## 3.4.0 (not yet released)
|
||||||
|
|
||||||
(nothing yet)
|
- Improvements for using node-triton as a module. E.g. a simple example:
|
||||||
|
|
||||||
|
var triton = require('triton');
|
||||||
|
var client = triton.createClient({profileName: 'env'});
|
||||||
|
client.listImages(function (err, imgs) {
|
||||||
|
console.log(err);
|
||||||
|
console.log(imgs);
|
||||||
|
});
|
||||||
|
|
||||||
|
See the README and "lib/index.js" for more info.
|
||||||
|
|
||||||
|
|
||||||
## 3.3.0
|
## 3.3.0
|
||||||
|
32
README.md
32
README.md
@ -74,7 +74,7 @@ For a more permanent installation:
|
|||||||
triton completion > /usr/local/etc/bash_completion.d/triton
|
triton completion > /usr/local/etc/bash_completion.d/triton
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## `triton` CLI Usage
|
||||||
|
|
||||||
### Create and view instances
|
### Create and view instances
|
||||||
|
|
||||||
@ -214,6 +214,33 @@ as a single `DOCKER_HOST`. See the [Triton Docker
|
|||||||
documentation](https://apidocs.joyent.com/docker) for more information.)
|
documentation](https://apidocs.joyent.com/docker) for more information.)
|
||||||
|
|
||||||
|
|
||||||
|
## `TritonApi` Module Usage
|
||||||
|
|
||||||
|
Node-triton can also be used as a node module for your own node.js tooling.
|
||||||
|
A basic example:
|
||||||
|
|
||||||
|
var triton = require('triton');
|
||||||
|
|
||||||
|
// See `createClient` block comment for full usage details:
|
||||||
|
// https://github.com/joyent/node-triton/blob/master/lib/index.js
|
||||||
|
var client = triton.createClient({
|
||||||
|
profile: {
|
||||||
|
url: URL,
|
||||||
|
account: ACCOUNT,
|
||||||
|
keyId: KEY_ID
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.listImages(function (err, images) {
|
||||||
|
client.close(); // Remember to close the client to close TCP conn.
|
||||||
|
if (err) {
|
||||||
|
console.error('listImages err:', err);
|
||||||
|
} else {
|
||||||
|
console.log(JSON.stringify(images, null, 4));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
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
|
||||||
@ -229,7 +256,8 @@ are in "etc/defaults.json" and can be overriden for the CLI in
|
|||||||
## node-triton differences with node-smartdc
|
## node-triton differences with node-smartdc
|
||||||
|
|
||||||
- There is a single `triton` command instead of a number of `sdc-*` commands.
|
- There is a single `triton` command instead of a number of `sdc-*` commands.
|
||||||
- The `SDC_USER` env variable is accepted in preference to `SDC_ACCOUNT`.
|
- `TRITON_*` environment variables are preferred to the `SDC_*` environment
|
||||||
|
variables. However the `SDC_*` envvars are still supported.
|
||||||
|
|
||||||
|
|
||||||
## cloudapi2.js differences with node-smartdc/lib/cloudapi.js
|
## cloudapi2.js differences with node-smartdc/lib/cloudapi.js
|
||||||
|
27
lib/bunyannoop.js
Normal file
27
lib/bunyannoop.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A stub for a `bunyan.createLogger()` that does no logging.
|
||||||
|
*/
|
||||||
|
function BunyanNoopLogger() {}
|
||||||
|
BunyanNoopLogger.prototype.trace = function () {};
|
||||||
|
BunyanNoopLogger.prototype.debug = function () {};
|
||||||
|
BunyanNoopLogger.prototype.info = function () {};
|
||||||
|
BunyanNoopLogger.prototype.warn = function () {};
|
||||||
|
BunyanNoopLogger.prototype.error = function () {};
|
||||||
|
BunyanNoopLogger.prototype.fatal = function () {};
|
||||||
|
BunyanNoopLogger.prototype.child = function () { return this; };
|
||||||
|
BunyanNoopLogger.prototype.end = function () {};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
BunyanNoopLogger: BunyanNoopLogger
|
||||||
|
};
|
29
lib/cli.js
29
lib/cli.js
@ -212,12 +212,6 @@ CLI.prototype.init = function (opts, args, callback) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
|
|
||||||
if (opts.version) {
|
|
||||||
console.log(this.name, pkg.version);
|
|
||||||
callback(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log = bunyan.createLogger({
|
this.log = bunyan.createLogger({
|
||||||
name: this.name,
|
name: this.name,
|
||||||
serializers: bunyan.stdSerializers,
|
serializers: bunyan.stdSerializers,
|
||||||
@ -230,6 +224,12 @@ CLI.prototype.init = function (opts, args, callback) {
|
|||||||
this.showErrStack = true;
|
this.showErrStack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.version) {
|
||||||
|
console.log(this.name, pkg.version);
|
||||||
|
callback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.url && opts.J) {
|
if (opts.url && opts.J) {
|
||||||
callback(new errors.UsageError(
|
callback(new errors.UsageError(
|
||||||
'cannot use both "--url" and "-J" options'));
|
'cannot use both "--url" and "-J" options'));
|
||||||
@ -268,6 +268,16 @@ 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;
|
||||||
|
}
|
||||||
|
cb(err, subcmd);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply overrides from CLI options to the given profile object *in place*.
|
* Apply overrides from CLI options to the given profile object *in place*.
|
||||||
*/
|
*/
|
||||||
@ -380,7 +390,14 @@ function main(argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We'd like to NOT use `process.exit` because that doesn't always
|
||||||
|
* allow std handles to flush (e.g. all logging to complete). However
|
||||||
|
* I don't know of another way to exit non-zero.
|
||||||
|
*/
|
||||||
|
if (exitStatus !== 0) {
|
||||||
process.exit(exitStatus);
|
process.exit(exitStatus);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ var querystring = require('querystring');
|
|||||||
var vasync = require('vasync');
|
var vasync = require('vasync');
|
||||||
var auth = require('smartdc-auth');
|
var auth = require('smartdc-auth');
|
||||||
|
|
||||||
|
var bunyannoop = require('./bunyannoop');
|
||||||
var errors = require('./errors');
|
var errors = require('./errors');
|
||||||
var SaferJsonClient = require('./SaferJsonClient');
|
var SaferJsonClient = require('./SaferJsonClient');
|
||||||
|
|
||||||
@ -55,21 +56,6 @@ var OS_PLATFORM = os.platform();
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---- internal support stuff
|
|
||||||
|
|
||||||
// A no-op bunyan logger shim.
|
|
||||||
function BunyanNoopLogger() {}
|
|
||||||
BunyanNoopLogger.prototype.trace = function () {};
|
|
||||||
BunyanNoopLogger.prototype.debug = function () {};
|
|
||||||
BunyanNoopLogger.prototype.info = function () {};
|
|
||||||
BunyanNoopLogger.prototype.warn = function () {};
|
|
||||||
BunyanNoopLogger.prototype.error = function () {};
|
|
||||||
BunyanNoopLogger.prototype.fatal = function () {};
|
|
||||||
BunyanNoopLogger.prototype.child = function () { return this; };
|
|
||||||
BunyanNoopLogger.prototype.end = function () {};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---- client API
|
// ---- client API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,7 +97,7 @@ function CloudApi(options) {
|
|||||||
this.account = options.account;
|
this.account = options.account;
|
||||||
this.user = options.user; // optional RBAC subuser
|
this.user = options.user; // optional RBAC subuser
|
||||||
this.sign = options.sign;
|
this.sign = options.sign;
|
||||||
this.log = options.log || new BunyanNoopLogger();
|
this.log = options.log || new bunyannoop.BunyanNoopLogger();
|
||||||
if (!options.version) {
|
if (!options.version) {
|
||||||
options.version = '*';
|
options.version = '*';
|
||||||
}
|
}
|
||||||
@ -132,6 +118,13 @@ function CloudApi(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CloudApi.prototype.close = function close(callback) {
|
||||||
|
this.log.trace({host: this.client.url && this.client.url.host},
|
||||||
|
'close cloudapi http client');
|
||||||
|
this.client.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
CloudApi.prototype._getAuthHeaders = function _getAuthHeaders(callback) {
|
CloudApi.prototype._getAuthHeaders = function _getAuthHeaders(callback) {
|
||||||
assert.func(callback, 'callback');
|
assert.func(callback, 'callback');
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -330,19 +330,25 @@ function normShortId(s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* take a "profile" object and return a slug based on the account name
|
* Take a "profile" object and return a slug based on: account, url and user.
|
||||||
* and DC URL. This is currently used to create a filesystem-safe name
|
* This is currently used to create a filesystem-safe name to use for caching
|
||||||
* to use for caching
|
|
||||||
*/
|
*/
|
||||||
function profileSlug(o) {
|
function profileSlug(o) {
|
||||||
assert.object(o, 'o');
|
assert.object(o, 'o');
|
||||||
assert.string(o.account, 'o.account');
|
assert.string(o.account, 'o.account');
|
||||||
assert.string(o.url, 'o.url');
|
assert.string(o.url, 'o.url');
|
||||||
|
|
||||||
var acct = o.account.replace(/[@]/g, '_');
|
var slug;
|
||||||
|
var account = o.account.replace(/[@]/g, '_');
|
||||||
var url = o.url.replace(/^https?:\/\//, '');
|
var url = o.url.replace(/^https?:\/\//, '');
|
||||||
var s = format('%s@%s', acct, url).replace(/[!#$%\^&\*:'"\?\/\\\.]/g, '_');
|
if (o.user) {
|
||||||
return s;
|
var user = o.user.replace(/[@]/g, '_');
|
||||||
|
slug = format('%s-%s@%s', user, account, url);
|
||||||
|
} else {
|
||||||
|
slug = format('%s@%s', account, url);
|
||||||
|
}
|
||||||
|
slug = slug.replace(/[!#$%\^&\*:'"\?\/\\\.]/g, '_');
|
||||||
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -819,6 +825,28 @@ function deepEqual(a, b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve "~/..." and "~" to an absolute path.
|
||||||
|
*
|
||||||
|
* Limitations: This does not handle "~user/...". This depends on the HOME
|
||||||
|
* envvar being defined. A better alternative is the "tilde-expansion"
|
||||||
|
* module (used elsewhere in node-triton), but that doesn't have a sync
|
||||||
|
* option.
|
||||||
|
*/
|
||||||
|
function tildeSync(s) {
|
||||||
|
var home = process.env.HOME;
|
||||||
|
if (!home) {
|
||||||
|
return s;
|
||||||
|
} else if (s === '~') {
|
||||||
|
return home;
|
||||||
|
} else if (s.slice(0, 2) === '~/') {
|
||||||
|
return home + s.slice(1);
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//---- exports
|
//---- exports
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -847,6 +875,7 @@ module.exports = {
|
|||||||
chomp: chomp,
|
chomp: chomp,
|
||||||
generatePassword: generatePassword,
|
generatePassword: generatePassword,
|
||||||
execPlus: execPlus,
|
execPlus: execPlus,
|
||||||
deepEqual: deepEqual
|
deepEqual: deepEqual,
|
||||||
|
tildeSync: tildeSync
|
||||||
};
|
};
|
||||||
// vim: set softtabstop=4 shiftwidth=4:
|
// vim: set softtabstop=4 shiftwidth=4:
|
||||||
|
@ -88,7 +88,8 @@ function loadConfig(opts) {
|
|||||||
assert.object(opts, 'opts');
|
assert.object(opts, 'opts');
|
||||||
assert.string(opts.configDir, 'opts.configDir');
|
assert.string(opts.configDir, 'opts.configDir');
|
||||||
|
|
||||||
var configPath = configPathFromDir(opts.configDir);
|
var configDir = common.tildeSync(opts.configDir);
|
||||||
|
var configPath = configPathFromDir(configDir);
|
||||||
|
|
||||||
var c = fs.readFileSync(DEFAULTS_PATH, 'utf8');
|
var c = fs.readFileSync(DEFAULTS_PATH, 'utf8');
|
||||||
var _defaults = JSON.parse(c);
|
var _defaults = JSON.parse(c);
|
||||||
@ -120,7 +121,7 @@ function loadConfig(opts) {
|
|||||||
config._user = _user;
|
config._user = _user;
|
||||||
}
|
}
|
||||||
config._defaults = _defaults;
|
config._defaults = _defaults;
|
||||||
config._configDir = opts.configDir;
|
config._configDir = configDir;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -272,15 +273,18 @@ function _profileFromPath(profilePath, name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function loadProfile(opts) {
|
function loadProfile(opts) {
|
||||||
assert.string(opts.configDir, 'opts.configDir');
|
|
||||||
assert.string(opts.name, 'opts.name');
|
assert.string(opts.name, 'opts.name');
|
||||||
|
assert.optionalString(opts.configDir, 'opts.configDir');
|
||||||
|
|
||||||
if (opts.name === 'env') {
|
if (opts.name === 'env') {
|
||||||
return _loadEnvProfile();
|
return _loadEnvProfile();
|
||||||
|
} else if (!opts.configDir) {
|
||||||
|
throw new errors.TritonError(
|
||||||
|
'cannot load profiles (other than "env") without `opts.configDir`');
|
||||||
} else {
|
} else {
|
||||||
var profilePath = path.resolve(opts.configDir, 'profiles.d',
|
var profilePath = path.resolve(
|
||||||
|
common.tildeSync(opts.configDir), 'profiles.d',
|
||||||
opts.name + '.json');
|
opts.name + '.json');
|
||||||
return _profileFromPath(profilePath, opts.name);
|
return _profileFromPath(profilePath, opts.name);
|
||||||
}
|
}
|
||||||
@ -294,7 +298,7 @@ function loadAllProfiles(opts) {
|
|||||||
_loadEnvProfile()
|
_loadEnvProfile()
|
||||||
];
|
];
|
||||||
|
|
||||||
var d = path.join(opts.configDir, 'profiles.d');
|
var d = path.join(common.tildeSync(opts.configDir), 'profiles.d');
|
||||||
var files = fs.readdirSync(d);
|
var files = fs.readdirSync(d);
|
||||||
files.forEach(function (file) {
|
files.forEach(function (file) {
|
||||||
file = path.join(d, file);
|
file = path.join(d, file);
|
||||||
|
155
lib/index.js
155
lib/index.js
@ -6,11 +6,160 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2015 Joyent, Inc.
|
* Copyright 2015 Joyent, Inc.
|
||||||
*
|
|
||||||
* module entry point
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
|
||||||
|
var bunyannoop = require('./bunyannoop');
|
||||||
|
var mod_config = require('./config');
|
||||||
|
var tritonapi = require('./tritonapi');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience wrapper around `tritonapi.createClient` to for simpler usage.
|
||||||
|
*
|
||||||
|
* Minimally this only requires that one of `profileName` or `profile` be
|
||||||
|
* specified. Examples:
|
||||||
|
*
|
||||||
|
* var triton = require('triton');
|
||||||
|
* var client = triton.createClient({
|
||||||
|
* profile: {
|
||||||
|
* url: "<cloudapi url>",
|
||||||
|
* account: "<account login for this cloud>",
|
||||||
|
* keyId: "<ssh key fingerprint for one of account's keys>"
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* --
|
||||||
|
* // Loading a profile from the environment (the `TRITON_*` and/or
|
||||||
|
* // `SDC_*` environment variables).
|
||||||
|
* var client = triton.createClient({profileName: 'env'});
|
||||||
|
* --
|
||||||
|
* var client = triton.createClient({
|
||||||
|
* configDir: '~/.triton', // use the CLI's config dir ...
|
||||||
|
* profileName: 'east1' // ... to find named profiles
|
||||||
|
* });
|
||||||
|
* --
|
||||||
|
* // The same thing using the underlying APIs.
|
||||||
|
* var client = triton.createClient({
|
||||||
|
* config: triton.loadConfig({configDir: '~/.triton'},
|
||||||
|
* profile: triton.loadProfile({name: 'east1', configDir: '~/.triton'})
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* A more complete example an app using triton internally might want:
|
||||||
|
*
|
||||||
|
* var triton = require('triton');
|
||||||
|
* var bunyan = require('bunyan');
|
||||||
|
*
|
||||||
|
* var appConfig = {
|
||||||
|
* // However the app handles its config.
|
||||||
|
* };
|
||||||
|
* var log = bunyan.createLogger({name: 'myapp', component: 'triton'});
|
||||||
|
* var client = triton.createClient({
|
||||||
|
* log: log,
|
||||||
|
* profile: appConfig.tritonProfile
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* TODO: The story for an app wanting to specify some Triton config but NOT
|
||||||
|
* have to have a triton $configDir/config.json is poor.
|
||||||
|
*
|
||||||
|
* @param opts {Object}:
|
||||||
|
* - @param profile {Object} A *Triton profile* object that includes the
|
||||||
|
* information required to connect to a CloudAPI -- minimally this:
|
||||||
|
* {
|
||||||
|
* "url": "<cloudapi url>",
|
||||||
|
* "account": "<account login for this cloud>",
|
||||||
|
* "keyId": "<ssh key fingerprint for one of account's keys>"
|
||||||
|
* }
|
||||||
|
* For example:
|
||||||
|
* {
|
||||||
|
* "url": "https://us-east-1.api.joyent.com",
|
||||||
|
* "account": "billy.bob",
|
||||||
|
* "keyId": "de:e7:73:9a:aa:91:bb:3e:72:8d:cc:62:ca:58:a2:ec"
|
||||||
|
* }
|
||||||
|
* Either `profile` or `profileName` is requires. See discussion above.
|
||||||
|
* - @param profileName {String} A Triton profile name. For any profile
|
||||||
|
* name other than "env", one must also provide either `configDir`
|
||||||
|
* or `config`.
|
||||||
|
* Either `profile` or `profileName` is requires. See discussion above.
|
||||||
|
* - @param configDir {String} A base config directory. This is used
|
||||||
|
* by TritonApi to find and store profiles, config, and cache data.
|
||||||
|
* For example, the `triton` CLI uses "~/.triton".
|
||||||
|
* One may not specify both `configDir` and `config`.
|
||||||
|
* - @param config {Object} A Triton config object loaded by
|
||||||
|
* `triton.loadConfig(...)`.
|
||||||
|
* One may not specify both `configDir` and `config`.
|
||||||
|
* - @param log {Bunyan Logger} Optional. A Bunyan logger. If not provided,
|
||||||
|
* a stub that does no logging will be used.
|
||||||
|
*/
|
||||||
|
function createClient(opts) {
|
||||||
|
assert.object(opts, 'opts');
|
||||||
|
assert.optionalObject(opts.profile, 'opts.profile');
|
||||||
|
assert.optionalString(opts.profileName, 'opts.profileName');
|
||||||
|
assert.optionalObject(opts.config, 'opts.config');
|
||||||
|
assert.optionalString(opts.configDir, 'opts.configDir');
|
||||||
|
assert.optionalObject(opts.log, 'opts.log');
|
||||||
|
|
||||||
|
assert.ok(!(opts.profile && opts.profileName),
|
||||||
|
'cannot specify both opts.profile and opts.profileName');
|
||||||
|
assert.ok(!(!opts.profile && !opts.profileName),
|
||||||
|
'must specify one opts.profile or opts.profileName');
|
||||||
|
assert.ok(!(opts.config && opts.configDir),
|
||||||
|
'cannot specify both opts.config and opts.configDir');
|
||||||
|
assert.ok(!(opts.config && opts.configDir),
|
||||||
|
'cannot specify both opts.config and opts.configDir');
|
||||||
|
if (opts.profileName && opts.profileName !== 'env') {
|
||||||
|
assert.ok(opts.configDir,
|
||||||
|
'must provide opts.configDir for opts.profileName!="env"');
|
||||||
|
}
|
||||||
|
|
||||||
|
var log = opts.log;
|
||||||
|
if (!opts.log) {
|
||||||
|
log = new bunyannoop.BunyanNoopLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = opts.config;
|
||||||
|
if (!config) {
|
||||||
|
config = mod_config.loadConfig({configDir: opts.configDir});
|
||||||
|
}
|
||||||
|
|
||||||
|
var profile = opts.profile;
|
||||||
|
if (!profile) {
|
||||||
|
profile = mod_config.loadProfile({
|
||||||
|
name: opts.profileName,
|
||||||
|
configDir: config._configDir
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Don't require one to arbitrarily have a profile.name if manually
|
||||||
|
// creating it.
|
||||||
|
if (!profile.name) {
|
||||||
|
// TODO: might want this to be hash or slug of profile params.
|
||||||
|
profile.name = '_';
|
||||||
|
}
|
||||||
|
mod_config.validateProfile(profile);
|
||||||
|
|
||||||
|
var client = tritonapi.createClient({
|
||||||
|
log: log,
|
||||||
|
config: config,
|
||||||
|
profile: profile
|
||||||
|
});
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createTritonApiClient: require('./tritonapi').createClient,
|
createClient: createClient,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `createClient` provides convenience parameters to not *have* to call
|
||||||
|
* the following (i.e. passing in `configDir` and/or `profileName`), but
|
||||||
|
* some users of node-triton as a module may want to call these directly.
|
||||||
|
*/
|
||||||
|
loadConfig: mod_config.loadConfig,
|
||||||
|
loadProfile: mod_config.loadProfile,
|
||||||
|
loadAllProfiles: mod_config.loadAllProfiles,
|
||||||
|
|
||||||
|
createTritonApiClient: tritonapi.createClient,
|
||||||
|
// For those wanting a lower-level raw CloudAPI client.
|
||||||
createCloudApiClient: require('./cloudapi2').createClient
|
createCloudApiClient: require('./cloudapi2').createClient
|
||||||
};
|
};
|
||||||
|
@ -92,7 +92,7 @@ function TritonApi(opts) {
|
|||||||
this.log = opts.log;
|
this.log = opts.log;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.cacheDir) {
|
if (this.config._configDir) {
|
||||||
this.cacheDir = path.resolve(this.config._configDir,
|
this.cacheDir = path.resolve(this.config._configDir,
|
||||||
this.config.cacheDir,
|
this.config.cacheDir,
|
||||||
common.profileSlug(this.profile));
|
common.profileSlug(this.profile));
|
||||||
@ -111,6 +111,11 @@ function TritonApi(opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TritonApi.prototype.close = function close() {
|
||||||
|
this.cloudapi.close();
|
||||||
|
delete this.cloudapi;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
TritonApi.prototype._cloudapiFromProfile =
|
TritonApi.prototype._cloudapiFromProfile =
|
||||||
function _cloudapiFromProfile(profile)
|
function _cloudapiFromProfile(profile)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "triton",
|
"name": "triton",
|
||||||
"description": "Joyent Triton CLI and client (https://www.joyent.com/triton)",
|
"description": "Joyent Triton CLI and client (https://www.joyent.com/triton)",
|
||||||
"version": "3.3.1",
|
"version": "3.4.0",
|
||||||
"author": "Joyent (joyent.com)",
|
"author": "Joyent (joyent.com)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert-plus": "0.2.0",
|
"assert-plus": "0.2.0",
|
||||||
|
99
test/integration/api-images.test.js
Normal file
99
test/integration/api-images.test.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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 using image-related APIs as a module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var h = require('./helpers');
|
||||||
|
var test = require('tape');
|
||||||
|
|
||||||
|
var common = require('../../lib/common');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- Globals
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- Tests
|
||||||
|
|
||||||
|
test('TritonApi images', function (tt) {
|
||||||
|
|
||||||
|
var client;
|
||||||
|
tt.test(' setup: client', function (t) {
|
||||||
|
client = h.createClient();
|
||||||
|
t.ok(client, 'client');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
var testOpts = {};
|
||||||
|
var img;
|
||||||
|
tt.test(' TritonApi listImages', function (t) {
|
||||||
|
client.listImages(function (err, images) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
t.ok(images, 'images');
|
||||||
|
t.ok(Array.isArray(images), 'images');
|
||||||
|
if (images.length) {
|
||||||
|
img = images[0];
|
||||||
|
t.ok(img, 'img');
|
||||||
|
t.ok(common.isUUID(img.id), 'img.id is a UUID');
|
||||||
|
t.ok(img.name, 'img.name');
|
||||||
|
t.ok(img.version, 'img.version');
|
||||||
|
} else {
|
||||||
|
testOpts.skip = true;
|
||||||
|
}
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' TritonApi getImage by uuid', testOpts, function (t) {
|
||||||
|
client.getImage(img.id, function (err, image) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
t.equal(image.id, img.id);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' TritonApi getImage by name', testOpts, function (t) {
|
||||||
|
client.getImage(img.name, function (err, image) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
t.equal(image.name, img.name); // might not be the same ID
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' TritonApi getImage by name (opts)', testOpts, function (t) {
|
||||||
|
client.getImage({name: img.name}, function (err, image) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
t.equal(image.name, img.name); // might not be the same ID
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' TritonApi getImage by shortId', testOpts, function (t) {
|
||||||
|
var shortId = img.id.split('-')[0];
|
||||||
|
client.getImage(shortId, function (err, image) {
|
||||||
|
if (h.ifErr(t, err))
|
||||||
|
return t.end();
|
||||||
|
t.equal(image.id, img.id);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tt.test(' teardown: client', function (t) {
|
||||||
|
client.close();
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
@ -18,7 +18,7 @@ var f = require('util').format;
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
var common = require('../../lib/common');
|
var common = require('../../lib/common');
|
||||||
var mod_config = require('../../lib/config');
|
var mod_triton = require('../../');
|
||||||
var testcommon = require('../lib/testcommon');
|
var testcommon = require('../lib/testcommon');
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ try {
|
|||||||
assert.optionalBool(CONFIG.profile.insecure,
|
assert.optionalBool(CONFIG.profile.insecure,
|
||||||
'CONFIG.profile.insecure');
|
'CONFIG.profile.insecure');
|
||||||
} else if (CONFIG.profileName) {
|
} else if (CONFIG.profileName) {
|
||||||
CONFIG.profile = mod_config.loadProfile({
|
CONFIG.profile = mod_triton.loadProfile({
|
||||||
configDir: path.join(process.env.HOME, '.triton'),
|
configDir: path.join(process.env.HOME, '.triton'),
|
||||||
name: CONFIG.profileName
|
name: CONFIG.profileName
|
||||||
});
|
});
|
||||||
@ -160,11 +160,24 @@ function safeTriton(t, opts, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a TritonApi client using the CLI.
|
||||||
|
*/
|
||||||
|
function createClient() {
|
||||||
|
return mod_triton.createClient({
|
||||||
|
log: LOG,
|
||||||
|
profile: CONFIG.profile,
|
||||||
|
configDir: '~/.triton' // piggy-back on Triton CLI config dir
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- exports
|
// --- exports
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
CONFIG: CONFIG,
|
CONFIG: CONFIG,
|
||||||
triton: triton,
|
triton: triton,
|
||||||
safeTriton: safeTriton,
|
safeTriton: safeTriton,
|
||||||
|
createClient: createClient,
|
||||||
ifErr: testcommon.ifErr
|
ifErr: testcommon.ifErr
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user