@yawetse/pkgcloud
Version:
An infrastructure-as-a-service agnostic cloud library for node.js
390 lines (338 loc) • 12.4 kB
JavaScript
/*
* instances.js: Instance methods for working with database instances from Openstack Trove
*
* (C) 2014 Hewlett-Packard Development Company, L.P.
*
*/
var pkgcloud = require('../../../../../lib/pkgcloud'),
Flavor = pkgcloud.providers.rackspace.database.Flavor,
Instance = pkgcloud.providers.rackspace.database.Instance,
errs = require('errs'),
qs = require('querystring');
// Create Database Instance
// Need a flavor
// ### @options {Object} Set of options can be
// #### options['name'] {string} Name of instance (required)
// #### options['flavor'] {string | Object} Should be the HREF for the flavor or a instance of Flavor class (required)
// #### options['size'] {number} The Volume size in Gigabytes, must be between 1 and 8
// #### options['databases'] {array} Array of strings with database names to create when the instance is ready.
// ### @callback {Function} Function to continue the call is cb(error, Instance)
exports.createInstance = function createInstance(options, callback) {
var self = this,
flavorRef,
size;
// Check for options
if (!options || typeof options === 'function') {
return errs.handle(errs.create({
message: 'Options required for create an instance.'
}), options);
}
if (!options['name']) {
return errs.handle(errs.create({
message: 'options. name is a required argument'
}), callback);
}
if (!options['flavor']) {
return errs.handle(errs.create({
message: 'options. flavor is a required argument'
}), callback);
}
// If the 'databases' are specified we create a template for each database name.
if (options && options['databases'] &&
Array.isArray(options['databases']) &&
options['databases'].length > 0) {
options['databases'].forEach(function (item, idx) {
if (typeof item === 'string') {
// This template is according to the defaults of rackspace.
options['databases'][idx] = {
name: item,
character_set: 'utf8',
collate: 'utf8_general_ci'
};
}
});
}
// Check for the correct value of 'size', should be between 1 and 8 otherwise will be 1
if (options && options['size']) {
// Ensure size is an Integer
if (typeof options['size'] !== 'number') {
return errs.handle(errs.create({
message: 'options. Volume size should be a Number, not a String'
}), callback);
}
size = (options['size'] > 0 && options['size'] < 9) ? options['size'] : 1;
}
// Extract the href value of the Flavor instance
// Should be always true because above we return an error if not exists
if (options && options['flavor']) {
flavorRef = options['flavor'] instanceof Flavor ? options['flavor'].href : options['flavor'];
}
var createOptions = {
method: 'POST',
path: 'instances',
body: {
instance: {
name: options['name'],
flavorRef: flavorRef,
databases: options['databases'] || [],
volume: { size: size || 1 }
}
}
};
this._request(createOptions, function (err, body) {
return err
? callback(err)
: callback(null, new Instance(self, body.instance));
});
};
// Gets all instances info
// ### @options {Object} Set of options can be
// #### options['limit'] {Integer} Number of results you want
// #### options['offset'] {Integer} Offset mark for result list
// ### @callback {Function} Function to continue the call is cb(error, instances, offset)
exports.getInstances = function getInstances(options, callback) {
var self = this,
completeUrl = {},
requestOptions = {};
if (typeof options === 'function') {
callback = options;
options = {};
}
// The limit parameter for truncate results
if (options && options.limit) {
completeUrl.limit = options.limit;
}
// The offset
if (options && options.offset) {
completeUrl.marker = options.offset;
}
requestOptions.qs = completeUrl;
requestOptions.path = 'instances';
this._request(requestOptions, function (err, body) {
if (err) {
return callback(err);
}
var marker = null;
if (body.links && body.links.length > 0) {
marker = qs.parse(body.links[0].href.split('?').pop()).marker;
}
callback(null, body.instances.map(function (result) {
return new Instance(self, result);
}), marker);
});
};
// Destroying the database instance
// ### @instance {string | Object} The ID of the istance of a instance of Instance class (required)
// ### @callback {Function} Function to continue the call is cb(error, res)
exports.destroyInstance = function destroyInstance(instance, callback) {
// Check for instance
if (typeof instance === 'function') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), instance);
}
var instanceId = instance instanceof Instance ? instance.id : instance;
this._request({
method: 'DELETE',
path: 'instances/' + instanceId
}, function (err, body, response) {
return err
? callback(err)
: callback(null, response);
});
};
// Details of specific instance
// ### @instance {string | Object} The ID of the istance of a instance of Instance class (required)
// ### @callback {Function} Function to continue the call is cb(error, instances, offset)
exports.getInstance = function getInstance(instance, callback) {
// Check for instance
if (typeof instance === 'function') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), instance);
}
var self = this;
var instanceId = instance instanceof Instance ? instance.id : instance;
this._request({
path: 'instances/' + instanceId
}, function (err, body) {
return err
? callback(err)
: callback(null, new Instance(self, body.instance));
});
};
// Restart the Instance
// Call this function cause a restart in the instance specified
// ### @instance {string | Object} The ID of the istance of a instance of Instance class (required)
// ### @callback {Function} Function to continue the call is cb(error)
exports.restartInstance = function restartInstance(instance, callback) {
// Check for instance
if (typeof instance === 'function' || typeof instance === 'undefined') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), Array.prototype.slice.call(arguments).pop());
}
var instanceId = instance instanceof Instance ? instance.id : instance;
var restartOptions = {
method: 'POST',
path: 'instances/' + instanceId + '/action',
body: { restart: {} }
};
this._request(restartOptions, function (err, body, response) {
if (err) {
return callback(err);
}
if (response.statusCode === 202) {
return callback(null);
}
errs.handle(errs.create({
message: 'Bad response from restart action.'
}), callback);
});
};
// Resize the memory of the database instance.
// You can use this to change the flavor of the database instance, need a new flavor.
// ### @instance {string | Object} The ID of the istance of a instance of Instance class (required)
// ### @flavor {Flavor class} The flavor to resize the instance, should be different (required)
// ### @callback {Function} Function to continue, no params are passed.
exports.setFlavor = function setFlavor(instance, flavor, callback) {
// Check for the flavor
if (typeof flavor === 'function' || typeof flavor === 'undefined') {
return errs.handle(errs.create({
message: 'A flavor is required.'
}), Array.prototype.slice.call(arguments).pop());
}
// Check for instance
if (typeof instance === 'function' || typeof instance === 'undefined') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), Array.prototype.slice.call(arguments).pop());
}
if (!(flavor instanceof Flavor)) {
return errs.handle(errs.create({
message: 'A valid flavor is required.'
}), Array.prototype.slice.call(arguments).pop());
}
//@todo: Check if the new flavor are different from the old.
var instanceId = instance instanceof Instance ? instance.id : instance;
var resizeOptions = {
method: 'POST',
path: 'instances/' + instanceId + '/action',
body: {
resize: {
flavorRef: flavor.href
}
}
};
this._request(resizeOptions, function (err, body, response) {
if (err) {
return callback(err);
}
if (response.statusCode === 202) {
return callback(null);
}
return errs.handle(errs.create({
message: 'Bad response from resize action.'
}), callback);
});
};
// Resize the volume size of the database instance.
// You can use this to change the size of the volume for a database instance.
// ### @instance {string | Object} The ID of the istance of a instance of Instance class (required)
// ### @newSize {Number} The new size for the volume (require)
// ### @callback {Function} Function to continue, no params are passed.
exports.setVolumeSize = function setVolumeSize(instance, newSize, callback) {
// Check for instance
if (typeof instance === 'function' || typeof instance === 'undefined') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), Array.prototype.slice.call(arguments).pop());
}
// Check for the volume size
if (typeof newSize === 'function' || typeof newSize === 'undefined') {
return errs.handle(errs.create({
message: 'An correct volume size is required.'
}), Array.prototype.slice.call(arguments).pop());
}
if (newSize > 10 || newSize < 1) {
return errs.handle(errs.create({
message: 'An correct volume size is required.'
}), Array.prototype.slice.call(arguments).pop());
}
var instanceId = instance instanceof Instance ? instance.id : instance;
var resizeOptions = {
method: 'POST',
path: 'instances/' + instanceId + '/action',
body: {
resize: {
volume: { size: newSize }
}
}
};
this._request(resizeOptions, function (err, body, response) {
if (err) {
return callback(err);
}
if (response.statusCode === 202) {
return callback(null);
}
return errs.handle(errs.create({
message: 'Bad response from resize action.'
}), callback);
});
};
// Enable the root user for the given database instance.
// ### @instance {string | Object} The ID of the instance or a instance or Instance class (required)
// ### @callback {Function} Function to continue, the call is cb(error, res)
exports.enableRootUser = function enableRootUser(instance, callback) {
// Check for instance
if (typeof instance === 'function' || typeof instance === 'undefined') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), Array.prototype.slice.call(arguments).pop());
}
var instanceId = instance instanceof Instance ? instance.id : instance;
var options = {
method: 'POST',
path: 'instances/' + instanceId + '/root'
};
this._request(options, function (err, body, response) {
if (err) {
return callback(err);
}
if (response.statusCode === 200) {
return callback(null, body);
}
return errs.handle(errs.create({
message: 'Bad response from enabling root user'
}), callback);
});
};
// List the root status for the database instance.
// `res` will be an object like `{ rootEnabled: true }`
// ### @instance {string | Object} The ID of the instance or a instance or Instance class (required)
// ### @callback {Function} Function to continue, the call is cb(error, res)
exports.listRootStatus = function listRootStatus(instance, callback) {
// Check for instance
if (typeof instance === 'function' || typeof instance === 'undefined') {
return errs.handle(errs.create({
message: 'An instance is required.'
}), Array.prototype.slice.call(arguments).pop());
}
var instanceId = instance instanceof Instance ? instance.id : instance;
var options = {
method: 'GET',
path: 'instances/' + instanceId + '/root'
};
this._request(options, function (err, body, response) {
if (err) {
return callback(err);
}
if (response.statusCode === 200) {
return callback(null, body);
}
return errs.handle(errs.create({
message: 'Bad response from listing root-enabled status'
}), callback);
});
};