This repository has been archived on 2020-01-20. You can view files and clone it, but cannot push or open issues or pull requests.
node-spearhead/lib/triton.js

237 lines
7.1 KiB
JavaScript
Raw Normal View History

2014-02-07 23:21:24 +02:00
/*
* Copyright (c) 2015, Joyent, Inc. All rights reserved.
2014-02-07 23:21:24 +02:00
*
* Core Triton client driver class.
2014-02-07 23:21:24 +02:00
*/
var p = console.log;
var assert = require('assert-plus');
var auth = require('smartdc-auth');
2014-02-08 10:15:26 +02:00
var EventEmitter = require('events').EventEmitter;
2014-02-07 23:21:24 +02:00
var fs = require('fs');
2015-07-26 08:45:20 +03:00
var once = require('once');
2014-02-07 23:21:24 +02:00
var path = require('path');
var restifyClients = require('restify-clients');
2015-07-26 08:45:20 +03:00
var sprintf = require('util').format;
2015-08-26 06:53:48 +03:00
var tabula = require('tabula');
2014-02-07 23:21:24 +02:00
var cloudapi = require('./cloudapi2');
2014-02-07 23:21:24 +02:00
var common = require('./common');
2015-07-26 08:45:20 +03:00
var errors = require('./errors');
2014-02-07 23:21:24 +02:00
var loadConfigSync = require('./config').loadConfigSync;
//---- Triton class
2014-02-07 23:21:24 +02:00
/**
* Create a Triton client.
2014-02-07 23:21:24 +02:00
*
* @param options {Object}
* - log {Bunyan Logger}
* - profile {String} Optional. Name of profile to use. Defaults to
* 'defaultProfile' in the config.
*/
function Triton(options) {
2014-02-07 23:21:24 +02:00
assert.object(options, 'options');
assert.object(options.log, 'options.log');
assert.optionalString(options.profile, 'options.profile');
// Make sure a given bunyan logger has reasonable client_re[qs] serializers.
// Note: This was fixed in restify, then broken again in
// https://github.com/mcavage/node-restify/pull/501
if (options.log.serializers &&
(!options.log.serializers.client_req ||
!options.log.serializers.client_req)) {
this.log = options.log.child({
// XXX cheating. restify-clients should export its 'bunyan'.
serializers: require('restify-clients/lib/helpers/bunyan').serializers
});
} else {
this.log = options.log;
}
2014-02-07 23:21:24 +02:00
this.config = loadConfigSync();
this.profiles = this.config.profiles;
this.profile = this.getProfile(
options.profile || this.config.defaultProfile);
this.log.trace({profile: this.profile}, 'profile data');
this.cloudapi = this._cloudapiFromProfile(this.profile);
2014-02-07 23:21:24 +02:00
}
Triton.prototype.getProfile = function getProfile(name) {
2014-02-07 23:21:24 +02:00
for (var i = 0; i < this.profiles.length; i++) {
if (this.profiles[i].name === name) {
return this.profiles[i];
}
}
};
Triton.prototype._cloudapiFromProfile = function _cloudapiFromProfile(profile) {
assert.object(profile, 'profile');
assert.string(profile.account, 'profile.account');
assert.string(profile.keyId, 'profile.keyId');
assert.string(profile.url, 'profile.url');
assert.optionalString(profile.privKey, 'profile.privKey');
assert.optionalBool(profile.insecure, 'profile.insecure');
var rejectUnauthorized = (profile.insecure === undefined
? true : !profile.insecure);
var sign;
if (profile.privKey) {
sign = auth.privateKeySigner({
user: profile.account,
keyId: profile.keyId,
key: profile.privKey
2014-02-07 23:21:24 +02:00
});
} else {
sign = auth.cliSigner({
keyId: profile.keyId,
user: profile.account
2015-07-26 08:45:20 +03:00
});
}
var client = cloudapi.createClient({
url: profile.url,
user: profile.account,
version: '*',
rejectUnauthorized: rejectUnauthorized,
sign: sign,
log: this.log
2015-07-26 08:45:20 +03:00
});
return client;
2015-07-26 08:45:20 +03:00
};
/**
* Get an image by ID, exact name, or short ID, in that order.
*
* If there is more than one image with that name, then the latest
* (by published_at) is returned.
2015-07-26 08:45:20 +03:00
*/
2015-08-26 06:53:48 +03:00
Triton.prototype.getImage = function getImage(name, cb) {
assert.string(name, 'name');
assert.func(cb, 'cb');
if (common.UUID_RE.test(name)) {
this.cloudapi.getImage({id: name}, function (err, img) {
if (err) {
cb(err);
} else if (img.state !== 'active') {
cb(new Error(format('image %s is not active', name)));
} else {
cb(null, img);
}
});
} else {
this.cloudapi.listImages(function (err, imgs) {
if (err) {
return cb(err);
}
var nameMatches = [];
var shortIdMatches = [];
2015-08-26 06:53:48 +03:00
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
if (img.name === name) {
nameMatches.push(img);
}
if (img.id.slice(0, 8) === name) {
shortIdMatches.push(img);
2015-07-26 08:45:20 +03:00
}
2015-08-26 06:53:48 +03:00
}
if (nameMatches.length === 1) {
2015-08-26 06:53:48 +03:00
cb(null, nameMatches[0]);
} else if (nameMatches.length > 1) {
2015-08-26 06:53:48 +03:00
tabula.sortArrayOfObjects(nameMatches, 'published_at');
cb(null, nameMatches[nameMatches.length - 1]);
} else if (shortIdMatches.length === 1) {
cb(null, shortIdMatches[0]);
} else if (shortIdMatches.length === 0) {
cb(new Error(format(
'no image with name or shortId "%s" was found', name)));
} else {
cb(new Error(format('no image with name "%s" was found '
+ 'and "%s" is an ambiguous shortId', name)));
2015-07-26 08:45:20 +03:00
}
2015-08-26 06:53:48 +03:00
});
2014-02-07 23:21:24 +02:00
}
};
2015-07-26 08:45:20 +03:00
/**
2015-08-26 06:53:48 +03:00
* Get an active package by ID or name. If there is more than one package
* with that name, then this errors out.
2015-07-26 08:45:20 +03:00
*/
2015-08-26 06:53:48 +03:00
Triton.prototype.getPackage = function getPackage(name, cb) {
assert.string(name, 'name');
assert.func(cb, 'cb');
if (common.UUID_RE.test(name)) {
this.cloudapi.getPackage({id: name}, function (err, pkg) {
if (err) {
cb(err);
} else if (!pkg.active) {
cb(new Error(format('image %s is not active', name)));
} else {
cb(null, pkg);
}
2015-07-26 08:45:20 +03:00
});
2015-08-26 06:53:48 +03:00
} else {
this.cloudapi.listPackages(function (err, pkgs) {
if (err) {
return cb(err);
}
var nameMatches = [];
for (var i = 0; i < pkgs.length; i++) {
if (pkgs[i].name === name) {
nameMatches.push(pkgs[i]);
}
}
if (nameMatches.length === 0) {
cb(new Error(format('no package with name=%s was found',
name)));
} else if (nameMatches.length === 1) {
cb(null, nameMatches[0]);
} else {
cb(new Error(format(
'package name "%s" is ambiguous: matches %d packages',
name, nameMatches.length)));
}
});
}
2015-07-26 08:45:20 +03:00
};
2015-08-26 03:27:46 +03:00
/**
* getMachine for an alias
*
* @param {String} alias - the machine alias
* @param {Function} callback `function (err, machine)`
*/
Triton.prototype.getMachineByAlias = function getMachineByAlias(alias, callback) {
this.cloudapi.listMachines({name: alias}, function (err, machines) {
if (err) {
callback(err);
return;
}
var found = false;
machines.forEach(function (machine) {
if (!found && machine.name === alias) {
callback(null, machine);
found = true;
}
});
if (!found) {
callback(new Error('machine ' + alias + ' not found'));
return;
}
});
};
2014-02-07 23:21:24 +02:00
//---- exports
module.exports = Triton;