Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: putBucketLifecycle add ColdArchive and DeepColdArchive #1256

Merged
merged 13 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -994,12 +994,12 @@ parameters:
- [createdBeforeDate] {String} expire date, e.g.: `2022-10-11T00:00:00.000Z`
`createdBeforeDate` and `days` must have one.
- [transition] {Object} Specifies the time when an object is converted to the IA or archive storage class during a valid life cycle.
- storageClass {String} Specifies the storage class that objects that conform to the rule are converted into. allow values: `IA` or `Archive`
- storageClass {String} Specifies the storage class that objects that conform to the rule are converted into. allow values: `IA` or `Archive` or `ColdArchive` or `DeepColdArchive`
- [days] {Number|String} expire after the `days`
- [createdBeforeDate] {String} expire date, e.g.: `2022-10-11T00:00:00.000Z`
`createdBeforeDate` and `days` must have one.
- [noncurrentVersionTransition] {Object} Specifies the time when an object is converted to the IA or archive storage class during a valid life cycle.
- storageClass {String} Specifies the storage class that history objects that conform to the rule are converted into. allow values: `IA` or `Archive`
- storageClass {String} Specifies the storage class that history objects that conform to the rule are converted into. allow values: `IA` or `Archive` or `ColdArchive` or `DeepColdArchive`
- noncurrentDays {String} expire after the `noncurrentDays`
`expiration`、 `abortMultipartUpload`、 `transition`、 `noncurrentVersionTransition` must have one.
- [noncurrentVersionExpiration] {Object} specifies the expiration attribute of the lifecycle rules for the history object.
Expand Down
36 changes: 32 additions & 4 deletions lib/common/bucket/putBucketLifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ function checkDaysAndDate(obj, key) {
}
}

function checkNoncurrentDays(obj, key) {
const { noncurrentDays } = obj;
if (!noncurrentDays) {
throw new Error(`${key} must includes noncurrentDays`);
} else if (noncurrentDays && !/^[1-9][0-9]*$/.test(noncurrentDays)) {
throw new Error('noncurrentDays must be a positive integer');
}
}

