diff --git a/lib/api/apiUtils/object/versioning.js b/lib/api/apiUtils/object/versioning.js index 9747f7f015..4249e867cc 100644 --- a/lib/api/apiUtils/object/versioning.js +++ b/lib/api/apiUtils/object/versioning.js @@ -98,6 +98,7 @@ function _storeNullVersionMD(bucketName, objKey, nullVersionId, objMD, log, cb) isNull2: true, }); } + nullVersionMD.originOp = 's3:StoreNullVersion'; metadata.putObjectMD(bucketName, objKey, nullVersionMD, { versionId }, log, err => { if (err) { log.debug('error from metadata storing null version as new version', diff --git a/tests/unit/api/multipartUpload.js b/tests/unit/api/multipartUpload.js index e5d8bd54a4..0cc2d5ef6a 100644 --- a/tests/unit/api/multipartUpload.js +++ b/tests/unit/api/multipartUpload.js @@ -2396,6 +2396,7 @@ describe('complete mpu with versioning', () => { assert.strictEqual(objVal.completeInProgress, true); } else { assert.strictEqual(params.replayId, testUploadId); + assert.strictEqual(objVal.originOp, 's3:ObjectCreated:CompleteMultipartUpload'); metadataBackend.putObject = origPutObject; } origPutObject( @@ -2413,6 +2414,7 @@ describe('complete mpu with versioning', () => { metadataBackend.putObject = (putBucketName, objName, objVal, params, log, cb) => { assert.strictEqual(params.oldReplayId, testUploadId); + assert.strictEqual(objVal.originOp, 's3:ObjectCreated:Put'); metadataBackend.putObject = origPutObject; origPutObject( putBucketName, objName, objVal, params, log, cb); diff --git a/tests/unit/api/objectCopy.js b/tests/unit/api/objectCopy.js index dc5981d373..b3d39614c0 100644 --- a/tests/unit/api/objectCopy.js +++ b/tests/unit/api/objectCopy.js @@ -113,6 +113,15 @@ describe('objectCopy with versioning', () => { objectCopy(authInfo, testObjectCopyRequest, sourceBucketName, objectKey, undefined, log, err => { assert.ifError(err, `Unexpected err: ${err}`); + sinon.assert.calledWith( + metadata.putObjectMD.lastCall, + destBucketName, + objectKey, + sinon.match({ _data: { originOp: 's3:ObjectCreated:Copy' } }), + sinon.match.any, + sinon.match.any, + sinon.match.any + ); setImmediate(() => { versioningTestUtils .assertDataStoreValues(ds, expectedValues); @@ -276,7 +285,9 @@ describe('non-versioned objectCopy', () => { undefined, log, next), async () => { sinon.assert.calledWith(metadata.putObjectMD.lastCall, - destBucketName, objectKey, any, sinon.match({ + destBucketName, objectKey, sinon.match({ + _data: { originOp: 's3:ObjectCreated:Copy' }, + }), sinon.match({ needOplogUpdate: undefined, originOp: undefined, }), any, any); @@ -291,7 +302,9 @@ describe('non-versioned objectCopy', () => { undefined, log, next), async () => { sinon.assert.calledWith(metadata.putObjectMD.lastCall, - destBucketName, objectKey, any, sinon.match({ + destBucketName, objectKey, sinon.match({ + _data: { originOp: 's3:ObjectCreated:Copy' }, + }), sinon.match({ needOplogUpdate: undefined, originOp: undefined, }), any, any); diff --git a/tests/unit/api/objectDelete.js b/tests/unit/api/objectDelete.js index b46687ebaf..e204b2f34b 100644 --- a/tests/unit/api/objectDelete.js +++ b/tests/unit/api/objectDelete.js @@ -7,7 +7,7 @@ const services = require('../../../lib/services'); const { bucketPut } = require('../../../lib/api/bucketPut'); const bucketPutACL = require('../../../lib/api/bucketPutACL'); const constants = require('../../../constants'); -const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../helpers'); +const { cleanup, DummyRequestLogger, makeAuthInfo, versioningTestUtils} = require('../helpers'); const objectPut = require('../../../lib/api/objectPut'); const { objectDelete, objectDeleteInternal } = require('../../../lib/api/objectDelete'); const objectGet = require('../../../lib/api/objectGet'); @@ -16,6 +16,7 @@ const mpuUtils = require('../utils/mpuUtils'); const metadataswitch = require('../metadataswitch'); const { fakeMetadataArchive } = require('../../functional/aws-node-sdk/test/utils/init'); const bucketPutNotification = require('../../../lib/api/bucketPutNotification'); +const bucketPutVersioning = require('../../../lib/api/bucketPutVersioning'); const any = sinon.match.any; const originalDeleteObject = services.deleteObject; @@ -32,6 +33,8 @@ const lateDate = new Date(); earlyDate.setMinutes(earlyDate.getMinutes() - 30); lateDate.setMinutes(lateDate.getMinutes() + 30); +const enableVersioningRequest = versioningTestUtils.createBucketPutVersioningReq(bucketName, 'Enabled'); + function testAuth(bucketOwner, authUser, bucketPutReq, objPutReq, objDelReq, log, cb) { bucketPut(bucketOwner, bucketPutReq, log, () => { @@ -371,3 +374,59 @@ describe('objectDelete API', () => { }); }); }); + + +describe('objectDelete API with versioning', () => { + let testPutObjectRequest; + + beforeEach(() => { + cleanup(); + testPutObjectRequest = new DummyRequest({ + bucketName, + namespace, + objectKey, + headers: {}, + url: `/${bucketName}/${objectKey}`, + }, postBody); + + sinon.stub(services, 'deleteObject').callsFake(originalDeleteObject); + sinon.spy(metadataswitch, 'putObjectMD'); + sinon.spy(metadataswitch, 'deleteObjectMD'); + }); + + afterEach(() => { + sinon.restore(); + }); + + const testBucketPutRequest = new DummyRequest({ + bucketName, + namespace, + headers: {}, + url: `/${bucketName}`, + }); + const testDeleteRequest = new DummyRequest({ + bucketName, + namespace, + objectKey, + headers: {}, + url: `/${bucketName}/${objectKey}`, + }); + + it('should store modified originOp when moving object null version', done => { + async.series([ + next => bucketPut(authInfo, testBucketPutRequest, log, next), + next => objectPut(authInfo, testPutObjectRequest, undefined, log, next), + next => bucketPutVersioning(authInfo, enableVersioningRequest, log, next), + next => objectDelete(authInfo, testDeleteRequest, log, next), + async () => { + const calls = metadataswitch.putObjectMD.getCalls(); + sinon.assert.calledWith(calls[calls.length - 2], + bucketName, objectKey, sinon.match({ + versionId: sinon.match.truthy, + isNull: true, + originOp: 's3:StoreNullVersion', + }), any, any, any); + }, + ], done); + }); +}); diff --git a/tests/unit/api/objectPut.js b/tests/unit/api/objectPut.js index 5825d1838b..7f172e8ffc 100644 --- a/tests/unit/api/objectPut.js +++ b/tests/unit/api/objectPut.js @@ -980,6 +980,30 @@ describe('objectPut API with versioning', () => { }); }); + it('should set originOp when moving null version', done => { + async.series([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + next => objectPut(authInfo, testPutObjectRequest, undefined, log, next), + next => bucketPutVersioning(authInfo, enableVersioningRequest, log, next), + next => objectPut(authInfo, testPutObjectRequest, undefined, log, next), + async () => { + // M was moved to V, with originOp overridden to prevent bucket notifications. + const calls = metadata.putObjectMD.getCalls(); + sinon.assert.calledWith(calls[calls.length - 2], + bucketName, objectName, sinon.match({ + originOp: 's3:StoreNullVersion', + }), any, any, any); + }, + async () => { + // New V was created with the right originOp. + sinon.assert.calledWith(metadata.putObjectMD.lastCall, + bucketName, objectName, sinon.match({ + _data: { originOp: 's3:ObjectCreated:Put' }, + }), any, any, any); + }, + ], done); + }); + it('should not pass needOplogUpdate when writing new object', done => { async.series([ next => bucketPut(authInfo, testPutBucketRequest, log, next),