Skip to content

Commit 686200b

Browse files
committed
Merge branch 'improvement/CLDSRV-561-add-tests-for-createAndStoreObject' into q/9.3
2 parents 942e4bc + a26bb42 commit 686200b

4 files changed

Lines changed: 836 additions & 38 deletions

File tree

tests/functional/aws-node-sdk/test/object/objectOverwrite.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
const assert = require('assert');
22
const {
33
PutObjectCommand,
4+
PutBucketVersioningCommand,
45
HeadObjectCommand,
56
GetObjectCommand,
67
} = require('@aws-sdk/client-s3');
78

89
const withV4 = require('../support/withV4');
910
const BucketUtility = require('../../lib/utility/bucket-util');
11+
const { fakeMetadataArchive, fakeMetadataTransition, getMetadata, initMetadata } = require('../utils/init');
12+
13+
const coldStateScenarios = [
14+
{
15+
name: 'transition in progress',
16+
transitionInProgress: true,
17+
},
18+
{
19+
name: 'archived',
20+
archiveState: {
21+
archiveInfo: { archiveId: 'archive-1' },
22+
restoreRequestedAt: new Date(0).toISOString(),
23+
restoreRequestedDays: 5,
24+
},
25+
},
26+
{
27+
name: 'restored (not expired)',
28+
archiveState: {
29+
archiveInfo: { archiveId: 'archive-restored' },
30+
restoreRequestedAt: new Date(0).toISOString(),
31+
restoreRequestedDays: 5,
32+
restoreCompletedAt: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
33+
restoreWillExpireAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
34+
},
35+
},
36+
{
37+
name: 'restored (expired)',
38+
archiveState: {
39+
archiveInfo: { archiveId: 'archive-expired' },
40+
restoreRequestedAt: new Date(0).toISOString(),
41+
restoreRequestedDays: 5,
42+
restoreCompletedAt: new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString(),
43+
restoreWillExpireAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
44+
},
45+
},
46+
];
1047

1148
const objectName = 'someObject';
1249
const firstPutMetadata = {
@@ -30,6 +67,7 @@ describe('Put object with same key as prior object', () => {
3067
bucketUtil = new BucketUtility('default', sigCfg);
3168
s3 = bucketUtil.s3;
3269
bucketName = await bucketUtil.createRandom(1);
70+
await initMetadata();
3371
});
3472

3573
beforeEach(async () => {
@@ -66,5 +104,109 @@ describe('Put object with same key as prior object', () => {
66104
const bodyText = await res.Body.transformToString();
67105
assert.deepStrictEqual(bodyText, 'Much different');
68106
});
107+
108+
coldStateScenarios.forEach(({ name, transitionInProgress, archiveState }) => {
109+
it(`should replace object with cold-state metadata (${name}) in non-versioned bucket`, async () => {
110+
if (transitionInProgress) {
111+
await fakeMetadataTransition(bucketName, objectName, undefined);
112+
} else {
113+
await fakeMetadataArchive(bucketName, objectName, undefined, archiveState);
114+
}
115+
116+
await s3.send(new PutObjectCommand({
117+
Bucket: bucketName,
118+
Key: objectName,
119+
Body: `overwrite cold state ${name}`,
120+
Metadata: secondPutMetadata,
121+
}));
122+
123+
const currentMD = await getMetadata(bucketName, objectName, undefined);
124+
assert.strictEqual(currentMD.archive, undefined);
125+
assert.strictEqual(currentMD['x-amz-scal-transition-in-progress'], undefined);
126+
});
127+
});
128+
129+
it('should create a new version when replacing archived current object in versioned bucket', async () => {
130+
await bucketUtil.empty(bucketName);
131+
132+
await s3.send(new PutBucketVersioningCommand({
133+
Bucket: bucketName,
134+
VersioningConfiguration: { Status: 'Enabled' },
135+
}));
136+
137+
const firstPutRes = await s3.send(new PutObjectCommand({
138+
Bucket: bucketName,
139+
Key: objectName,
140+
Body: 'versioned first payload',
141+
Metadata: firstPutMetadata,
142+
}));
143+
assert(firstPutRes.VersionId);
144+
145+
await fakeMetadataArchive(bucketName, objectName, undefined, {
146+
archiveInfo: { archiveId: 'archive-versioned-current' },
147+
restoreRequestedAt: new Date(0).toISOString(),
148+
restoreRequestedDays: 5,
149+
});
150+
151+
const secondPutRes = await s3.send(new PutObjectCommand({
152+
Bucket: bucketName,
153+
Key: objectName,
154+
Body: 'versioned second payload',
155+
Metadata: secondPutMetadata,
156+
}));
157+
assert(secondPutRes.VersionId);
158+
assert.notStrictEqual(secondPutRes.VersionId, firstPutRes.VersionId);
159+
160+
const headRes = await s3.send(new HeadObjectCommand({
161+
Bucket: bucketName,
162+
Key: objectName,
163+
}));
164+
assert.deepStrictEqual(headRes.Metadata, secondPutMetadata);
165+
166+
const currentMD = await getMetadata(bucketName, objectName, undefined);
167+
assert.strictEqual(currentMD.archive, undefined);
168+
});
169+
170+
it('should replace archived current null version in version-suspended bucket', async () => {
171+
await bucketUtil.empty(bucketName);
172+
173+
await s3.send(new PutBucketVersioningCommand({
174+
Bucket: bucketName,
175+
VersioningConfiguration: { Status: 'Enabled' },
176+
}));
177+
await s3.send(new PutObjectCommand({
178+
Bucket: bucketName,
179+
Key: objectName,
180+
Body: 'enabled-version-payload',
181+
}));
182+
await s3.send(new PutBucketVersioningCommand({
183+
Bucket: bucketName,
184+
VersioningConfiguration: { Status: 'Suspended' },
185+
}));
186+
187+
await s3.send(new PutObjectCommand({
188+
Bucket: bucketName,
189+
Key: objectName,
190+
Body: 'null-current-before-archive',
191+
}));
192+
193+
await fakeMetadataArchive(bucketName, objectName, undefined, {
194+
archiveInfo: { archiveId: 'archive-null-current' },
195+
restoreRequestedAt: new Date(0).toISOString(),
196+
restoreRequestedDays: 5,
197+
});
198+
199+
await s3.send(new PutObjectCommand({
200+
Bucket: bucketName,
201+
Key: objectName,
202+
Body: 'replace archived null current',
203+
Metadata: secondPutMetadata,
204+
}));
205+
206+
const currentMD = await getMetadata(bucketName, objectName, undefined);
207+
assert.strictEqual(currentMD.archive, undefined);
208+
assert.deepStrictEqual(currentMD['x-amz-meta-secondput'], secondPutMetadata.secondput);
209+
});
210+
69211
});
70212
});

