Compare commits

...

3 Commits

Author SHA1 Message Date
Rahul Padigela fd935a9588
Merge pull request #1263 from yutaka-oishi/ft/restore-object
FT: Provide RestoreObject API to support cold storages
2020-11-17 17:54:44 -08:00
yutaka-oishi 27904cedac
Merge branch 'development/8.1' into ft/restore-object 2020-09-15 14:59:07 +09:00
Yutaka Oishi c5e4c522ca FT: Provide RestoreObject API to support cold storages 2020-09-11 16:30:26 +09:00
8 changed files with 256 additions and 0 deletions

View File

@ -153,6 +153,10 @@ module.exports = {
},
},
utils: require('./lib/storage/utils'),
coldstorage: {
ColdStorageWrapper:
require('./lib/storage/coldstorage/ColdStorageWrapper'),
}
},
models: {
BackendInfo: require('./lib/models/BackendInfo'),

View File

@ -39,6 +39,7 @@ const awsSubresources = [
'replication',
'versions',
'website',
'restore',
];
function getCanonicalizedResource(request, clientType) {

View File

@ -5,6 +5,9 @@ const VersionIDUtils = require('../versioning/VersionID');
const ObjectMDLocation = require('./ObjectMDLocation');
const ObjectMDAmzRestore = require('./ObjectMDAmzRestore');
/**
* Class to manage metadata object for regular s3 objects (instead of
* mpuPart metadata for example)
@ -42,6 +45,7 @@ class ObjectMD {
// set latest md model version now that we ensured
// backward-compat conversion
this._data['md-model-version'] = constants.mdModelVersion;
}
/**
@ -102,6 +106,9 @@ class ObjectMD {
// similar to normalizing request but after checkAuth so
// string to sign is not impacted. This is GH Issue#89.
'x-amz-storage-class': 'STANDARD',
'x-amz-restore': {
'ongoing-request': false,
},
'x-amz-server-side-encryption': '',
'x-amz-server-side-encryption-aws-kms-key-id': '',
'x-amz-server-side-encryption-customer-algorithm': '',
@ -1033,6 +1040,33 @@ class ObjectMD {
return this;
}
/**
* Get x-amz-restore
*
* @returns {ObjectMDAmzRestore} x-amz-restore
*/
getAmzRestore() {
return this._data['x-amz-restore'];
}
/**
* Set x-amz-restore
*
* @param {ObjectMDAmzRestore} value x-amz-restore object
* @returns {ObjectMD} itself
* @throws {Error} case of invalid parameter
*/
setAmzRestore(value) {
if (value && !(value instanceof ObjectMDAmzRestore)) {
throw new Error('x-amz-restore is must be type of ObjectMDAmzRestore.');
}
this._data['x-amz-restore'] = value;
return this;
}
/**
* Create or update the microVersionId field
*

View File

@ -0,0 +1,73 @@
/**
* class representing the x-amz-restore of object metadata.
*
* @class
*/
class ObjectMDAmzRestore {
/**
*
* @constructor
* @param {boolean} ongoingRequest ongoing-request
* @param {Date} [expiryDate] expiry-date
* @throws {Error} case of invalid parameter
*/
constructor(ongoingRequest, expiryDate = undefined) {
this._data = {};
this.setOngoingRequest(ongoingRequest);
this.setExpirayDate(expiryDate);
}
/**
*
* @returns {boolean} ongoing-request
*/
getOngoingRequest() {
return this._data['ongoing-request'];
}
/**
*
* @param {boolean} value ongoing-request
* @returns {void}
* @throws {Error} case of invalid parameter
*/
setOngoingRequest(value) {
if (!value) {
throw new Error('ongoing-request is required.');
} else if (typeof value !== 'boolean') {
throw new Error('ongoing-request is must be type of boolean.');
}
this._data['ongoing-request'] = value;
}
/**
*
* @returns {Date} expiry-date
*/
getExpiryDate() {
return this._data['expiry-date'] || null;
}
/**
*
* @param {Date} value expiry-date
* @returns {void}
* @throws {Error} case of invalid parameter
*/
setExpiryDate(value) {
if (!(value instanceof Date)) {
throw new Error('expiry-date is must be type of Date.');
}
this._data['expiry-date'] = value;
}
}
module.exports = ObjectMDAmzRestore;

View File

@ -47,6 +47,14 @@ function routePOST(request, response, api, log) {
corsHeaders));
}
// POST Object restore
if (request.query.restore !== undefined) {
return api.callApiMethod('objectRestore', request, response,
log, (err, statusCode, resHeaders) =>
routesUtils.responseNoBody(err, resHeaders, response,
statusCode, log));
}
return routesUtils.responseNoBody(errors.NotImplemented, null, response,
200, log);
}

