Skip to content

Commit b6d9035

Browse files
Merge branch 'bugfix/BB-722' into w/9.0/bugfix/BB-722
2 parents 097e0bc + b5cb688 commit b6d9035

File tree

5 files changed

+164
-3
lines changed

5 files changed

+164
-3
lines changed

extensions/lifecycle/tasks/LifecycleTask.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1649,7 +1649,7 @@ class LifecycleTask extends BackbeatTask {
16491649
return done();
16501650
}
16511651

1652-
if (rules.NoncurrentVersionTransition) {
1652+
if (rules.NoncurrentVersionTransition && !this._isDeleteMarker(version)) {
16531653
return this._checkAndApplyNCVTransitionRule(bucketData, version, rules, log, done);
16541654
}
16551655

extensions/lifecycle/tasks/LifecycleTaskV2.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ class LifecycleTaskV2 extends LifecycleTask {
338338
this._checkAndApplyExpirationRule(bucketData, obj, rules, log)) {
339339
return process.nextTick(cb);
340340
}
341-
if (rules.Transition) {
341+
if (rules.Transition && !this._isDeleteMarker(obj)) {
342342
return this._applyTransitionRule({
343343
owner: bucketData.target.owner,
344344
accountId: bucketData.target.accountId,
@@ -384,7 +384,7 @@ class LifecycleTaskV2 extends LifecycleTask {
384384
return process.nextTick(cb);
385385
}
386386

387-
if (rules.NoncurrentVersionTransition) {
387+
if (rules.NoncurrentVersionTransition && !this._isDeleteMarker(obj)) {
388388
return this._checkAndApplyNCVTransitionRule(bucketData, obj, rules, log, cb);
389389
}
390390

tests/functional/lifecycle/LifecycleTask.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,48 @@ describe('lifecycle task functional tests', function dF() {
18691869
});
18701870
});
18711871
});
1872+
1873+
it('should not transition a non-current delete marker with NoncurrentVersionTransition rule', done => {
1874+
const bucket = 'test-bucket';
1875+
const keyName = 'test-key';
1876+
const bucketEntry = {
1877+
action: 'testing-noncurrent-transition',
1878+
target: {
1879+
bucket,
1880+
owner: OWNER,
1881+
},
1882+
details: {},
1883+
};
1884+
const params = {
1885+
lcTask,
1886+
lcp,
1887+
counter: 0,
1888+
};
1889+
async.waterfall([
1890+
next => s3Helper.setAndCreateBucket(bucket, next),
1891+
next => s3Helper.setBucketVersioning('Enabled', next),
1892+
(data, next) => s3.putObject({ Bucket: bucket, Key: keyName, Body: 'test-content' }, next),
1893+
(data, next) => s3.deleteObject({ Bucket: bucket, Key: keyName }, next),
1894+
(data, next) => s3.putObject({ Bucket: bucket, Key: keyName, Body: 'new-content' }, next),
1895+
(data, next) => s3Helper.setBucketLifecycleConfigurations([
1896+
new LifecycleRule().addID('task-1').addNCVTransitions([{
1897+
NoncurrentDays: 0,
1898+
StorageClass: 'us-east-2',
1899+
}]).build(),
1900+
], next),
1901+
(data, next) => s3.getBucketLifecycleConfiguration({ Bucket: bucket }, next),
1902+
(data, next) => wrapProcessBucketEntry(data.Rules, bucketEntry, s3, params, (err, data) => {
1903+
assert.ifError(err);
1904+
1905+
assertTransitionResult({ data, expectedKeys: [] });
1906+
1907+
next();
1908+
}),
1909+
], err => {
1910+
assert.ifError(err);
1911+
done();
1912+
});
1913+
});
18721914
}); // end versioned describe block
18731915

18741916
describe('incomplete mpu objects', () => {

tests/functional/lifecycle/LifecycleTaskV2-versioned.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,46 @@ describe('LifecycleTaskV2 with bucket versioned', () => {
427427
});
428428
});
429429

