From 7d6d05ae32ddb04da07cbea992a681940849516d Mon Sep 17 00:00:00 2001 From: Charles Fulton Date: Thu, 10 Nov 2022 09:59:39 -0500 Subject: [PATCH 1/4] feat(synthetics): expose lifecycle rules for auto-generated artifact buckets This change exposes the lifecycle rules property for new and existing auto-generated artifact buckets so that workload owners can easily manage growth. fixes #22634 --- .../@aws-cdk/aws-synthetics/lib/canary.ts | 9 ++++++ .../aws-synthetics/test/canary.test.ts | 31 +++++++++++++++++++ .../aws-synthetics/test/integ.canary.ts | 5 +++ 3 files changed, 45 insertions(+) diff --git a/packages/@aws-cdk/aws-synthetics/lib/canary.ts b/packages/@aws-cdk/aws-synthetics/lib/canary.ts index 717bfcbe6082d..e5e06f331ca59 100644 --- a/packages/@aws-cdk/aws-synthetics/lib/canary.ts +++ b/packages/@aws-cdk/aws-synthetics/lib/canary.ts @@ -88,6 +88,14 @@ export interface ArtifactsBucketLocation { * Properties for a canary */ export interface CanaryProps { + /** + * Lifecycle rules for the generated canary artifact bucket. + * + * @default - No rules applied. This has no effect if a bucket is passed + * to `artifactsBucketLocation`. + */ + readonly artifactsBucketLifecycleRules?: Array; + /** * The s3 location that stores the data of the canary runs. * @@ -260,6 +268,7 @@ export class Canary extends cdk.Resource implements ec2.IConnectable { this.artifactsBucket = props.artifactsBucketLocation?.bucket ?? new s3.Bucket(this, 'ArtifactsBucket', { encryption: s3.BucketEncryption.KMS_MANAGED, enforceSSL: true, + lifecycleRules: props.artifactsBucketLifecycleRules, }); this.role = props.role ?? this.createDefaultRole(props); diff --git a/packages/@aws-cdk/aws-synthetics/test/canary.test.ts b/packages/@aws-cdk/aws-synthetics/test/canary.test.ts index aee1ba78cd134..ec3de1acbd8c4 100644 --- a/packages/@aws-cdk/aws-synthetics/test/canary.test.ts +++ b/packages/@aws-cdk/aws-synthetics/test/canary.test.ts @@ -151,6 +151,37 @@ test('An existing bucket and prefix can be specified instead of auto-created', ( }); }); +test('An auto-generated bucket has lifecycle rules', () => { + // GIVEN + const stack = new Stack(); + const lifecycleRules: Array = [ + { + expiration: Duration.days(30), + }, + ]; + + // WHEN + new synthetics.Canary(stack, 'Canary', { + artifactsBucketLifecycleRules: lifecycleRules, + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + runtime: synthetics.Runtime.SYNTHETICS_1_0, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::S3::Bucket', { + LifecycleConfiguration: { + Rules: [ + { + ExpirationInDays: 30, + }, + ], + }, + }); +}); + test('Runtime can be specified', () => { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.canary.ts b/packages/@aws-cdk/aws-synthetics/test/integ.canary.ts index bd5963acf77e6..0ac2ae1200e91 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.canary.ts +++ b/packages/@aws-cdk/aws-synthetics/test/integ.canary.ts @@ -41,6 +41,11 @@ new synthetics.Canary(stack, 'MyCanaryOne', { code: synthetics.Code.fromAsset(path.join(__dirname, 'canaries')), }), runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_2, + artifactsBucketLifecycleRules: [ + { + expiration: cdk.Duration.days(30), + }, + ], }); new synthetics.Canary(stack, 'MyCanaryTwo', { From 0541c2589098f17518879d6c010b067afa1314b4 Mon Sep 17 00:00:00 2001 From: Charles Fulton Date: Thu, 10 Nov 2022 13:07:20 -0500 Subject: [PATCH 2/4] Update inline docs --- packages/@aws-cdk/aws-synthetics/lib/canary.ts | 8 +++++--- packages/@aws-cdk/aws-synthetics/test/canary.test.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-synthetics/lib/canary.ts b/packages/@aws-cdk/aws-synthetics/lib/canary.ts index e5e06f331ca59..3528a036ae504 100644 --- a/packages/@aws-cdk/aws-synthetics/lib/canary.ts +++ b/packages/@aws-cdk/aws-synthetics/lib/canary.ts @@ -89,10 +89,12 @@ export interface ArtifactsBucketLocation { */ export interface CanaryProps { /** - * Lifecycle rules for the generated canary artifact bucket. + * Lifecycle rules for the generated canary artifact bucket. Has no effect + * if a bucket is passed to `artifactsBucketLocation`. If you pass a bucket + * to `artifactsBucketLocation`, you can add lifecycle rules to the bucket + * itself. * - * @default - No rules applied. This has no effect if a bucket is passed - * to `artifactsBucketLocation`. + * @default - no rules applied to the generated bucket. */ readonly artifactsBucketLifecycleRules?: Array; diff --git a/packages/@aws-cdk/aws-synthetics/test/canary.test.ts b/packages/@aws-cdk/aws-synthetics/test/canary.test.ts index ec3de1acbd8c4..e9bddc1f027ab 100644 --- a/packages/@aws-cdk/aws-synthetics/test/canary.test.ts +++ b/packages/@aws-cdk/aws-synthetics/test/canary.test.ts @@ -167,7 +167,7 @@ test('An auto-generated bucket has lifecycle rules', () => { handler: 'index.handler', code: synthetics.Code.fromInline('/* Synthetics handler code */'), }), - runtime: synthetics.Runtime.SYNTHETICS_1_0, + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_8, }); // THEN From 3fc5a8ec97ac95365c4c90a31094614023136f2e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 10 Nov 2022 15:52:57 -0500 Subject: [PATCH 3/4] ran integ test --- .../canary-one.assets.json | 4 +- .../canary-one.template.json | 8 ++++ .../integ.canary.js.snapshot/manifest.json | 14 +++---- .../test/integ.canary.js.snapshot/tree.json | 40 +++++++++++++++---- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.assets.json b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.assets.json index 63cb882a1205c..5a52010096eaa 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.assets.json +++ b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.assets.json @@ -27,7 +27,7 @@ } } }, - "32cb6cc1550e67ae5e77eb85a64192d2aa44c328b778079b139e3965fb042575": { + "118dbbafbcc45f72b55290773ddb91e44b138ccf366add6c1c5c74c2235fec37": { "source": { "path": "canary-one.template.json", "packaging": "file" @@ -35,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "32cb6cc1550e67ae5e77eb85a64192d2aa44c328b778079b139e3965fb042575.json", + "objectKey": "118dbbafbcc45f72b55290773ddb91e44b138ccf366add6c1c5c74c2235fec37.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.template.json b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.template.json index 04fff7008b44f..0e188ac69bb89 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.template.json +++ b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/canary-one.template.json @@ -148,6 +148,14 @@ } } ] + }, + "LifecycleConfiguration": { + "Rules": [ + { + "ExpirationInDays": 30, + "Status": "Enabled" + } + ] } }, "UpdateReplacePolicy": "Retain", diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/manifest.json b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/manifest.json index 1046b3ff32e7a..9a7dae474662d 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { "version": "21.0.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "canary-one.assets": { "type": "cdk:asset-manifest", "properties": { @@ -23,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/32cb6cc1550e67ae5e77eb85a64192d2aa44c328b778079b139e3965fb042575.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/118dbbafbcc45f72b55290773ddb91e44b138ccf366add6c1c5c74c2235fec37.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -215,6 +209,12 @@ ] }, "displayName": "canary-one" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/tree.json b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/tree.json index cb9fa9d58d965..bf413c2313aa3 100644 --- a/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-synthetics/test/integ.canary.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.133" - } - }, "canary-one": { "id": "canary-one", "path": "canary-one", @@ -228,6 +220,14 @@ } } ] + }, + "lifecycleConfiguration": { + "rules": [ + { + "expirationInDays": 30, + "status": "Enabled" + } + ] } } }, @@ -1963,12 +1963,36 @@ "fqn": "@aws-cdk/aws-synthetics.Canary", "version": "0.0.0" } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "canary-one/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "canary-one/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } } }, "constructInfo": { "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.140" + } } }, "constructInfo": { From e54e78324b268ff5fb1d259b5ecce5d86139345e Mon Sep 17 00:00:00 2001 From: Charles Fulton Date: Sat, 12 Nov 2022 18:10:46 -0500 Subject: [PATCH 4/4] Update README --- packages/@aws-cdk/aws-synthetics/README.md | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/@aws-cdk/aws-synthetics/README.md b/packages/@aws-cdk/aws-synthetics/README.md index 09c771a1e5215..5efef5feb28b9 100644 --- a/packages/@aws-cdk/aws-synthetics/README.md +++ b/packages/@aws-cdk/aws-synthetics/README.md @@ -220,3 +220,53 @@ new cloudwatch.Alarm(this, 'CanaryAlarm', { comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD, }); ``` + +### Artifacts + +You can pass an S3 bucket to store artifacts from canary runs. If you do not, one will be auto-generated when the canary is created. You may add [lifecycle rules](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html) to the auto-generated bucket. + +Artifact bucket examples: + +```typescript +// Auto-generated bucket with lifecycle rules. +const canary = new synthetics.Canary(this, 'MyCanary', { + schedule: synthetics.Schedule.rate(Duration.minutes(5)), + test: synthetics.Test.custom({ + code: synthetics.Code.fromAsset(path.join(__dirname, 'canary')), + handler: 'index.handler', + }), + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_8, + environmentVariables: { + stage: 'prod', + }, + artifactsBucketLifecycleRules: [ + { + expiration: Duration.days(30), + }, + ] +}); + +// Custom bucket as artifact bucket. +import * as s3 from '@aws-cdk/aws-s3'; + +const bucket = new s3.Bucket(this, 'MyArtifactsBucket', { + encryption: s3.BucketEncryption.KMS_MANAGED, + enforceSSL: true, +}); + +const canary = new synthetics.Canary(this, 'MyCanary', { + schedule: synthetics.Schedule.rate(Duration.minutes(5)), + test: synthetics.Test.custom({ + code: synthetics.Code.fromAsset(path.join(__dirname, 'canary')), + handler: 'index.handler', + }), + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_8, + environmentVariables: { + stage: 'prod', + }, + artifactsBucketLocation: { + bucket: bucket, + } +}); +``` +