tests/functional/aws-node-sdk/test/object/putVersion.js

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const archive = {
3434
restoreRequestedDays: 5,
3535
};
3636

37-
async function putObjectVersion(s3, params, vid, next) {
37+
function putObjectVersion(s3, params, vid, cb) {
3838
const paramsWithBody = { ...params, Body: '123' };
3939
const command = new PutObjectCommand(paramsWithBody);
4040
command.middlewareStack.add(
@@ -48,13 +48,9 @@ async function putObjectVersion(s3, params, vid, next) {
4848
name: 'addVersionIdHeader', // Add a name to identify the middleware
4949
}
5050
);
51-
52-
try {
53-
const res = await s3.send(command);
54-
next(null, res);
55-
} catch (err) {
56-
next(err);
57-
}
51+
52+
const promise = s3.send(command);
53+
return cb ? promise.then(res => cb(null, res), cb) : promise;
5854
}
5955

6056
function clearRestoreStatus(versions) {
@@ -956,6 +952,68 @@ describe('PUT object with x-scal-s3-version-id header', () => {
956952
},
957953
], done);
958954
});
955+
956+
it('should set restore originOp and drop restore-attempt metadata', done => {
957+
const params = { Bucket: bucketName, Key: objectName };
958+
959+
async.series([
960+
next => s3.send(new PutObjectCommand({
961+
...params,
962+
Metadata: {
963+
'custom-md': 'preserved-value',
964+
},
965+
})).then(() => next()).catch(next),
966+
next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next),
967+
next => getMetadata(bucketName, objectName, undefined, (err, objMD) => {
968+
if (err) {
969+
return next(err);
970+
}
971+
/* eslint-disable no-param-reassign */
972+
objMD['x-amz-meta-scal-s3-restore-attempt'] = '3';
973+
/* eslint-enable no-param-reassign */
974+
return metadata.putObjectMD(bucketName, objectName, objMD, undefined, log, next);
975+
}),
976+
next => putObjectVersion(s3, params, '', next),
977+
next => getMetadata(bucketName, objectName, undefined, (err, objMD) => {
978+
if (err) {
979+
return next(err);
980+
}
981+
assert.strictEqual(objMD.originOp, 's3:ObjectRestore:Completed');
982+
assert.strictEqual(objMD['x-amz-meta-custom-md'], 'preserved-value');
983+
assert.strictEqual(objMD['x-amz-meta-scal-s3-restore-attempt'], undefined);
984+
return next();
985+
}),
986+
], done);
987+
});
988+
989+
it('should keep x-amz-meta-scal-version-id when restoring on ingestion bucket', async () => {
990+
const ingestionBucketName = `ingestion-restore-${Date.now()}`;
991+
const params = { Bucket: ingestionBucketName, Key: objectName };
992+
let putVersionId;
993+
try {
994+
await s3.send(new CreateBucketCommand({
995+
Bucket: ingestionBucketName,
996+
CreateBucketConfiguration: {
997+
LocationConstraint: 'us-east-2:ingest',
998+
},
999+
}));
1000+
1001+
const putRes = await s3.send(new PutObjectCommand(params));
1002+
putVersionId = putRes.VersionId;
1003+
1004+
await fakeMetadataArchive(ingestionBucketName, objectName, putVersionId, archive);
1005+
1006+
await putObjectVersion(s3, params, putVersionId);
1007+
1008+
const restoredObjMD = await getMetadata(
1009+
ingestionBucketName, objectName, putVersionId);
1010+
1011+
assert.strictEqual(restoredObjMD['x-amz-meta-scal-version-id'], putVersionId);
1012+
} finally {
1013+
await bucketUtil.emptyMany([ingestionBucketName]).catch(() => {});
1014+
await bucketUtil.deleteMany([ingestionBucketName]).catch(() => {});
1015+
}
1016+
});
9591017
});
9601018
});
9611019
});