430+
it('should not publish any object entry if object is a non-current delete marker with NCVT rule', done => {
431+
const transitionRule = [
432+
{
433+
NoncurrentVersionTransitions: [{ NoncurrentDays: 2, StorageClass: destinationLocation }],
434+
ID: '123',
435+
Prefix: '',
436+
Status: 'Enabled',
437+
}
438+
];
439+
const keyName = 'key1';
440+
const versionId = 'versionid1';
441+
// Create a delete marker (no ETag, no Size, no StorageClass)
442+
const deleteMarker = keyMock.nonCurrent({ keyName, versionId, daysEarlier: 2 });
443+
delete deleteMarker.ETag;
444+
delete deleteMarker.Size;
445+
delete deleteMarker.StorageClass;
446+
447+
const contents = [deleteMarker];
448+
backbeatMetadataProxy.listLifecycleResponse = { contents, isTruncated: false, markerInfo: {} };
449+
450+
const nbRetries = 0;
451+
return lifecycleTask.processBucketEntry(transitionRule, bucketData, s3,
452+
backbeatMetadataProxy, nbRetries, err => {
453+
assert.ifError(err);
454+
// test that the non-current listing is triggered
455+
assert.strictEqual(backbeatMetadataProxy.listLifecycleType, 'noncurrent');
456+
457+
// test parameters used to list lifecycle keys
458+
const { listLifecycleParams } = backbeatMetadataProxy;
459+
assert.strictEqual(listLifecycleParams.Bucket, bucketName);
460+
assert.strictEqual(listLifecycleParams.Prefix, '');
461+
assert(!!listLifecycleParams.BeforeDate);
462+
assert.strictEqual(listLifecycleParams.ExcludedDataStoreName, destinationLocation);
463+
464+
// test that no entries are pushed to kafka topic (delete markers cannot be transitioned)
465+
assert.strictEqual(kafkaEntries.length, 0);
466+
return done();
467+
});
468+
});
469+
430470
it('should publish one bucket entry to list orphan delete markers if Expiration rule is set', done => {
431471
const expirationRule = [
432472
{
@@ -464,6 +504,45 @@ describe('LifecycleTaskV2 with bucket versioned', () => {
464504
});
465505
});
466506

507+
it('should not publish any object entry if object is a delete marker if Transition rule is set', done => {
508+
const transitionRule = [
509+
{
510+
Transitions: [{ Days: 2, StorageClass: destinationLocation }],
511+
ID: '123',
512+
Prefix: '',
513+
Status: 'Enabled',
514+
}
515+
];
516+
const keyName = 'key1';
517+
// Create a delete marker (no ETag, no StorageClass properties)
518+
const deleteMarker = keyMock.current({ keyName, daysEarlier: 2 });
519+
delete deleteMarker.ETag;
520+
delete deleteMarker.Size;
521+
delete deleteMarker.StorageClass;
522+
523+
const contents = [deleteMarker];
524+
backbeatMetadataProxy.listLifecycleResponse = { contents, isTruncated: false, markerInfo: {} };
525+
526+
const nbRetries = 0;
527+
return lifecycleTask.processBucketEntry(
528+
transitionRule, bucketData, s3, backbeatMetadataProxy, nbRetries, err => {
529+
assert.ifError(err);
530+
// test that the current listing is triggered
531+
assert.strictEqual(backbeatMetadataProxy.listLifecycleType, 'current');
532+
533+
// test parameters used to list lifecycle keys
534+
const { listLifecycleParams } = backbeatMetadataProxy;
535+
assert.strictEqual(listLifecycleParams.Bucket, bucketName);
536+
assert.strictEqual(listLifecycleParams.Prefix, '');
537+
assert(!!listLifecycleParams.BeforeDate);
538+
assert.strictEqual(listLifecycleParams.ExcludedDataStoreName, destinationLocation);
539+
540+
// test that no entries are pushed to kafka topic (delete markers cannot be transitioned)
541+
assert.strictEqual(kafkaEntries.length, 0);
542+
return done();
543+
});
544+
});
545+
467546
it('should publish one object entry if object is eligible with Transitions rule', done => {
468547
const transitionRule = [
469548
{

tests/functional/lifecycle/LifecycleTaskV2.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,46 @@ describe('LifecycleTaskV2 with bucket non-versioned', () => {
613613
});
614614
});
615615

616+
it('should not publish any object entry if object is a delete marker with Transitions rule', done => {
617+
const transitionRule = [
618+
{
619+
Transitions: [{ Days: 2, StorageClass: destinationLocation }],
620+
ID: '123',
621+
Prefix: '',
622+
Status: 'Enabled',
623+
}
624+
];
625+
626+
const keyName = 'key1';
627+
// Create a delete marker (no ETag, no StorageClass properties)
628+
const deleteMarker = keyMock.current({ keyName, daysEarlier: 2 });
629+
delete deleteMarker.ETag;
630+
delete deleteMarker.Size;
631+
delete deleteMarker.StorageClass;
632+
633+
const contents = [deleteMarker];
634+
backbeatMetadataProxy.listLifecycleResponse = { contents, isTruncated: false, markerInfo: {} };
635+
636+
const nbRetries = 0;
637+
return lifecycleTask.processBucketEntry(
638+
transitionRule, bucketData, s3, backbeatMetadataProxy, nbRetries, err => {
639+
assert.ifError(err);
640+
// test that the current listing is triggered
641+
assert.strictEqual(backbeatMetadataProxy.listLifecycleType, 'current');
642+
643+
// test parameters used to list lifecycle keys
644+
const { listLifecycleParams } = backbeatMetadataProxy;
645+
assert.strictEqual(listLifecycleParams.Bucket, bucketName);
646+
assert.strictEqual(listLifecycleParams.Prefix, '');
647+
assert.strictEqual(listLifecycleParams.ExcludedDataStoreName, destinationLocation);
648+
assert(!!listLifecycleParams.BeforeDate);
649+
650+
// test that no entry is pushed to kafka topic (delete markers cannot be transitioned)
651+
assert.strictEqual(kafkaEntries.length, 0);
652+
return done();
653+
});
654+
});
655+
616656
it('should publish one bucket entry if listing is truncated', done => {
617657
const keyName = 'key1';
618658
const key = keyMock.current({ keyName, daysEarlier: 1 });

0 commit comments

Comments
 (0)