config, cache images
This commit is contained in:
parent
d6ac9fed33
commit
a5213658fa
20
lib/cli.js
20
lib/cli.js
@ -14,6 +14,7 @@ var cmdln = require('cmdln'),
|
||||
var fs = require('fs');
|
||||
var util = require('util'),
|
||||
format = util.format;
|
||||
var path = require('path');
|
||||
var vasync = require('vasync');
|
||||
|
||||
var common = require('./common');
|
||||
@ -99,7 +100,24 @@ CLI.prototype.init = function (opts, args, callback) {
|
||||
|
||||
this.__defineGetter__('triton', function () {
|
||||
if (self._triton === undefined) {
|
||||
self._triton = new Triton({log: log, profile: opts.profile});
|
||||
var userConfigPath = require('./config').DEFAULT_USER_CONFIG_PATH;
|
||||
var dir = path.dirname(userConfigPath);
|
||||
var cacheDir = path.join(dir, 'cache');
|
||||
|
||||
[dir, cacheDir].forEach(function (d) {
|
||||
try {
|
||||
fs.mkdirSync(d);
|
||||
} catch (e) {
|
||||
log.info({err: e}, 'failed to make dir %s', d);
|
||||
}
|
||||
});
|
||||
|
||||
self._triton = new Triton({
|
||||
log: log,
|
||||
profile: opts.profile,
|
||||
config: userConfigPath,
|
||||
cachedir: cacheDir
|
||||
});
|
||||
}
|
||||
return self._triton;
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ var common = require('./common');
|
||||
var errors = require('./errors');
|
||||
|
||||
|
||||
var CONFIG_PATH = path.resolve(process.env.HOME, '.triton', 'config.json');
|
||||
var DEFAULT_USER_CONFIG_PATH = path.resolve(process.env.HOME, '.triton', 'config.json');
|
||||
var DEFAULTS_PATH = path.resolve(__dirname, '..', 'etc', 'defaults.json');
|
||||
var OVERRIDE_KEYS = []; // config object keys to do a one-level deep override
|
||||
|
||||
@ -24,17 +24,17 @@ var OVERRIDE_KEYS = []; // config object keys to do a one-level deep override
|
||||
*
|
||||
* This includes some internal data on keys with a leading underscore.
|
||||
*/
|
||||
function loadConfigSync() {
|
||||
function loadConfigSync(configPath) {
|
||||
var c = fs.readFileSync(DEFAULTS_PATH, 'utf8');
|
||||
var _defaults = JSON.parse(c);
|
||||
var config = JSON.parse(c);
|
||||
if (fs.existsSync(CONFIG_PATH)) {
|
||||
c = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
if (configPath && fs.existsSync(configPath)) {
|
||||
c = fs.readFileSync(configPath, 'utf8');
|
||||
var _user = JSON.parse(c);
|
||||
var userConfig = JSON.parse(c);
|
||||
if (typeof(userConfig) !== 'object' || Array.isArray(userConfig)) {
|
||||
throw new errors.ConfigError(
|
||||
sprintf('"%s" is not an object', CONFIG_PATH));
|
||||
sprintf('"%s" is not an object', configPath));
|
||||
}
|
||||
// These special keys are merged into the key of the same name in the
|
||||
// base "defaults.json".
|
||||
@ -88,7 +88,7 @@ function updateUserConfigSync(config, updates) {
|
||||
//---- exports
|
||||
|
||||
module.exports = {
|
||||
CONFIG_PATH: CONFIG_PATH,
|
||||
DEFAULT_USER_CONFIG_PATH: DEFAULT_USER_CONFIG_PATH,
|
||||
loadConfigSync: loadConfigSync
|
||||
};
|
||||
// vim: set softtabstop=4 shiftwidth=4:
|
||||
|
@ -41,7 +41,7 @@ function do_images(subcmd, opts, args, callback) {
|
||||
listOpts.state = 'all';
|
||||
}
|
||||
|
||||
this.triton.cloudapi.listImages(listOpts, function onRes(err, imgs, res) {
|
||||
this.triton.listImages(listOpts, function onRes(err, imgs, res) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
* `triton instances ...`
|
||||
*/
|
||||
|
||||
var f = require('util').format;
|
||||
|
||||
var tabula = require('tabula');
|
||||
|
||||
var common = require('./common');
|
||||
@ -33,6 +35,7 @@ var validFields = [
|
||||
'updated',
|
||||
'package',
|
||||
'image',
|
||||
'img',
|
||||
'ago'
|
||||
];
|
||||
|
||||
@ -56,17 +59,46 @@ function do_instances(subcmd, opts, args, callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.triton.cloudapi.listMachines(listOpts, function (err, machines) {
|
||||
var i = 0;
|
||||
|
||||
i++;
|
||||
var images;
|
||||
this.triton.listImages({usecache: true}, function (err, _images) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
images = _images;
|
||||
done();
|
||||
});
|
||||
|
||||
i++;
|
||||
var machines;
|
||||
this.triton.cloudapi.listMachines(listOpts, function (err, _machines) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
machines = _machines;
|
||||
done();
|
||||
});
|
||||
|
||||
function done() {
|
||||
if (--i > 0)
|
||||
return;
|
||||
|
||||
// map "uuid" => "image_name"
|
||||
var imgmap = {};
|
||||
images.forEach(function (image) {
|
||||
imgmap[image.id] = f('%s@%s', image.name, image.version);
|
||||
});
|
||||
|
||||
// add extra fields for nice output
|
||||
var now = new Date();
|
||||
machines.forEach(function (machine) {
|
||||
var created = new Date(machine.created);
|
||||
machine.ago = common.longAgo(created, now);
|
||||
machine.img = imgmap[machine.image] || machine.image;
|
||||
});
|
||||
|
||||
if (opts.json) {
|
||||
@ -80,7 +112,7 @@ function do_instances(subcmd, opts, args, callback) {
|
||||
});
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
do_instances.options = [
|
||||
@ -97,7 +129,7 @@ do_instances.options = [
|
||||
{
|
||||
names: ['o'],
|
||||
type: 'string',
|
||||
default: 'id,name,state,type,image,memory,disk,ago',
|
||||
default: 'id,name,state,type,img,memory,disk,ago',
|
||||
help: 'Specify fields (columns) to output.',
|
||||
helpArg: 'field1,...'
|
||||
},
|
||||
|
@ -36,6 +36,8 @@ function Triton(options) {
|
||||
assert.object(options, 'options');
|
||||
assert.object(options.log, 'options.log');
|
||||
assert.optionalString(options.profile, 'options.profile');
|
||||
assert.optionalString(options.config, 'options.config');
|
||||
assert.optionalString(options.cachedir, 'options.cachedir');
|
||||
|
||||
// Make sure a given bunyan logger has reasonable client_re[qs] serializers.
|
||||
// Note: This was fixed in restify, then broken again in
|
||||
@ -50,11 +52,12 @@ function Triton(options) {
|
||||
} else {
|
||||
this.log = options.log;
|
||||
}
|
||||
this.config = loadConfigSync();
|
||||
this.config = loadConfigSync(options.config);
|
||||
this.profiles = this.config.profiles;
|
||||
this.profile = this.getProfile(
|
||||
options.profile || this.config.defaultProfile);
|
||||
this.log.trace({profile: this.profile}, 'profile data');
|
||||
this.cachedir = options.cachedir;
|
||||
|
||||
this.cloudapi = this._cloudapiFromProfile(this.profile);
|
||||
}
|
||||
@ -104,6 +107,72 @@ Triton.prototype._cloudapiFromProfile = function _cloudapiFromProfile(profile) {
|
||||
return client;
|
||||
};
|
||||
|
||||
/**
|
||||
* cloudapi listImages wrapper with optional caching
|
||||
*/
|
||||
Triton.prototype.listImages = function listImages(opts, cb) {
|
||||
var self = this;
|
||||
if (cb === undefined) {
|
||||
cb = opts;
|
||||
opts = {};
|
||||
}
|
||||
assert.object(opts, 'opts');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
var cachefile;
|
||||
if (self.cachedir)
|
||||
cachefile = path.join(self.cachedir, 'images.json');
|
||||
|
||||
if (opts.usecache && !cachefile) {
|
||||
cb(new Error('opts.usecache set but no cachedir found'));
|
||||
return;
|
||||
}
|
||||
|
||||
// try to read the cache if the user wants it
|
||||
// if this fails for any reason we fallback to hitting the cloudapi
|
||||
if (opts.usecache) {
|
||||
fs.readFile(cachefile, 'utf8', function (err, out) {
|
||||
if (err) {
|
||||
self.log.info({err: err}, 'failed to read cache file %s', cachefile);
|
||||
fetch();
|
||||
return;
|
||||
}
|
||||
var data;
|
||||
try {
|
||||
data = JSON.parse(out);
|
||||
} catch (e) {
|
||||
self.log.info({err: e}, 'failed to parse cache file %s', cachefile);
|
||||
fetch();
|
||||
return;
|
||||
}
|
||||
|
||||
cb(null, data, {});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fetch();
|
||||
function fetch() {
|
||||
self.cloudapi.listImages(function (err, imgs, res) {
|
||||
if (!err && self.cachedir) {
|
||||
// cache the results
|
||||
var data = JSON.stringify(imgs);
|
||||
fs.writeFile(cachefile, data, {encoding: 'utf8'}, function (err) {
|
||||
if (err)
|
||||
self.log.info({err: err}, 'error caching images results');
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
|
||||
|
||||
function done() {
|
||||
cb(err, imgs, res);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an image by ID, exact name, or short ID, in that order.
|
||||
|
Reference in New Issue
Block a user