View File

@ -0,0 +1,99 @@
const errors = require('../../errors');
const ColdStorageFileInterface = require('./file/ColdStorageFileInterface');
class ColdStorageWrapper {
constructor(clientName, params, logger) {
if (clientName === 'file') {
this.client = new ColdStorageFileInterface(params, logger);
this.implName = 'coldstorageFile';
}
}
setup(done) {
if (this.client.setup) {
return this.client.setup(done);
}
return process.nextTick(done);
}
updateAmzStorageClass(bucketName, objName, objMD, storageClass, log, cb) {
if (this.client.updateAmzStorageClass){
log.debug('try to update x-amz-storage-class');
this.client.updateAmzStorageClass(bucketName, objName, objMD, storageClass, log, err => {
if (err) {
log.debug('error from metadata', { implName: this.implName,
error: err });
return cb(err);
}
log.trace('update x-amz-storage-class complete. : ' + storageClass);
return cb();
});
}
}
updateRestoreExpiration(bucketName, objName, objMD, log, cb){
if (this.client.updateRestoreExpiration){
log.debug('try to update restore enpire-date');
this.client.updateRestoreExpiration(bucketName, objName, objMD, storageClass, log, err => {
if (err) {
log.debug('error from metadata', { implName: this.implName,
error: err });
return cb(err);
}
log.trace('update restore enpire-date complete. : ' + storageClass);
return cb();
});
}
}
updateRestoreOngoing(bucketName, objName, objMD, updateParam, log, cb){
if (this.client.updateRestoreOngoing){
log.debug('try to update restore ongoing-request');
this.client.updateRestoreOngoing(bucketName, objName, objMD, updateParam, log, err => {
if (err) {
log.debug('error from metadata', { implName: this.implName,
error: err });
return cb(err);
}
log.trace('update restore ongoing-request complete. : ' + updateParam);
return cb();
});
}
}
deleteAmzRestore(bucketName, objName, objMD, log, cb){
if (this.client.deleteAmzRestore){
log.debug('try to delete x-amz-restore');
this.client.deleteAmzRestore(bucketName, objName, objMD, log, err => {
if (err) {
log.debug('error from metadata', { implName: this.implName,
error: err });
return cb(err);
}
log.trace('delete x-amz-restore complete.');
return cb();
});
}
}
switch(newClient, cb) {
this.client = newClient;
return cb();
}
close(cb) {
if (typeof this.client.close === 'function') {
return this.client.close(cb);
}
return cb();
}
}
module.exports = ColdStorageWrapper;

View File

@ -0,0 +1,36 @@
const assert = require('assert');
class ColdStorageFileInterface {
updateAmzStorageClass(bucketName, objName, objMD, storageClass, log, cb) {
log.debug('does not support updateAmzStorageClass', {
implName: this.implName,
});
return cb(errors.NotImplemented);
}
updateRestoreExpiration(bucketName, objName, objMD, requestParam, log, cb){
log.debug('does not support updateRestoreExpiration', {
implName: this.implName,
});
return cb(errors.NotImplemented);
}
updateRestoreOngoing(bucketName, objName, objMD, requestParam, log, cb){
log.debug('does not support updateRestoreOngoing', {
implName: this.implName,
});
return cb(errors.NotImplemented);
}
deleteAmzRestore(bucketName, objName, objMD, log, cb){
log.debug('does not support deleteAmzRestore', {
implName: this.implName,
});
return cb(errors.NotImplemented);
}
}
module.exports = ColdStorageFileInterface;

View File

@ -413,6 +413,7 @@ describe('getAttributes static method', () => {
'x-amz-server-side-encryption-aws-kms-key-id': true,
'x-amz-server-side-encryption-customer-algorithm': true,
'x-amz-website-redirect-location': true,
'x-amz-restore': true,
'acl': true,
'key': true,
'location': true,