Skip to content

Commit

Permalink
Add tests for copyObject in OOB bucket
Browse files Browse the repository at this point in the history
Issue: CLDSRV-563
  • Loading branch information
francoisferrand committed Jan 10, 2025
1 parent f95d3c9 commit f2b2305
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/api/objectCopy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const constants = require('../../constants');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const locationConstraintCheck
= require('./apiUtils/object/locationConstraintCheck');
const { checkQueryVersionId, versioningPreprocessing }
const { checkQueryVersionId, versioningPreprocessing, decodeVID }
= require('./apiUtils/object/versioning');
const getReplicationInfo = require('./apiUtils/object/getReplicationInfo');
const { data } = require('../data/wrapper');
Expand Down
127 changes: 126 additions & 1 deletion tests/unit/api/objectCopy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const assert = require('assert');
const async = require('async');
const { storage } = require('arsenal');
const { storage, versioning } = require('arsenal');
const sinon = require('sinon');

const { bucketPut } = require('../../../lib/api/bucketPut');
Expand All @@ -12,6 +12,8 @@ const { cleanup, DummyRequestLogger, makeAuthInfo, versioningTestUtils }
= require('../helpers');
const mpuUtils = require('../utils/mpuUtils');
const metadata = require('../metadataswitch');
const { data } = require('../../../lib/data/wrapper');
const { objectLocationConstraintHeader } = require('../../../constants');

const any = sinon.match.any;

Expand Down Expand Up @@ -247,3 +249,126 @@ describe('objectCopy overheadField', () => {
});
});
});

describe('objectCopy in ingestion bucket', () => {
const dataClient = data.client;
const prevDataImplName = data.implName;
const prevConfigBackendsData = data.config.backends.data;
const prevConfigLocationConstraints1Type = data.config.locationConstraints['us-east-1'].type;
const prevConfigLocationConstraints2Type = data.config.locationConstraints['us-east-2'].type;

before(() => {
// Setup multi-backend, this is required for ingestion
data.switch(new storage.data.MultipleBackendGateway({
'us-east-1': dataClient,
'us-east-2': dataClient,
}, metadata, data.locStorageCheckFn));
data.implName = 'multipleBackends';

// "mock" the data location, simulating a backend supporting server-side copy
data.config.backends.data = 'multiple';
data.config.locationConstraints['us-east-1'].type = 'aws_s3';
data.config.locationConstraints['us-east-2'].type = 'aws_s3';
});

after(() => {
data.switch(dataClient);
data.implName = prevDataImplName;
data.config.backends.data = prevConfigBackendsData;
data.config.locationConstraints['us-east-1'].type = prevConfigLocationConstraints1Type;
data.config.locationConstraints['us-east-2'].type = prevConfigLocationConstraints2Type;
});

const versionIDs = [];

beforeEach(() => {
cleanup();

sinon.stub(dataClient, 'put').callsFake((writeStream, size, keyContext, reqUids, cb) => {
const versionID = versioning.VersionID.encode(versioning.VersionID.generateVersionId('0', ''));
versionIDs.push(versionID);
cb(null, `${keyContext.bucketName}/${keyContext.objectKey}`, versionID, size, 'md5');
});
});

afterEach(() => {
sinon.restore();
});

const newPutIngestBucketRequest = location => new DummyRequest({
bucketName: destBucketName,
namespace,
headers: { host: `${destBucketName}.s3.amazonaws.com` },
url: '/',
post: '<?xml version="1.0" encoding="UTF-8"?>' +
'<CreateBucketConfiguration ' +
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
`<LocationConstraint>${location}</LocationConstraint>` +
'</CreateBucketConfiguration>',
});
const putSourceObjectRequest = versioningTestUtils.createPutObjectRequest(
sourceBucketName, objectKey, objData[0]);
const newPutObjectRequest = params => {
const { location } = params || {};
const r = _createObjectCopyRequest(destBucketName);
if (location) {
r.headers[objectLocationConstraintHeader] = location;

// Need to 'replace' the metadata for the constraint to be taken into account
r.headers['x-amz-metadata-directive'] = 'REPLACE';
}
return r;
};

it('should use the versionID from the backend', done => {
const versionID = versioning.VersionID.encode(versioning.VersionID.generateVersionId('0', ''));
dataClient.copyObject = sinon.stub().yields(null, objectKey, versionID);

async.series([
next => bucketPut(authInfo, putSourceBucketRequest, log, next),
next => bucketPut(authInfo, newPutIngestBucketRequest('us-east-1:ingest'), log, next),
next => objectPut(authInfo, putSourceObjectRequest, undefined, log, next),
next => objectCopy(authInfo, newPutObjectRequest(), sourceBucketName, objectKey, undefined, log,
(err, xml, headers) => {
assert.ifError(err);
assert.strictEqual(headers['x-amz-version-id'], versionID);
next();
}),
], done);
});

it('should not use the versionID from the backend when writing in another location', done => {
const versionID = versioning.VersionID.encode(versioning.VersionID.generateVersionId('0', ''));
dataClient.copyObject = sinon.stub().yields(null, objectKey, versionID);

const copyObjectRequest = newPutObjectRequest({ location: 'us-east-2' });
async.series([
next => bucketPut(authInfo, putSourceBucketRequest, log, next),
next => bucketPut(authInfo, newPutIngestBucketRequest('us-east-1:ingest'), log, next),
next => objectPut(authInfo, putSourceObjectRequest, undefined, log, next),
next => objectCopy(authInfo, copyObjectRequest, sourceBucketName, objectKey, undefined, log,
(err, xml, headers) => {
assert.ifError(err);
assert.notEqual(headers['x-amz-version-id'], versionID);
next();
}),
], done);
});

it('should not use the versionID from the backend when it is not a valid versionID', done => {
const versionID = undefined;
dataClient.copyObject = sinon.stub().yields(null, objectKey, versionID);

async.series([
next => bucketPut(authInfo, putSourceBucketRequest, log, next),
next => bucketPut(authInfo, newPutIngestBucketRequest('us-east-1:ingest'), log, next),
next => objectPut(authInfo, putSourceObjectRequest, undefined, log, next),
next => objectCopy(authInfo, newPutObjectRequest(), sourceBucketName, objectKey, undefined, log,
(err, xml, headers) => {
assert.ifError(err);
assert.notEqual(headers['x-amz-version-id'], versionID);
next();
}),
], done);
});
});

0 comments on commit f2b2305

Please sign in to comment.