Skip to content

Commit

Permalink
Add test for archive object replacement
Browse files Browse the repository at this point in the history
Add replacement with MPU or copyObject.

Issue: ZENKO-4928
  • Loading branch information
francoisferrand committed Nov 14, 2024
1 parent c230f9a commit cf777ca
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 2 deletions.
23 changes: 21 additions & 2 deletions tests/ctst/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import assert from 'assert';
import { Admin, Kafka } from 'kafkajs';
import {
createBucketWithConfiguration,
putMpuObject,
copyObject,
putObject,
runActionAgainstBucket,
getObjectNameWithBackendFlakiness,
Expand Down Expand Up @@ -62,7 +64,7 @@ export async function cleanS3Bucket(
}

async function addMultipleObjects(this: Zenko, numberObjects: number,
objectName: string, sizeBytes: number, userMD?: string) {
objectName: string, sizeBytes: number, userMD?: string, parts?: number) {
let lastResult = null;
for (let i = 1; i <= numberObjects; i++) {
this.resetCommand();
Expand All @@ -74,7 +76,9 @@ async function addMultipleObjects(this: Zenko, numberObjects: number,
if (userMD) {
this.addToSaved('userMetadata', userMD);
}
lastResult = await putObject(this, objectNameFinal);
lastResult = parts === undefined
? await putObject(this, objectNameFinal)
: await putMpuObject(this, parts, objectNameFinal);
}
return lastResult;
}
Expand Down Expand Up @@ -147,6 +151,16 @@ Given('{int} objects {string} of size {int} bytes',
await addMultipleObjects.call(this, numberObjects, objectName, sizeBytes);
});

Given('{int} mpu objects {string} of size {int} bytes',
async function (this: Zenko, numberObjects: number, objectName: string, sizeBytes: number) {
await addMultipleObjects.call(this, numberObjects, objectName, sizeBytes, undefined, 3);
});

Given('{string} is copied to {string}',
async function (this: Zenko, sourceObject: string, destinationObject: string) {
await copyObject(this, sourceObject, destinationObject);
});

Given('{int} objects {string} of size {int} bytes on {string} site',
async function (this: Zenko, numberObjects: number, objectName: string, sizeBytes: number, site: string) {
this.resetCommand();
Expand All @@ -164,6 +178,11 @@ Given('{int} objects {string} of size {int} bytes with user metadata {string}',
await addMultipleObjects.call(this, numberObjects, objectName, sizeBytes, userMD);
});

Given('{int} mpu objects {string} of size {int} bytes with user metadata {string}',
async function (this: Zenko, numberObjects: number, objectName: string, sizeBytes: number, userMD: string) {
await addMultipleObjects.call(this, numberObjects, objectName, sizeBytes, userMD);
});

Given('a tag on object {string} with key {string} and value {string}',
async function (this: Zenko, objectName: string, tagKey: string, tagValue: string) {
this.resetCommand();
Expand Down
41 changes: 41 additions & 0 deletions tests/ctst/features/dmf.feature
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,44 @@ Feature: DMF
| versioningConfiguration | objectCount | objectSize |
| Non versioned | 1 | 100 |
| Suspended | 1 | 100 |

@2.7.0
@PreMerge
@Dmf
@ColdStorage
Scenario Outline: Overwriting of a cold mpu object
Given a "<versioningConfiguration>" bucket
And a transition workflow to "e2e-cold" location
And <objectCount> objects "obj" of size <objectSize> bytes
Then object "obj-1" should be "transitioned" and have the storage class "e2e-cold"
And dmf volume should contain <objectCount> objects
Given <objectCount> mpu objects "obj" of size <objectSize> bytes
Then object "obj-1" should be "transitioned" and have the storage class "e2e-cold"
And dmf volume should contain 1 objects

Examples:
| versioningConfiguration | objectCount | objectSize |
| Non versioned | 1 | 100 |
| Suspended | 1 | 100 |

@2.7.0
@PreMerge
@Dmf
@ColdStorage
Scenario Outline: Overwriting of a cold object with copyObject
Given a "<versioningConfiguration>" bucket
And a transition workflow to "e2e-cold" location
And 2 objects "obj" of size <objectSize> bytes
Then object "obj-1" should be "transitioned" and have the storage class "e2e-cold"
And object "obj-2" should be "transitioned" and have the storage class "e2e-cold"
And dmf volume should contain 2 objects
When i restore object "obj-1" for 5 days
Then object "obj-1" should be "restored" and have the storage class "e2e-cold"
Given "obj-1" is copied to "obj-2"
Then object "obj-2" should be "transitioned" and have the storage class "e2e-cold"
And dmf volume should contain 2 objects

Examples:
| versioningConfiguration | objectSize |
| Non versioned | 100 |
| Suspended | 100 |
80 changes: 80 additions & 0 deletions tests/ctst/steps/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,84 @@ async function createBucketWithConfiguration(
}
}

async function putMpuObject(world: Zenko, parts: number = 2, objectName: string, content?: string) {
const key = objectName || `${Utils.randomString()}`;
const bucket = world.getSaved<string>('bucketName');

world.resetCommand();
world.addToSaved('objectName', objectName);
world.logger.debug('Adding mpu object', { objectName });
world.addCommandParameter({ key });
world.addCommandParameter({ bucket });
const userMetadata = world.getSaved<string>('userMetadata');
if (userMetadata) {
world.addCommandParameter({ metadata: JSON.stringify(userMetadata) });
}

const initiateMPUResult = await S3.createMultipartUpload(world.getCommandParameters());
assert.ifError(initiateMPUResult.stderr || initiateMPUResult.err);
const uploadId = extractPropertyFromResults<string>(initiateMPUResult, 'UploadId');

await uploadSetup(world, 'UploadPart', content);
const body = world.getSaved<string>('tempFileName');

const uploadedParts = [];
for (let i = 0; i < parts; i++) {
world.resetCommand();
world.addCommandParameter({ key });
world.addCommandParameter({ bucket });
world.addCommandParameter({ partNumber: i+1 });
world.addCommandParameter({ uploadId });
if (body) {
world.addCommandParameter({ body });
}

const uploadPartResult = await S3.uploadPart(world.getCommandParameters());
assert.ifError(uploadPartResult.stderr || uploadPartResult.err);

uploadedParts.push({
ETag: extractPropertyFromResults<string>(uploadPartResult, 'ETag'),
PartNumber: (i+1).toString(),
});
}

await uploadTeardown(world, 'UploadPart');

world.resetCommand();
world.addCommandParameter({ key });
world.addCommandParameter({ bucket });
world.addCommandParameter({ uploadId });
world.addCommandParameter({ multipartUpload: JSON.stringify({ Parts: uploadedParts }) });

const result = await S3.completeMultipartUpload(world.getCommandParameters());
const versionId = extractPropertyFromResults<string>(result, 'VersionId');
world.saveCreatedObject(objectName, versionId || '');
world.setResult(result);
return result;
}

async function copyObject(world: Zenko, srcObjectName?: string, dstObjectName?: string) {
const bucket = world.getSaved<string>('bucketName');
const key = dstObjectName || world.getSaved<string>('objectName');
const copySource = `${bucket}/${srcObjectName || world.getSaved<string>('objectName')}`;

world.resetCommand();
world.addCommandParameter({ copySource });
world.addCommandParameter({ bucket });
world.addCommandParameter({ key });

const userMetadata = world.getSaved<string>('userMetadata');
if (userMetadata) {
world.addCommandParameter({ metadata: JSON.stringify(userMetadata) });
}

const result = await S3.copyObject(world.getCommandParameters());
const versionId = extractPropertyFromResults<string>(result, 'VersionId');
world.saveCreatedObject(key, versionId || '');
world.setResult(result);
return result;
}

async function putObject(world: Zenko, objectName?: string, content?: string) {
world.resetCommand();
let finalObjectName = objectName;
Expand Down Expand Up @@ -394,6 +472,8 @@ export {
runActionAgainstBucket,
createBucketWithConfiguration,
getAuthorizationConfiguration,
putMpuObject,
copyObject,
putObject,
emptyNonVersionedBucket,
emptyVersionedBucket,
Expand Down

0 comments on commit cf777ca

Please sign in to comment.