@yawetse/pkgcloud
Version:
An infrastructure-as-a-service agnostic cloud library for node.js
429 lines (369 loc) • 12.4 kB
JavaScript
/*
* cdn-containers.js: Instance methods for working with containers from Rackspace Cloudfiles
*
* (C) 2011 Charlie Robbins, Ken Perkins, Ross Kukulinski & the Contributors.
*
*/
var async = require('async'),
crypto = require('crypto'),
_ = require('underscore');
/**
* client.getFiles
*
* @description get the list of containers for an account
*
* @param {object|Function} options
* @param {Number} [options.limit] the number of records to return
* @param {String} [options.marker] the id of the first record to return in the current query
* @param {Function} callback
*/
exports.getContainers = function (options, callback) {
var self = this;
if (typeof options === 'function') {
callback = options;
options = {};
}
var getContainerOpts = {
path: '',
qs: _.extend({
format: 'json'
}, _.pick(options, ['limit', 'marker', 'end_marker']))
};
this._request(getContainerOpts, function (err, body) {
if (err) {
return callback(err);
}
else if (!body || !(body instanceof Array)) {
return new Error('Malformed API Response');
}
if (!options.loadCDNAttributes) {
return callback(null, body.map(function (container) {
return new self.models.Container(self, container);
}));
}
else {
var containers = [];
async.forEachLimit(body, 10, function (c, next) {
var container = new self.models.Container(self, c);
containers.push(container);
container.refreshCdnDetails(function (err) {
if (err) {
return next(err);
}
next();
});
}, function (err) {
callback(err, containers);
});
}
});
};
/**
* client.getContainer
*
* @description get the details for a specific container
*
* @param {String|object} container the container or containerName
* @param callback
*/
exports.getContainer = function (container, callback) {
var containerName = container instanceof this.models.Container ? container.name : container,
self = this;
this._request({
method: 'HEAD',
container: containerName
}, function (err, body, res) {
if (err) {
return callback(err);
}
self._getCdnContainerDetails(containerName, function (err, details) {
if (err) {
return callback(err);
}
container = _.extend({}, details, {
name: containerName,
count: parseInt(res.headers['x-container-object-count'], 10),
bytes: parseInt(res.headers['x-container-bytes-used'], 10)
});
container.metadata = self.deserializeMetadata(self.CONTAINER_META_PREFIX, res.headers);
callback(null, new self.models.Container(self, container));
});
});
};
/**
* client.getCdnContainers
*
* @description get the list of cdn enabled containers
*
* @param {object|Function} options
* @param {Number} [options.limit] the number of records to return
* @param {String} [options.marker] the id of the first record to return in the current query
* @param {String} [options.end_marker] the id of the last record to return in the current query
* @param {boolean} [options.enabled_only] only get containers which are cdn enabled = true
* @param {Function} callback
*/
exports.getCdnContainers = function (options, callback) {
var self = this;
if (typeof options === 'function') {
callback = options;
options = {};
}
var getContainerOpts = {
path: '',
serviceType: this.cdnServiceType,
qs: _.extend({
format: 'json'
}, _.pick(options, ['limit', 'marker', 'end_marker']))
};
if (options.cdnOnly) {
getContainerOpts.qs['enabled_only'] = options.cdnOnly;
}
this._request(getContainerOpts, function (err, body) {
return err
? callback(err)
: callback(null, body.map(function (container) {
//
// The cdn properties are normally set in response headers
// when requesting single cdn containers
//
container.cdnEnabled = container.cdn_enabled == 'true';
container.logRetention = container.log_retention == 'true';
container.cdnUri = container.cdn_uri;
container.cdnSslUri = container.cdn_ssl_uri;
return new self.models.Container(self, container);
}));
});
};
/**
* client.getCdnContainer
*
* @description get the details for a specific cdn enabled container
*
* @param {String|object} container the container or containerName
* @param callback
*/
exports.getCdnContainer = function (container, callback) {
var self = this;
this._getCdnContainerDetails(container, function(err, details) {
return err
? callback(err)
: callback(null, new self.models.Container(self, details));
});
};
/**
* client.setCdnEnabled
*
* @description enable or disable cdn capabilities on a storage container
*
* @param {String|object} container the container or containerName
* @param {object|boolean} options an object with options, or boolean to just enable/disable
* @param {boolean} options.enabled enable or disable the cdn capability
* @param {number} options.ttl configure the CDN ttl for this container
* @param callback
*/
exports.setCdnEnabled = function (container, options, callback) {
var self = this,
containerName = container instanceof self.models.Container ? container.name : container,
enabled = typeof options === 'boolean' ? options : options.enabled;
if (typeof options === 'function') {
callback = options;
options = {};
enabled = true;
}
var cdnOpts = {
method: 'PUT',
container: containerName,
serviceType: this.cdnServiceType,
headers: {
'x-cdn-enabled': enabled
}
};
if (options.ttl) {
cdnOpts.headers['x-ttl'] = options.ttl;
}
self._request(cdnOpts, function(err) {
if (err) {
return callback(err);
}
self.getContainer(containerName, function(err, container) {
return err
? callback(err)
: callback(err, container);
});
});
};
/**
* client.updateCdnContainer
*
* @description update the settings for a cdn container
*
* @param {String|object} container the container or containerName
* @param {object} options
* @param {boolean} options.enabled enable or disable the cdn capability
* @param {number} options.ttl configure the CDN ttl for this container
* @param {boolean} options.logRetention enable log retention for this container
* @param callback
*/
exports.updateCdnContainer = function (container, options, callback) {
var self = this,
containerName = container instanceof self.models.Container ? container.name : container;
var cdnOpts = {
method: 'POST',
container: containerName,
serviceType: this.cdnServiceType,
headers: {}
};
if (options.ttl) {
cdnOpts.headers['x-ttl'] = options.ttl;
}
if (typeof options.enabled === 'boolean') {
cdnOpts.headers['x-cdn-enabled'] = options.enabled;
}
if (typeof options.logRetention === 'boolean') {
cdnOpts.headers['x-log-retention'] = options.logRetention;
}
self._request(cdnOpts, function (err) {
if (err) {
return callback(err);
}
self.getContainer(containerName, function (err, container) {
return err
? callback(err)
: callback(err, container);
});
});
};
/**
* client._getCdnContainerDetails
*
* @description Convenience function for getting CDN container details
*/
exports._getCdnContainerDetails = function(container, callback) {
var containerName = container instanceof this.models.Container ? container.name : container,
self = this;
this._request({
method: 'HEAD',
container: containerName,
serviceType: this.cdnServiceType
}, function (err, body, res) {
if (err && !(err.statusCode === 403 || err.statusCode === 404)) {
return callback(err);
}
else if (err) {
return callback(null, {}); // return empty object
}
container = {
name: containerName,
count: parseInt(res.headers['x-container-object-count'], 10),
bytes: parseInt(res.headers['x-container-bytes-used'], 10)
};
container.cdnUri = res.headers['x-cdn-uri'];
container.cdnSslUri = res.headers['x-cdn-ssl-uri'];
container.cdnEnabled = res.headers['x-cdn-enabled'] == 'True';
container.cdnStreamingUri = res.headers['x-cdn-streaming-uri'];
container.cdniOSUri = res.headers['x-cdn-ios-uri'];
container.ttl = parseInt(res.headers['x-ttl'], 10);
container.logRetention = res.headers['x-log-retention'] == 'True';
container.metadata = self.deserializeMetadata(self.CONTAINER_META_PREFIX, res.headers);
callback(null, container);
});
};
/**
* client.setStaticWebsite
*
* @description set the static website index page on a storage container
*
* @param {String|object} container the container or containerName
* @param {object} options an object with options
* @param {String} options.indexFile configure the static website index file for this container
* @param {String} options.errorFile configure the static website error file for this container
* @param callback
*/
exports.setStaticWebsite = function (container, options, callback) {
var indexFile = options.indexFile, errorFile = options.errorFile;
if (typeof options === 'function') {
callback = options;
indexFile = 'index.html';
errorFile = 'error.html';
}
container.metadata['web-index'] = indexFile;
container.metadata['web-error'] = errorFile;
this.updateContainerMetadata(container, callback);
};
/**
* client.removeStaticWebsite
*
* @description remove the static website index/error page on a storage container
*
* @param {String|object} container the container or containerName
* @param callback
*/
exports.removeStaticWebsite = function (container, callback) {
var indexFile = container.metadata['web-index'], errorFile = container.metadata['web-error'],
metadata = {
'web-index': indexFile,
'web-error': errorFile
};
this.removeContainerMetadata(container, metadata, callback);
};
/**
* client.setTemporaryUrlKey
*
* @description set a temporaryUrl key on the current account
*
* @param {String} key the secret key to be used in hmac signing temporary urls
* @param callback
*/
exports.setTemporaryUrlKey = function(key, callback) {
this._request({
method: 'POST',
headers: {
'X-Account-Meta-Temp-Url-Key': key
}
}, function (err) {
callback(err);
});
};
/**
* client.generateTempUrl
*
* @description create a temporary url for GET/PUT to a cloud files container
*
* @param {String|object} container the container or container name for the url
* @param {String|object} file the file or fileName for the url
* @param {String} method either GET or PUT
* @param {Number} time expiry for the url in seconds (from now)
* @param {String} key the secret key to be used for signing the url
* @param callback
*/
exports.generateTempUrl = function(container, file, method, time, key, callback) {
var containerName = container instanceof this.models.Container ? container.name : container,
fileName = file instanceof this.models.File ? file.name : file,
time = typeof time === 'number' ? time : parseInt(time),
self = this,
split = '/v1';
function createUrl() {
// construct our hmac signature
var expiry = parseInt(new Date().getTime() / 1000) + time,
url = self._getUrl({
container: containerName,
path: fileName
}),
hmac_body = method.toUpperCase() + '\n' + expiry + '\n' + split + url.split(split)[1];
var hash = crypto.createHmac('sha1', key).update(hmac_body).digest('hex');
callback(null, url + '?temp_url_sig=' + hash + '&temp_url_expires=' + expiry);
}
// We have to be authed to make sure we have the service catalog
// this is required to validate the service url
if (!this._isAuthorized()) {
this.auth(function(err) {
if (err) {
callback(err);
return;
}
createUrl();
});
return;
}
createUrl();
};