function handleCheckTag(tag) {
if (!isArray(tag) && !isObject(tag)) {
throw new Error('tag must be Object or Array');
Expand All @@ -87,6 +96,11 @@ function handleCheckTag(tag) {
checkObjectTag(tagObj);
}

function checkStorageClass(storageClass) {
if (!['IA', 'Archive', 'ColdArchive', 'DeepColdArchive'].includes(storageClass))
throw new Error(`StorageClass must be IA or Archive or ColdArchive or DeepColdArchive`);
}

function checkRule(rule) {
if (rule.id && getStrBytesCount(rule.id) > 255) throw new Error('ID is composed of 255 bytes at most');

Expand All @@ -95,8 +109,7 @@ function checkRule(rule) {
if (!['Enabled', 'Disabled'].includes(rule.status)) throw new Error('Status must be Enabled or Disabled');

if (rule.transition) {
if (!['IA', 'Archive'].includes(rule.transition.storageClass))
throw new Error('StorageClass must be IA or Archive');
checkStorageClass(rule.transition.storageClass);
checkDaysAndDate(rule.transition, 'Transition');
}

Expand All @@ -112,12 +125,27 @@ function checkRule(rule) {
checkDaysAndDate(rule.abortMultipartUpload, 'AbortMultipartUpload');
}

if (!rule.expiration && !rule.abortMultipartUpload && !rule.transition && !rule.noncurrentVersionTransition) {
if (
!rule.expiration &&
!rule.noncurrentVersionExpiration &&
!rule.abortMultipartUpload &&
!rule.transition &&
!rule.noncurrentVersionTransition
) {
throw new Error(
'Rule must includes expiration or abortMultipartUpload or transition or noncurrentVersionTransition'
'Rule must includes expiration or noncurrentVersionExpiration or abortMultipartUpload or transition or noncurrentVersionTransition'
);
shungang marked this conversation as resolved.
Show resolved Hide resolved
}

if (rule.noncurrentVersionTransition) {
checkStorageClass(rule.noncurrentVersionTransition.storageClass);
checkNoncurrentDays(rule.noncurrentVersionTransition, 'NoncurrentVersionTransition');
}

shungang marked this conversation as resolved.
Show resolved Hide resolved
if (rule.noncurrentVersionExpiration) {
checkNoncurrentDays(rule.noncurrentVersionExpiration, 'NoncurrentVersionExpiration');
}

if (rule.tag) {
if (rule.abortMultipartUpload) {
throw new Error('Tag cannot be used with abortMultipartUpload');
Expand Down
64 changes: 58 additions & 6 deletions test/node/bucket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('test/bucket.test.js', () => {
const { prefix, includesConf } = utils;
let store;
let bucket;
const bucketRegion = config.region;
const bucketRegion = 'oss-ap-southeast-1'; // oss-ap-southeast-1 suport PutBucketLifecycle DeepColdArchive
const { accountId } = config;
[
{
Expand All @@ -25,7 +25,7 @@ describe('test/bucket.test.js', () => {
].forEach((moreConfigs, idx) => {
describe(`test bucket in iterate ${idx}`, () => {
before(async () => {
store = oss({ ...config, ...moreConfigs });
store = oss({ ...config, ...moreConfigs, region: bucketRegion });
bucket = `ali-oss-test-bucket-${prefix.replace(/[/.]/g, '-')}${idx}`;

const result = await store.putBucket(bucket, { timeout });
Expand Down Expand Up @@ -764,7 +764,25 @@ describe('test/bucket.test.js', () => {
});

describe('putBucketLifecycle()', () => {
// todo delete
it('should put the lifecycle throw error', async () => {
try {
await store.putBucketLifecycle(bucket, [
{
id: 'expiration1',
prefix: 'logs/',
status: 'Enabled',
day: 1
}
]);
assert.fail('expected an error to be thrown');
} catch (e) {
assert.equal(
e.message,
'Rule must includes expiration or noncurrentVersionExpiration or abortMultipartUpload or transition or noncurrentVersionTransition'
);
}
});

it('should put the lifecycle with old api', async () => {
const putresult1 = await store.putBucketLifecycle(bucket, [
{
Expand All @@ -774,6 +792,7 @@ describe('test/bucket.test.js', () => {
days: 1
}
]);

assert.equal(putresult1.res.status, 200);

const putresult2 = await store.putBucketLifecycle(bucket, [
Expand Down Expand Up @@ -864,7 +883,7 @@ describe('test/bucket.test.js', () => {
status: 'Enabled',
transition: {
createdBeforeDate: '2020-02-18T00:00:00.000Z',
storageClass: 'Archive'
storageClass: 'IA'
},
expiration: {
createdBeforeDate: '2020-02-17T00:00:00.000Z'
Expand Down Expand Up @@ -893,6 +912,39 @@ describe('test/bucket.test.js', () => {
}
]);
assert.equal(putresult2.res.status, 200);
const putresult3 = await store.putBucketLifecycle(bucket, [
{
id: 'transition3',
prefix: 'logs/',
status: 'Enabled',
transition: {
days: 20,
storageClass: 'ColdArchive'
},
tag: {
key: 'test3',
value: '123'
}
}
]);
assert.equal(putresult3.res.status, 200);
// Regions that need to support DeepColdArchive
const putresult4 = await store.putBucketLifecycle(bucket, [
{
id: 'transition4',
prefix: 'logs/',
status: 'Enabled',
transition: {
days: 20,
storageClass: 'DeepColdArchive'
},
tag: {
key: 'test4',
value: '123'
}
}
]);
assert.equal(putresult4.res.status, 200);
});

it('should put the lifecycle with expiration and Tag', async () => {
Expand Down Expand Up @@ -1023,7 +1075,7 @@ describe('test/bucket.test.js', () => {
]);
assert(false);
} catch (error) {
assert(error.message.includes('IA or Archive'));
assert(error.message.includes('IA or Archive or ColdArchive or DeepColdArchive'));
}
});

Expand Down Expand Up @@ -1206,7 +1258,7 @@ describe('test/bucket.test.js', () => {
});

it('should throw error when rule have no expiration or abortMultipartUpload', async () => {
const errorMessage = 'expiration or abortMultipartUpload';
const errorMessage = 'expiration or noncurrentVersionExpiration or abortMultipartUpload';
try {
await store.putBucketLifecycle(bucket, [
{
Expand Down
69 changes: 53 additions & 16 deletions test/node/multiversion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe('test/multiversion.test.js', () => {
].forEach((moreConfigs, idx) => {
describe(`test multiversion in iterate ${idx}`, () => {
before(async () => {
store = oss({ ...config, ...moreConfigs });
// oss-ap-southeast-1 suport PutBucketLifecycle DeepColdArchive
store = oss({ ...config, ...moreConfigs, region: 'oss-ap-southeast-1' });
bucket = `ali-oss-test-bucket-version-${prefix.replace(/[/.]/g, '-')}${idx}`;

const result = await store.putBucket(bucket);
Expand Down Expand Up @@ -117,12 +118,11 @@ describe('test/multiversion.test.js', () => {
});

describe('putBucketLifecycle() getBucketLifecycle()', async () => {
it('should putBucketLifecycle with NoncurrentVersionExpiration', async () => {
it('should putBucketLifecycle with noncurrentVersionExpiration', async () => {
const putresult1 = await store.putBucketLifecycle(
bucket,
[
{
id: 'expiration1',
prefix: 'logs/',
status: 'Enabled',
expiration: {
Expand All @@ -131,6 +131,13 @@ describe('test/multiversion.test.js', () => {
noncurrentVersionExpiration: {
noncurrentDays: 1
}
},
{
prefix: 'logss/',
status: 'Enabled',
noncurrentVersionExpiration: {
noncurrentDays: 1
}
}
],
{
Expand All @@ -142,6 +149,7 @@ describe('test/multiversion.test.js', () => {
const { rules } = await store.getBucketLifecycle(bucket);
assert.strictEqual(rules[0].noncurrentVersionExpiration.noncurrentDays, '1');
});

it('should putBucketLifecycle with expiredObjectDeleteMarker', async () => {
const putresult1 = await store.putBucketLifecycle(bucket, [
{
Expand All @@ -151,7 +159,7 @@ describe('test/multiversion.test.js', () => {
expiration: {
expiredObjectDeleteMarker: 'true'
},
NoncurrentVersionExpiration: {
noncurrentVersionExpiration: {
noncurrentDays: 1
}
}
Expand All @@ -163,26 +171,55 @@ describe('test/multiversion.test.js', () => {
});

it('should putBucketLifecycle with noncurrentVersionTransition', async () => {
const putresult1 = await store.putBucketLifecycle(bucket, [
{
id: 'expiration1',
prefix: 'logs/',
status: 'Enabled',
noncurrentVersionTransition: {
noncurrentDays: '10',
storageClass: 'IA'
const putresult = await store.putBucketLifecycle(
bucket,
[
{
prefix: 'log/',
status: 'Enabled',
noncurrentVersionTransition: {
noncurrentDays: '10',
storageClass: 'IA'
}
},
{
prefix: 'log/',
status: 'Enabled',
noncurrentVersionTransition: {
noncurrentDays: '10',
storageClass: 'Archive'
}
},
{
prefix: 'log/',
status: 'Enabled',
noncurrentVersionTransition: {
noncurrentDays: '10',
storageClass: 'ColdArchive'
}
},
{
prefix: 'log/',
status: 'Enabled',
noncurrentVersionTransition: {
noncurrentDays: '10',
storageClass: 'DeepColdArchive'
}
}
}
]);
assert.equal(putresult1.res.status, 200);
],
{ headers: { 'x-oss-allow-same-action-overlap': 'true' } }
);
assert.equal(putresult.res.status, 200);
await utils.sleep(1000);

const { rules } = await store.getBucketLifecycle(bucket);
const [
{
noncurrentVersionTransition: { noncurrentDays, storageClass }
}
] = rules;
assert(noncurrentDays === '10' && storageClass === 'IA');

assert(noncurrentDays === '10' && storageClass === 'IA' && rules.length === 4);
});
});

Expand Down
Loading