Compare commits
14 Commits
developmen
...
feature/CL
Author | SHA1 | Date |
---|---|---|
Nicolas Humbert | aef0e84081 | |
Nicolas Humbert | 3c08060753 | |
Nicolas Humbert | 9d007e7d01 | |
Nicolas Humbert | 676f6db1cc | |
Nicolas Humbert | 11f70f8a70 | |
Nicolas Humbert | 88d09a5307 | |
Nicolas Humbert | feb67531aa | |
Nicolas Humbert | 52d6ce2795 | |
Nicolas Humbert | 1104d466ca | |
Nicolas Humbert | f52f9a0de9 | |
Nicolas Humbert | 9b14d0acb1 | |
Nicolas Humbert | 46fadbc8f4 | |
Nicolas Humbert | fc06498df0 | |
Nicolas Humbert | 43c0ea5dc9 |
|
@ -118,6 +118,12 @@ function prepareRequestContexts(apiMethod, request, sourceBucket,
|
||||||
generateRequestContext('objectPutACL');
|
generateRequestContext('objectPutACL');
|
||||||
requestContexts.push(putAclRequestContext);
|
requestContexts.push(putAclRequestContext);
|
||||||
}
|
}
|
||||||
|
// if put object with version
|
||||||
|
if (request.headers['x-scal-s3-version-id']) {
|
||||||
|
const putVersionRequestContext =
|
||||||
|
generateRequestContext('objectPutVersion');
|
||||||
|
requestContexts.push(putVersionRequestContext);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const requestContext =
|
const requestContext =
|
||||||
generateRequestContext(apiMethodAfterVersionCheck);
|
generateRequestContext(apiMethodAfterVersionCheck);
|
||||||
|
|
|
@ -8,7 +8,7 @@ const services = require('../../../services');
|
||||||
const logger = require('../../../utilities/logger');
|
const logger = require('../../../utilities/logger');
|
||||||
const { dataStore } = require('./storeObject');
|
const { dataStore } = require('./storeObject');
|
||||||
const locationConstraintCheck = require('./locationConstraintCheck');
|
const locationConstraintCheck = require('./locationConstraintCheck');
|
||||||
const { versioningPreprocessing } = require('./versioning');
|
const { versioningPreprocessing, overwrittingVersioning } = require('./versioning');
|
||||||
const removeAWSChunked = require('./removeAWSChunked');
|
const removeAWSChunked = require('./removeAWSChunked');
|
||||||
const getReplicationInfo = require('./getReplicationInfo');
|
const getReplicationInfo = require('./getReplicationInfo');
|
||||||
const { config } = require('../../../Config');
|
const { config } = require('../../../Config');
|
||||||
|
@ -60,6 +60,9 @@ function _storeInMDandDeleteData(bucketName, dataGetInfo, cipherBundle,
|
||||||
function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
|
function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
|
||||||
canonicalID, cipherBundle, request, isDeleteMarker, streamingV4Params,
|
canonicalID, cipherBundle, request, isDeleteMarker, streamingV4Params,
|
||||||
log, callback) {
|
log, callback) {
|
||||||
|
const putVersionId = request.headers['x-scal-s3-version-id'];
|
||||||
|
const isPutVersion = putVersionId || putVersionId === '';
|
||||||
|
|
||||||
const size = isDeleteMarker ? 0 : request.parsedContentLength;
|
const size = isDeleteMarker ? 0 : request.parsedContentLength;
|
||||||
// although the request method may actually be 'DELETE' if creating a
|
// although the request method may actually be 'DELETE' if creating a
|
||||||
// delete marker, for our purposes we consider this to be a 'PUT'
|
// delete marker, for our purposes we consider this to be a 'PUT'
|
||||||
|
@ -257,6 +260,15 @@ function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
|
||||||
return next(null, dataGetInfoArr);
|
return next(null, dataGetInfoArr);
|
||||||
},
|
},
|
||||||
function getVersioningInfo(infoArr, next) {
|
function getVersioningInfo(infoArr, next) {
|
||||||
|
if (isPutVersion) {
|
||||||
|
return overwrittingVersioning(bucketName, putVersionId, objMD, metadataStoreParams, log,
|
||||||
|
(err, options) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
return next(null, options, infoArr);
|
||||||
|
});
|
||||||
|
}
|
||||||
return versioningPreprocessing(bucketName, bucketMD,
|
return versioningPreprocessing(bucketName, bucketMD,
|
||||||
metadataStoreParams.objectKey, objMD, log, (err, options) => {
|
metadataStoreParams.objectKey, objMD, log, (err, options) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -278,6 +290,7 @@ function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
|
||||||
metadataStoreParams.isNull = options.isNull;
|
metadataStoreParams.isNull = options.isNull;
|
||||||
metadataStoreParams.nullVersionId = options.nullVersionId;
|
metadataStoreParams.nullVersionId = options.nullVersionId;
|
||||||
metadataStoreParams.nullUploadId = options.nullUploadId;
|
metadataStoreParams.nullUploadId = options.nullUploadId;
|
||||||
|
metadataStoreParams.masterVersionId = options.masterVersionId;
|
||||||
return _storeInMDandDeleteData(bucketName, infoArr,
|
return _storeInMDandDeleteData(bucketName, infoArr,
|
||||||
cipherBundle, metadataStoreParams,
|
cipherBundle, metadataStoreParams,
|
||||||
options.dataToDelete, requestLogger, requestMethod, next);
|
options.dataToDelete, requestLogger, requestMethod, next);
|
||||||
|
|
|
@ -10,6 +10,38 @@ const versionIdUtils = versioning.VersionID;
|
||||||
const nonVersionedObjId =
|
const nonVersionedObjId =
|
||||||
versionIdUtils.getInfVid(config.replicationGroupId);
|
versionIdUtils.getInfVid(config.replicationGroupId);
|
||||||
|
|
||||||
|
/** _decodePutVersionId - decode the version id from x-scal-s3-version-id header
|
||||||
|
* @param {string} [versionId] - version id from x-scal-s3-version-id header
|
||||||
|
* @return {object} response
|
||||||
|
* {string} response.decodedVid - decoded version ID
|
||||||
|
* {boolean} response.isNull - if true, version is null or empty, false otherwise.
|
||||||
|
* {Error} response.err - error
|
||||||
|
*/
|
||||||
|
function _decodePutVersionId(versionId) {
|
||||||
|
const invalidErr = errors.InvalidArgument
|
||||||
|
.customizeDescription('Invalid version id specified in x-scal-s3-version-id header');
|
||||||
|
if (versionId || versionId === '') {
|
||||||
|
if (versionId === '' || versionId === 'null') {
|
||||||
|
return { decodedVid: nonVersionedObjId, isNull: true, err: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
let decoded;
|
||||||
|
try {
|
||||||
|
decoded = versionIdUtils.decode(versionId);
|
||||||
|
} catch (err) {
|
||||||
|
return { decodedVid: null, isNull: false, err: invalidErr };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded instanceof Error) {
|
||||||
|
return { decodedVid: null, isNull: false, err: invalidErr };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { decodedVid: decoded, isNull: false, err: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { decodedVid: undefined, isNull: false, err: null };
|
||||||
|
}
|
||||||
|
|
||||||
/** decodedVidResult - decode the version id from a query object
|
/** decodedVidResult - decode the version id from a query object
|
||||||
* @param {object} [reqQuery] - request query object
|
* @param {object} [reqQuery] - request query object
|
||||||
* @param {string} [reqQuery.versionId] - version ID sent in request query
|
* @param {string} [reqQuery.versionId] - version ID sent in request query
|
||||||
|
@ -149,6 +181,7 @@ function processVersioningState(mst, vstat) {
|
||||||
const options = {};
|
const options = {};
|
||||||
const storeOptions = {};
|
const storeOptions = {};
|
||||||
const delOptions = {};
|
const delOptions = {};
|
||||||
|
|
||||||
// object does not exist or is not versioned (before versioning)
|
// object does not exist or is not versioned (before versioning)
|
||||||
if (mst.versionId === undefined || mst.isNull) {
|
if (mst.versionId === undefined || mst.isNull) {
|
||||||
// versioning is suspended, overwrite existing master version
|
// versioning is suspended, overwrite existing master version
|
||||||
|
@ -370,6 +403,68 @@ function preprocessingVersioningDelete(bucketName, bucketMD, objectMD,
|
||||||
return callback(null, options);
|
return callback(null, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** overwrittingVersioning - return versioning information for S3 to handle
|
||||||
|
* storing version metadata with a specific version id.
|
||||||
|
* @param {string} bucketName - name of the bucket.
|
||||||
|
* @param {string} putVersionId - version id from x-scal-s3-version-id header
|
||||||
|
* @param {object} objMD - obj metadata of the master version
|
||||||
|
* @param {object} metadataStoreParams - custom built object containing resource details.
|
||||||
|
* @param {RequestLogger} log - logger instance
|
||||||
|
* @param {function} cb - cb(err, options)
|
||||||
|
* options.versionId - specific versionId to overwrite in metadata
|
||||||
|
* ('' overwrites the master version)
|
||||||
|
* options.versioning - (true/undefined) metadata instruction to create new ver
|
||||||
|
* options.isNull - (true/undefined) whether new version is null or not
|
||||||
|
* options.nullVersionId - if storing a null version in version history, the
|
||||||
|
* version id of the null version
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function overwrittingVersioning(bucketName, putVersionId, objMD, metadataStoreParams, log, cb) {
|
||||||
|
const { err, decodedVid: decodedPutVersionId, isNull } = _decodePutVersionId(putVersionId);
|
||||||
|
if (err) {
|
||||||
|
log.error('invalid x-scal-s3-version-id header value', { error: err, putVersionId });
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 1: overwritting the master null version or,
|
||||||
|
// overwritting a non-versioned object.
|
||||||
|
if (isNull && (objMD.isNull || !objMD.versionId)) {
|
||||||
|
metadataStoreParams.creationTime = objMD['creation-time'];
|
||||||
|
metadataStoreParams.lastModifiedDate = objMD['last-modified'];
|
||||||
|
// masterVersionId is not undefined: overwrite the master version.
|
||||||
|
const options = { versionId: '', masterVersionId: objMD.versionId || '' };
|
||||||
|
if (objMD.versionId) {
|
||||||
|
// NOTE: if object is not versioned,
|
||||||
|
// we do not store the isNull property.
|
||||||
|
options.isNull = true;
|
||||||
|
}
|
||||||
|
return process.nextTick(() => cb(null, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 2: overwritting a version id.
|
||||||
|
const options = {
|
||||||
|
versionId: decodedPutVersionId,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: We need to retrieve the specific object version ("versionMD") to ensure
|
||||||
|
// that the rest of the original object properties remain intact.
|
||||||
|
// NOTE: "objMD" holds the master version properties,
|
||||||
|
return metadata.getObjectMD(bucketName, metadataStoreParams.objectKey, options, log,
|
||||||
|
(err, versionMD) => {
|
||||||
|
if (err) {
|
||||||
|
log.error('version from x-scal-s3-version-id header does not exist',
|
||||||
|
{ error: err, putVersionId });
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
metadataStoreParams.creationTime = versionMD['creation-time'];
|
||||||
|
metadataStoreParams.lastModifiedDate = versionMD['last-modified'];
|
||||||
|
// NOTE: set isNull property to true if overwritting null version.
|
||||||
|
options.isNull = versionMD.isNull;
|
||||||
|
options.nullVersionId = versionMD.nullVersionId;
|
||||||
|
return cb(null, options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
decodeVersionId,
|
decodeVersionId,
|
||||||
getVersionIdResHeader,
|
getVersionIdResHeader,
|
||||||
|
@ -378,4 +473,5 @@ module.exports = {
|
||||||
getMasterState,
|
getMasterState,
|
||||||
versioningPreprocessing,
|
versioningPreprocessing,
|
||||||
preprocessingVersioningDelete,
|
preprocessingVersioningDelete,
|
||||||
|
overwrittingVersioning,
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,7 +97,7 @@ const services = {
|
||||||
lastModifiedDate, versioning, versionId, uploadId,
|
lastModifiedDate, versioning, versionId, uploadId,
|
||||||
tagging, taggingCopy, replicationInfo, defaultRetention,
|
tagging, taggingCopy, replicationInfo, defaultRetention,
|
||||||
dataStoreName, creationTime, retentionMode, retentionDate,
|
dataStoreName, creationTime, retentionMode, retentionDate,
|
||||||
legalHold, originOp } = params;
|
legalHold, originOp, masterVersionId } = params;
|
||||||
log.trace('storing object in metadata');
|
log.trace('storing object in metadata');
|
||||||
assert.strictEqual(typeof bucketName, 'string');
|
assert.strictEqual(typeof bucketName, 'string');
|
||||||
const md = new ObjectMD();
|
const md = new ObjectMD();
|
||||||
|
@ -163,6 +163,10 @@ const services = {
|
||||||
md.setUploadId(uploadId);
|
md.setUploadId(uploadId);
|
||||||
options.replayId = uploadId;
|
options.replayId = uploadId;
|
||||||
}
|
}
|
||||||
|
// used to overwrite master version.
|
||||||
|
if (masterVersionId || masterVersionId === '') {
|
||||||
|
options.masterVersionId = masterVersionId;
|
||||||
|
}
|
||||||
// information to store about the version and the null version id
|
// information to store about the version and the null version id
|
||||||
// in the object metadata
|
// in the object metadata
|
||||||
const { isNull, nullVersionId, nullUploadId, isDeleteMarker } = params;
|
const { isNull, nullVersionId, nullUploadId, isDeleteMarker } = params;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
"homepage": "https://github.com/scality/S3#readme",
|
"homepage": "https://github.com/scality/S3#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hapi/joi": "^17.1.0",
|
"@hapi/joi": "^17.1.0",
|
||||||
"arsenal": "git+https://github.com/scality/Arsenal.git#8.1.47",
|
"arsenal": "git+https://github.com/scality/Arsenal.git#feature/ARSN-202/overwrite",
|
||||||
"async": "~2.5.0",
|
"async": "~2.5.0",
|
||||||
"aws-sdk": "2.905.0",
|
"aws-sdk": "2.905.0",
|
||||||
"azure-storage": "^2.1.0",
|
"azure-storage": "^2.1.0",
|
||||||
|
|
|
@ -0,0 +1,523 @@
|
||||||
|
const assert = require('assert');
|
||||||
|
const async = require('async');
|
||||||
|
const { versioning } = require('arsenal');
|
||||||
|
|
||||||
|
const { config } = require('../../../../../lib/Config');
|
||||||
|
const withV4 = require('../support/withV4');
|
||||||
|
const BucketUtility = require('../../lib/utility/bucket-util');
|
||||||
|
const metadata = require('../../../../../lib/metadata/wrapper');
|
||||||
|
const { DummyRequestLogger } = require('../../../../unit/helpers');
|
||||||
|
const checkError = require('../../lib/utility/checkError');
|
||||||
|
|
||||||
|
const versionIdUtils = versioning.VersionID;
|
||||||
|
|
||||||
|
const log = new DummyRequestLogger();
|
||||||
|
|
||||||
|
const nonVersionedObjId =
|
||||||
|
versionIdUtils.getInfVid(config.replicationGroupId);
|
||||||
|
const bucketName = 'bucket1putversion27';
|
||||||
|
const objectName = 'object1putversion';
|
||||||
|
const mdListingParams = { listingType: 'DelimiterVersions', maxKeys: 1000 };
|
||||||
|
|
||||||
|
function _getMetadata(bucketName, objectName, versionId, cb) {
|
||||||
|
let decodedVersionId;
|
||||||
|
if (versionId) {
|
||||||
|
if (versionId === 'null') {
|
||||||
|
decodedVersionId = nonVersionedObjId;
|
||||||
|
} else {
|
||||||
|
decodedVersionId = versionIdUtils.decode(versionId);
|
||||||
|
}
|
||||||
|
if (decodedVersionId instanceof Error) {
|
||||||
|
return cb(new Error('Invalid version id specified'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metadata.getObjectMD(bucketName, objectName, { versionId: decodedVersionId },
|
||||||
|
log, (err, objMD) => {
|
||||||
|
if (err) {
|
||||||
|
assert.equal(err, null, 'Getting object metadata: expected success, ' +
|
||||||
|
`got error ${JSON.stringify(err)}`);
|
||||||
|
}
|
||||||
|
return cb(null, objMD);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function putObjectVersion(s3, params, vid, next) {
|
||||||
|
const request = s3.putObject(params);
|
||||||
|
request.on('build', () => {
|
||||||
|
request.httpRequest.headers['x-scal-s3-version-id'] = vid;
|
||||||
|
});
|
||||||
|
return request.send(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('PUT object with x-scal-s3-version-id header', () => {
|
||||||
|
withV4(sigCfg => {
|
||||||
|
let bucketUtil;
|
||||||
|
let s3;
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
|
bucketUtil = new BucketUtility('default', sigCfg);
|
||||||
|
s3 = bucketUtil.s3;
|
||||||
|
return metadata.setup(() =>
|
||||||
|
s3.createBucket({ Bucket: bucketName }, err => {
|
||||||
|
if (err) {
|
||||||
|
assert.equal(err, null, 'Creating bucket: Expected success, ' +
|
||||||
|
`got error ${JSON.stringify(err)}`);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.stdout.write('Emptying bucket');
|
||||||
|
return bucketUtil.empty(bucketName)
|
||||||
|
.then(() => {
|
||||||
|
process.stdout.write('Deleting bucket');
|
||||||
|
return bucketUtil.deleteOne(bucketName);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
process.stdout.write('Error in afterEach');
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite an object', done => {
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putObject(params, next),
|
||||||
|
(res, next) => _getMetadata(bucketName, objectName, undefined, next),
|
||||||
|
(objMD, next) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return putObjectVersion(s3, params, '', next);
|
||||||
|
},
|
||||||
|
(res, next) => _getMetadata(bucketName, objectName, undefined, next),
|
||||||
|
], (err, objMDAfter) => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite a versioned object', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
let vId;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, (err, res) => {
|
||||||
|
vId = res.VersionId;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => _getMetadata(bucketName, objectName, undefined, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => putObjectVersion(s3, params, vId, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, undefined, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if version specified is invalid', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => putObjectVersion(s3, params, 'aJLWKz4Ko9IjBBgXKj5KQT.G9UHv0g7P', err => {
|
||||||
|
checkError(err, 'InvalidArgument', 400);
|
||||||
|
return next();
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail if version specified does not exist', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => putObjectVersion(s3, params, '', err => {
|
||||||
|
checkError(err, 'NoSuchKey', 404);
|
||||||
|
return next();
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite a non-current null version', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, 'null', (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => putObjectVersion(s3, params, 'null', err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, 'null', (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite the lastest version and keep nullVersionId', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let vId;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, (err, res) => {
|
||||||
|
vId = res.VersionId;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => putObjectVersion(s3, params, vId, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite a current null version', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const sParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Suspended',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => s3.putBucketVersioning(sParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, undefined, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => putObjectVersion(s3, params, '', err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, undefined, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite a non-current versioned object', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
let vId;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => s3.putObject(params, (err, res) => {
|
||||||
|
vId = res.VersionId;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => putObjectVersion(s3, params, vId, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite the current versioned object', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
let vId;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => s3.putObject(params, (err, res) => {
|
||||||
|
vId = res.VersionId;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => putObjectVersion(s3, params, vId, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite the current versioned object after bucket version suspended', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const sParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Suspended',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
let vId;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => s3.putObject(params, (err, res) => {
|
||||||
|
vId = res.VersionId;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => s3.putBucketVersioning(sParams, err => next(err)),
|
||||||
|
next => putObjectVersion(s3, params, vId, err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, vId, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should overwrite the current versioned object after bucket version enabled', done => {
|
||||||
|
const vParams = {
|
||||||
|
Bucket: bucketName,
|
||||||
|
VersioningConfiguration: {
|
||||||
|
Status: 'Enabled',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const params = { Bucket: bucketName, Key: objectName };
|
||||||
|
let objMDBefore;
|
||||||
|
let objMDAfter;
|
||||||
|
let versionsBefore;
|
||||||
|
let versionsAfter;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
next => s3.putObject(params, err => next(err)),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsBefore = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
next => _getMetadata(bucketName, objectName, undefined, (err, objMD) => {
|
||||||
|
objMDBefore = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => s3.putBucketVersioning(vParams, err => next(err)),
|
||||||
|
next => putObjectVersion(s3, params, 'null', err => next(err)),
|
||||||
|
next => _getMetadata(bucketName, objectName, undefined, (err, objMD) => {
|
||||||
|
objMDAfter = objMD;
|
||||||
|
return next(err);
|
||||||
|
}),
|
||||||
|
next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => {
|
||||||
|
versionsAfter = res.Versions;
|
||||||
|
next(err);
|
||||||
|
}),
|
||||||
|
], err => {
|
||||||
|
assert.equal(err, null, `Expected success got error ${JSON.stringify(err)}`);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(versionsAfter, versionsBefore);
|
||||||
|
assert.deepStrictEqual(objMDAfter, objMDBefore);
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -510,9 +510,9 @@ arraybuffer.slice@~0.0.7:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
ioctl "^2.0.2"
|
ioctl "^2.0.2"
|
||||||
|
|
||||||
"arsenal@git+https://github.com/scality/Arsenal.git#8.1.47":
|
"arsenal@git+https://github.com/scality/Arsenal.git#feature/ARSN-202/overwrite":
|
||||||
version "8.1.47"
|
version "8.1.47"
|
||||||
resolved "git+https://github.com/scality/Arsenal.git#e37712e94f747de6c2118d0280da7370e8aedd69"
|
resolved "git+https://github.com/scality/Arsenal.git#4d6cd5642f88e70b93f5e4265ec9afa92b736299"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/async" "^3.2.12"
|
"@types/async" "^3.2.12"
|
||||||
"@types/utf8" "^3.0.1"
|
"@types/utf8" "^3.0.1"
|
||||||
|
|
Loading…
Reference in New Issue