tests/functional/aws-node-sdk/test/utils/init.js

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,33 @@ function decodeVersionId(versionId) {
3838

3939
let metadataInit = false;
4040

41-
function initMetadata(done) {
42-
if (metadataInit === true) {
43-
return done();
44-
}
45-
return metadata.setup(err => {
46-
if (err) {
47-
return done(err);
41+
function initMetadata(cb) {
42+
const promise = (async () => {
43+
if (metadataInit) {
44+
return;
4845
}
49-
metadataInit = true;
50-
return done();
51-
});
46+
await new Promise((resolve, reject) => {
47+
metadata.setup(err => {
48+
if (err) {
49+
return reject(err);
50+
}
51+
metadataInit = true;
52+
return resolve();
53+
});
54+
});
55+
})();
56+
return cb ? promise.then(() => cb(), cb) : promise;
5257
}
5358

5459
function getMetadata(bucketName, objectName, versionId, cb) {
55-
return metadata.getObjectMD(bucketName, objectName, { versionId: decodeVersionId(versionId) },
56-
log, cb);
60+
const promise = new Promise((resolve, reject) => metadata.getObjectMD(
61+
bucketName,
62+
objectName,
63+
{ versionId: decodeVersionId(versionId) },
64+
log,
65+
(err, data) => (err ? reject(err) : resolve(data)),
66+
));
67+
return cb ? promise.then(res => cb(null, res), cb) : promise;
5768
}
5869

5970
/**
@@ -66,16 +77,19 @@ function getMetadata(bucketName, objectName, versionId, cb) {
6677
* @returns {undefined}
6778
*/
6879
function fakeMetadataTransition(bucketName, objectName, versionId, cb) {
69-
return getMetadata(bucketName, objectName, versionId, (err, objMD) => {
70-
if (err) {
71-
return cb(err);
72-
}
73-
/* eslint-disable no-param-reassign */
80+
const promise = (async () => {
81+
const objMD = await getMetadata(bucketName, objectName, versionId);
7482
objMD['x-amz-scal-transition-in-progress'] = true;
75-
/* eslint-enable no-param-reassign */
76-
return metadata.putObjectMD(bucketName, objectName, objMD, { versionId: decodeVersionId(versionId) },
77-
log, err => cb(err));
78-
});
83+
await new Promise((resolve, reject) => metadata.putObjectMD(
84+
bucketName,
85+
objectName,
86+
objMD,
87+
{ versionId: decodeVersionId(versionId) },
88+
log,
89+
err => (err ? reject(err) : resolve()),
90+
));
91+
})();
92+
return cb ? promise.then(() => cb(), cb) : promise;
7993
}
8094

8195
/**
@@ -89,18 +103,21 @@ function fakeMetadataTransition(bucketName, objectName, versionId, cb) {
89103
* @returns {undefined}
90104
*/
91105
function fakeMetadataArchive(bucketName, objectName, versionId, archive, cb) {
92-
return getMetadata(bucketName, objectName, versionId, (err, objMD) => {
93-
if (err) {
94-
return cb(err);
95-
}
96-
/* eslint-disable no-param-reassign */
106+
const promise = (async () => {
107+
const objMD = await getMetadata(bucketName, objectName, versionId);
97108
objMD['x-amz-storage-class'] = LOCATION_NAME_DMF;
98109
objMD.dataStoreName = LOCATION_NAME_DMF;
99110
objMD.archive = archive;
100-
/* eslint-enable no-param-reassign */
101-
return metadata.putObjectMD(bucketName, objectName, objMD, { versionId: decodeVersionId(versionId) },
102-
log, err => cb(err));
103-
});
111+
await new Promise((resolve, reject) => metadata.putObjectMD(
112+
bucketName,
113+
objectName,
114+
objMD,
115+
{ versionId: decodeVersionId(versionId) },
116+
log,
117+
err => (err ? reject(err) : resolve()),
118+
));
119+
})();
120+
return cb ? promise.then(() => cb(), cb) : promise;
104121
}
105122

106123
module.exports = {

0 commit comments

Comments
 (0)