UNPKG

@yawetse/pkgcloud

Version:

An infrastructure-as-a-service agnostic cloud library for node.js

240 lines (207 loc) 6.64 kB
/* * 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); }); }); };