@yawetse/pkgcloud
Version:
An infrastructure-as-a-service agnostic cloud library for node.js
240 lines (207 loc) • 6.64 kB
JavaScript
/*
* containers.js: Instance methods for working with containers
* for Openstack Object Storage
*
* (C) 2013 Rackspace, Ken Perkins
* MIT LICENSE
*
*/
var async = require('async'),
_ = require('underscore');
/**
* client.getContainers
*
* @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] Marker value. Operation returns object names that are greater than this value.
* @param {String} [options.end_marker] Operation returns object names that are less than this value.
* @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');
}
return callback(null, body.map(function (container) {
return new self.models.Container(self, container);
}));
});
};
/**
* 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);
}
var details = _.extend({}, body, {
name: containerName,
count: parseInt(res.headers['x-container-object-count'], 10),
bytes: parseInt(res.headers['x-container-bytes-used'], 10)
});
details.metadata = self.deserializeMetadata(self.CONTAINER_META_PREFIX, res.headers);
callback(null, new self.models.Container(self, details));
});
};
/**
* client.createContainer
*
* @description create a new container
*
* @param {object} options
* @param {String} options.name the name of the new container
* @param {object} [options.metadata] optional metadata about the container
* @param callback
*/
exports.createContainer = function (options, callback) {
var containerName = typeof options === 'object' ? options.name : options,
self = this;
var createContainerOpts = {
method: 'PUT',
container: containerName
};
if (options.metadata) {
createContainerOpts.headers = self.serializeMetadata(self.CONTAINER_META_PREFIX, options.metadata);
}
this._request(createContainerOpts, function (err) {
return err
? callback(err)
: callback(null, new self.models.Container(self, { name: containerName, metadata: options.metadata }));
});
};
/**
* client.updateContainerMetadata
*
* @description Updates the metadata in the specified `container` in
* the storage container associated with this instance.
*
* @param {String|object} container the container or containerName
* @param callback
*/
exports.updateContainerMetadata = function (container, callback) {
this._updateContainerMetadata(container,
this.serializeMetadata(this.CONTAINER_META_PREFIX, container.metadata),
callback);
};
/**
* client.updateContainerMetadata
*
* @description Removes the provided `metadata` in the specified
* `container` in the storage container associated with this instance.
*
* @param {String|object} container the container or containerName
* @param {object} metadataToRemove the metadata to remove from the container
* @param callback
*/
exports.removeContainerMetadata = function (container, metadataToRemove, callback) {
this._updateContainerMetadata(container,
this.serializeMetadata(this.CONTAINER_REMOVE_META_PREFIX, metadataToRemove),
callback);
};
/**
* client._updateContainerMetadata
*
* @description Convenience function for updating container metadata
*/
exports._updateContainerMetadata = function(container, metadata, callback) {
var self = this;
if (!(container instanceof self.models.Container)) {
throw new Error('Must update an existing container instance');
}
var updateContainerOpts = {
method: 'POST',
container: container.name,
headers: metadata
};
this._request(updateContainerOpts, function (err) {
// omit our newly deleted header fields, if any
if (!err) {
container.metadata = _.omit(container.metadata,
_.keys(self.deserializeMetadata(self.CONTAINER_REMOVE_META_PREFIX, metadata)));
}
return err
? callback(err)
: callback(null, container);
});
};
/**
* client.destroyContainer
*
* @description Delete the storage container and all files within it
*
* @param {String|object} container the container or containerName
* @param callback
*/
exports.destroyContainer = function (container, callback) {
var containerName = container instanceof this.models.Container ? container.name : container,
self = this;
// first fetch the container, to get the count of objects
this.getContainer(container, function(err, container) {
if (err) {
callback(err);
return;
}
// we need to call get files, but with a limit of the current count of the container
// with a modest offset incase of any cache staleness
self.getFiles(container, { limit: container.count + 1000 }, function (err, files) {
if (err) {
return callback(err);
}
function deleteContainer(err) {
if (err) {
return callback(err);
}
self._request({
method: 'DELETE',
container: containerName
}, function (err) {
// if the file we're deleting returns a 404 error, ignore it
if (err && err.statusCode === 404) {
callback(null, false);
return;
}
// non-404 case, propagate it
else if (err) {
callback(err);
return;
}
callback(null, true);
});
}
function destroyFile(file, next) {
file.remove(next);
}
if (files.length === 0) {
return deleteContainer();
}
async.forEachLimit(files, 15, destroyFile, deleteContainer);
});
});
};