Skip to content

Commit

Permalink
feat(s3): add noncurrentVersionsToRetain property to lifecycle rule (
Browse files Browse the repository at this point in the history
…aws#20348)

Fixes aws#19784

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
mayforblue authored and wphilipw committed May 23, 2022
1 parent b22bd2c commit 0973c34
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 3 deletions.
36 changes: 36 additions & 0 deletions packages/@aws-cdk/aws-s3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,3 +551,39 @@ bucket.transferAccelerationUrlForObject('objectname');
});

```

## Lifecycle Rule

[Managing lifecycle](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html) can be configured transition or expiration actions.

```ts
const bucket = new s3.Bucket(this, 'MyBucket', {
lifecycleRules: [{
abortIncompleteMultipartUploadAfter: cdk.Duration.minutes(30),
enabled: false,
expiration: cdk.Duration.days(30),
expirationDate: new Date(),
expiredObjectDeleteMarker: false,
id: 'id',
noncurrentVersionExpiration: cdk.Duration.days(30),

// the properties below are optional
noncurrentVersionsToRetain: 123,
noncurrentVersionTransitions: [{
storageClass: s3.StorageClass.GLACIER,
transitionAfter: cdk.Duration.days(30),

// the properties below are optional
noncurrentVersionsToRetain: 123,
}],
prefix: 'prefix',
transitions: [{
storageClass: s3.StorageClass.GLACIER,

// the properties below are optional
transitionAfter: cdk.Duration.days(30),
transitionDate: new Date(),
}],
}]
});
```
5 changes: 4 additions & 1 deletion packages/@aws-cdk/aws-s3/lib/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1905,7 +1905,10 @@ export class Bucket extends BucketBase {
expirationDate: rule.expirationDate,
expirationInDays: rule.expiration?.toDays(),
id: rule.id,
noncurrentVersionExpirationInDays: rule.noncurrentVersionExpiration && rule.noncurrentVersionExpiration.toDays(),
noncurrentVersionExpiration: rule.noncurrentVersionExpiration && {
noncurrentDays: rule.noncurrentVersionExpiration.toDays(),
newerNoncurrentVersions: rule.noncurrentVersionsToRetain,
},
noncurrentVersionTransitions: mapOrUndefined(rule.noncurrentVersionTransitions, t => ({
storageClass: t.storageClass.value,
transitionInDays: t.transitionAfter.toDays(),
Expand Down
10 changes: 10 additions & 0 deletions packages/@aws-cdk/aws-s3/lib/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ export interface LifecycleRule {
*/
readonly noncurrentVersionExpiration?: Duration;

/**
* Indicates a maximum number of noncurrent versions to retain.
*
* If there are this many more noncurrent versions,
* Amazon S3 permanently deletes them.
*
* @default No noncurrent versions to retain
*/
readonly noncurrentVersionsToRetain?: number;

/**
* One or more transition rules that specify when non-current objects transition to a specified storage class.
*
Expand Down
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-s3/test/integ.lifecycle-expiration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { App, Duration, Stack } from '@aws-cdk/core';
import { Bucket } from '../lib';

const app = new App();

const stack = new Stack(app, 'aws-cdk-s3');

new Bucket(stack, 'MyBucket', {
lifecycleRules: [{
noncurrentVersionExpiration: Duration.days(30),
noncurrentVersionsToRetain: 123,
}],
versioned: true,
});

app.synth();
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"Resources": {
"MyBucketF68F3FF0": {
"Type": "AWS::S3::Bucket",
"Properties": {
"LifecycleConfiguration": {
"Rules": [
{
"NoncurrentVersionExpiration": {
"NewerNoncurrentVersions": 123,
"NoncurrentDays": 30
},
"Status": "Enabled"
}
]
},
"VersioningConfiguration": {
"Status": "Enabled"
}
},
"UpdateReplacePolicy": "Retain",
"DeletionPolicy": "Retain"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"19.0.0"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version": "19.0.0",
"testCases": {
"aws-s3/test/integ.lifecycle-expiration": {
"stacks": [
"aws-cdk-s3"
],
"diffAssets": false,
"stackUpdateWorkflow": true
}
},
"synthContext": {},
"enableLookups": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"version": "19.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"aws-cdk-s3": {
"type": "aws:cloudformation:stack",
"environment": "aws://unknown-account/unknown-region",
"properties": {
"templateFile": "aws-cdk-s3.template.json",
"validateOnSynth": false
},
"metadata": {
"/aws-cdk-s3/MyBucket/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "MyBucketF68F3FF0"
}
]
},
"displayName": "aws-cdk-s3"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"version": "tree-0.1",
"tree": {
"id": "App",
"path": "",
"children": {
"Tree": {
"id": "Tree",
"path": "Tree",
"constructInfo": {
"fqn": "@aws-cdk/core.Construct",
"version": "0.0.0"
}
},
"aws-cdk-s3": {
"id": "aws-cdk-s3",
"path": "aws-cdk-s3",
"children": {
"MyBucket": {
"id": "MyBucket",
"path": "aws-cdk-s3/MyBucket",
"children": {
"Resource": {
"id": "Resource",
"path": "aws-cdk-s3/MyBucket/Resource",
"attributes": {
"aws:cdk:cloudformation:type": "AWS::S3::Bucket",
"aws:cdk:cloudformation:props": {
"lifecycleConfiguration": {
"rules": [
{
"noncurrentVersionExpiration": {
"noncurrentDays": 30,
"newerNoncurrentVersions": 123
},
"status": "Enabled"
}
]
},
"versioningConfiguration": {
"status": "Enabled"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-s3.CfnBucket",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-s3.Bucket",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/core.Stack",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/core.App",
"version": "0.0.0"
}
}
}
84 changes: 82 additions & 2 deletions packages/@aws-cdk/aws-s3/test/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ describe('rules', () => {
Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {
LifecycleConfiguration: {
Rules: [{
NoncurrentVersionExpirationInDays: 10,
NoncurrentVersionExpiration: {
NoncurrentDays: 10,
},
NoncurrentVersionTransitions: [
{
NewerNoncurrentVersions: 1,
Expand Down Expand Up @@ -199,7 +201,85 @@ describe('rules', () => {
Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {
LifecycleConfiguration: {
Rules: [{
NoncurrentVersionExpirationInDays: 10,
NoncurrentVersionExpiration: {
NoncurrentDays: 10,
},
NoncurrentVersionTransitions: [
{
StorageClass: 'GLACIER_IR',
TransitionInDays: 10,
},
],
Status: 'Enabled',
}],
},
});
});

test('Noncurrent expiration rule with versions to retain', () => {
// GIVEN
const stack = new Stack();

// WHEN: Noncurrent version to retain available
new Bucket(stack, 'Bucket1', {
versioned: true,
lifecycleRules: [{
noncurrentVersionExpiration: Duration.days(10),
noncurrentVersionsToRetain: 1,
noncurrentVersionTransitions: [
{
storageClass: StorageClass.GLACIER_INSTANT_RETRIEVAL,
transitionAfter: Duration.days(10),
},
],
}],
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {
LifecycleConfiguration: {
Rules: [{
NoncurrentVersionExpiration: {
NoncurrentDays: 10,
NewerNoncurrentVersions: 1,
},
NoncurrentVersionTransitions: [
{
StorageClass: 'GLACIER_IR',
TransitionInDays: 10,
},
],
Status: 'Enabled',
}],
},
});
});

test('Noncurrent expiration rule without versions to retain', () => {
// GIVEN
const stack = new Stack();

// WHEN: Noncurrent version to retain not set
new Bucket(stack, 'Bucket1', {
versioned: true,
lifecycleRules: [{
noncurrentVersionExpiration: Duration.days(10),
noncurrentVersionTransitions: [
{
storageClass: StorageClass.GLACIER_INSTANT_RETRIEVAL,
transitionAfter: Duration.days(10),
},
],
}],
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', {
LifecycleConfiguration: {
Rules: [{
NoncurrentVersionExpiration: {
NoncurrentDays: 10,
},
NoncurrentVersionTransitions: [
{
StorageClass: 'GLACIER_IR',
Expand Down

0 comments on commit 0973c34

Please sign in to comment.