Compare commits

...

4 Commits

2 changed files with 321 additions and 2 deletions

View File

@ -490,16 +490,28 @@ function putMetadata(request, response, bucketInfo, objMd, log, callback) {
omVal[headerName] = objMd[headerName];
});
}
const versionId = decodeVersionId(request.query);
const versioning = bucketInfo.isVersioningEnabled();
const options = {};
if (versionId || omVal.replicationInfo.isNFS) {
// specify both 'versioning' and 'versionId' to create a "new"
// version (updating master as well) but with specified
// versionId
options.versioning = bucketInfo.isVersioningEnabled();
options.versioning = versioning;
options.versionId = versionId;
}
if (versionId === 'null') {
const nullVersionId = objMd && objMd.versionId;
if (versioning && !nullVersionId) {
// If the null version does not have a version id, it is a current null version.
// To update the metadata of a current version, versioning is set to false
options.versioning = false;
}
options.versionId = nullVersionId;
}
// If the object is from a source bucket without versioning (i.e. NFS),
// then we want to create a version for the replica object even though
// none was provided in the object metadata value.

View File

@ -3,7 +3,8 @@ const AWS = require('aws-sdk');
const async = require('async');
const crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');
const { versioning } = require('arsenal');
const { models, versioning } = require('arsenal');
const { ObjectMD } = models;
const versionIdUtils = versioning.VersionID;
const { makeid } = require('../../unit/helpers');
@ -39,6 +40,7 @@ const TEST_BUCKET = 'backbeatbucket';
const TEST_ENCRYPTED_BUCKET = 'backbeatbucket-encrypted';
const TEST_KEY = 'fookey';
const NONVERSIONED_BUCKET = 'backbeatbucket-non-versioned';
const BUCKET_FOR_NULL_VERSION = 'backbeatbucket-null-version';
const testArn = 'aws::iam:123456789012:user/bart';
const testKey = 'testkey';
@ -132,6 +134,21 @@ function checkObjectData(s3, bucket, objectKey, dataValue, done) {
});
}
function updateStorageClass(data, storageClass) {
let parsedBody;
try {
parsedBody = JSON.parse(data.body);
} catch (err) {
return { error: err };
}
const { result, error } = ObjectMD.createFromBlob(parsedBody.Body);
if (error) {
return { error };
}
result.setAmzStorageClass(storageClass);
return { result };
}
/** makeBackbeatRequest - utility function to generate a request going
* through backbeat route
* @param {object} params - params for making request
@ -275,6 +292,296 @@ describe.skip('backbeat routes', () => {
});
describe('backbeat PUT routes', () => {
describe('null version', () => {
beforeEach(done => s3.createBucket({ Bucket: BUCKET_FOR_NULL_VERSION }, done));
afterEach(done => {
bucketUtil.empty(BUCKET_FOR_NULL_VERSION)
.then(() => s3.deleteBucket({ Bucket: BUCKET_FOR_NULL_VERSION }).promise())
.then(() => done());
});
it('should update metadata of a current null version', done => {
const bucket = BUCKET_FOR_NULL_VERSION;
const keyName = 'key0';
const storageClass = 'foo';
let objMD;
return async.series([
next => s3.putObject({ Bucket: bucket, Key: keyName, Body: new Buffer(testData) }, next),
next => s3.putBucketVersioning({ Bucket: bucket, VersioningConfiguration: { Status: 'Enabled' } },
next),
next => makeBackbeatRequest({
method: 'GET',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
}, (err, data) => {
if (err) {
return next(err);
}
const { error, result } = updateStorageClass(data, storageClass);
if (error) {
return next(error);
}
objMD = result;
return next();
}),
next => makeBackbeatRequest({
method: 'PUT',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
requestBody: objMD.getSerialized(),
}, next),
next => s3.headObject({ Bucket: bucket, Key: keyName, VersionId: 'null' }, next),
next => s3.listObjectVersions({ Bucket: bucket }, next),
], (err, data) => {
if (err) {
return done(err);
}
const headObjectRes = data[4];
assert.strictEqual(headObjectRes.VersionId, 'null');
assert.strictEqual(headObjectRes.StorageClass, storageClass);
const listObjectVersionsRes = data[5];
const versions = listObjectVersionsRes.Versions;
assert.strictEqual(versions.length, 1);
const version = versions[0];
assert.strictEqual(version.Key, keyName);
assert.strictEqual(version.VersionId, 'null');
assert.strictEqual(version.StorageClass, storageClass);
return done();
});
});
it('should update metadata of a non-current null version', done => {
const bucket = BUCKET_FOR_NULL_VERSION;
const keyName = 'key0';
const storageClass = 'foo';
let objMD;
let expectedVersionId;
return async.series([
next => s3.putObject({ Bucket: bucket, Key: keyName, Body: new Buffer(testData) }, next),
next => s3.putBucketVersioning({ Bucket: bucket, VersioningConfiguration: { Status: 'Enabled' } },
next),
next => s3.putObject({ Bucket: bucket, Key: keyName, Body: new Buffer(testData) }, (err, data) => {
if (err) {
return next(err);
}
expectedVersionId = data.VersionId;
return next();
}),
next => makeBackbeatRequest({
method: 'GET',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
}, (err, data) => {
if (err) {
return next(err);
}
const { error, result } = updateStorageClass(data, storageClass);
if (error) {
return next(error);
}
objMD = result;
return next();
}),
next => makeBackbeatRequest({
method: 'PUT',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
requestBody: objMD.getSerialized(),
}, next),
next => s3.headObject({ Bucket: bucket, Key: keyName, VersionId: 'null' }, next),
next => s3.listObjectVersions({ Bucket: bucket }, next),
], (err, data) => {
if (err) {
return done(err);
}
const headObjectRes = data[5];
assert.strictEqual(headObjectRes.VersionId, 'null');
assert.strictEqual(headObjectRes.StorageClass, storageClass);
const listObjectVersionsRes = data[6];
const versions = listObjectVersionsRes.Versions;
assert.strictEqual(versions.length, 2);
const currentVersions = versions.filter(v => v.IsLatest);
assert.strictEqual(currentVersions.length, 1);
const currentVersion = currentVersions[0];
assert.strictEqual(currentVersion.Key, keyName);
assert.strictEqual(currentVersion.VersionId, expectedVersionId);
assert.strictEqual(currentVersion.StorageClass, 'STANDARD');
const nonCurrentVersions = versions.filter(v => !v.IsLatest);
assert.strictEqual(nonCurrentVersions.length, 1);
const nonCurrentVersion = nonCurrentVersions[0];
assert.strictEqual(nonCurrentVersion.Key, keyName);
assert.strictEqual(nonCurrentVersion.VersionId, 'null');
assert.strictEqual(nonCurrentVersion.StorageClass, storageClass);
return done();
});
});
it('should update metadata of a non-version object', done => {
const bucket = BUCKET_FOR_NULL_VERSION;
const keyName = 'key0';
const storageClass = 'foo';
let objMD;
return async.series([
next => s3.putObject({ Bucket: bucket, Key: keyName, Body: new Buffer(testData) }, next),
next => makeBackbeatRequest({
method: 'GET',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
}, (err, data) => {
if (err) {
return next(err);
}
const { error, result } = updateStorageClass(data, storageClass);
if (error) {
return next(error);
}
objMD = result;
return next();
}),
next => makeBackbeatRequest({
method: 'PUT',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
requestBody: objMD.getSerialized(),
}, next),
next => s3.headObject({ Bucket: bucket, Key: keyName }, next),
next => s3.listObjectVersions({ Bucket: bucket }, next),
], (err, data) => {
if (err) {
return done(err);
}
const headObjectRes = data[3];
assert(!headObjectRes.VersionId);
assert.strictEqual(headObjectRes.StorageClass, storageClass);
const listObjectVersionsRes = data[4];
const versions = listObjectVersionsRes.Versions;
assert.strictEqual(versions.length, 1);
const currentVersion = versions[0];
assert.strictEqual(currentVersion.Key, keyName);
assert.strictEqual(currentVersion.VersionId, 'null');
assert.strictEqual(currentVersion.StorageClass, storageClass);
return done();
});
});
it('should update metadata of the current null version created after suspending versioning', done => {
const bucket = BUCKET_FOR_NULL_VERSION;
const keyName = 'key0';
const storageClass = 'foo';
let expectedVersionId;
let objMD;
return async.series([
next => s3.putBucketVersioning({ Bucket: bucket, VersioningConfiguration: { Status: 'Enabled' } },
next),
next => s3.putObject({ Bucket: bucket, Key: keyName, Body: new Buffer(testData) }, (err, data) => {
if (err) {
return next(err);
}
expectedVersionId = data.VersionId;
return next();
}),
next => s3.putBucketVersioning({ Bucket: bucket, VersioningConfiguration: { Status: 'Suspended' } },
next),
next => s3.putObject({ Bucket: bucket, Key: keyName, Body: new Buffer(testData) }, next),
next => makeBackbeatRequest({
method: 'GET',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
}, (err, data) => {
if (err) {
return next(err);
}
const { error, result } = updateStorageClass(data, storageClass);
if (error) {
return next(error);
}
objMD = result;
return next();
}),
next => makeBackbeatRequest({
method: 'PUT',
resourceType: 'metadata',
bucket,
objectKey: keyName,
queryObj: {
versionId: 'null',
},
authCredentials: backbeatAuthCredentials,
requestBody: objMD.getSerialized(),
}, next),
next => s3.headObject({ Bucket: bucket, Key: keyName, VersionId: 'null' }, next),
next => s3.listObjectVersions({ Bucket: bucket }, next),
], (err, data) => {
if (err) {
return done(err);
}
const headObjectRes = data[6];
assert.strictEqual(headObjectRes.VersionId, 'null');
assert.strictEqual(headObjectRes.StorageClass, storageClass);
const listObjectVersionsRes = data[7];
const versions = listObjectVersionsRes.Versions;
assert.strictEqual(versions.length, 2);
const currentVersions = versions.filter(v => v.IsLatest);
assert.strictEqual(currentVersions.length, 1);
const currentVersion = currentVersions[0];
assert.strictEqual(currentVersion.Key, keyName);
assert.strictEqual(currentVersion.VersionId, 'null');
assert.strictEqual(currentVersion.StorageClass, storageClass);
const nonCurrentVersions = versions.filter(v => !v.IsLatest);
assert.strictEqual(nonCurrentVersions.length, 1);
const nonCurrentVersion = nonCurrentVersions[0];
assert.strictEqual(nonCurrentVersion.Key, keyName);
assert.strictEqual(nonCurrentVersion.VersionId, expectedVersionId);
assert.strictEqual(nonCurrentVersion.StorageClass, 'STANDARD');
return done();
});
});
});
describe('PUT data + metadata should create a new complete object',
() => {
[{