From d9f060e670c69d44d4dad1f12e4bc13a9edf36e5 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Fri, 2 Nov 2018 13:36:09 -0700 Subject: [PATCH] feat(aws-s3): do not poll by default in the CodePipeline source Action. --- packages/@aws-cdk/aws-s3/lib/bucket.ts | 37 +++++++++++++++ .../@aws-cdk/aws-s3/lib/pipeline-action.ts | 13 +++-- packages/@aws-cdk/aws-s3/package.json | 4 +- .../aws-s3/test/test.notifications.ts | 47 ++++++++++++++++++- 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index ffa144a1cf2f9..553f0e7d7735a 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -1,4 +1,5 @@ import actions = require('@aws-cdk/aws-codepipeline-api'); +import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); import { IBucketNotificationDestination } from '@aws-cdk/aws-s3-notifications'; @@ -131,6 +132,42 @@ export abstract class BucketRef extends cdk.Construct { }); } + /** + * Defines a CloudWatch Event Rule that triggers upon putting an object into the Bucket. + * + * @param name the logical ID of the newly created Event Rule + * @param target the target of the Event Rule + * @param path the optional path inside the Bucket that will be watched for changes + * @returns a new {@link events.EventRule} instance + */ + public onObjectPut(name: string, target?: events.IEventRuleTarget, path?: string): events.EventRule { + const eventRule = new events.EventRule(this, name, { + eventPattern: { + source: [ + 'aws.s3', + ], + detailType: [ + 'AWS API Call via CloudTrail', + ], + detail: { + eventSource: [ + 's3.amazonaws.com', + ], + eventName: [ + 'PutObject', + ], + resources: { + ARN: [ + path ? this.arnForObjects(path) : this.bucketArn, + ], + }, + }, + }, + }); + eventRule.addTarget(target); + return eventRule; + } + /** * Adds a statement to the resource policy for a principal (i.e. * account/role/service) to perform actions on this bucket and/or it's diff --git a/packages/@aws-cdk/aws-s3/lib/pipeline-action.ts b/packages/@aws-cdk/aws-s3/lib/pipeline-action.ts index a4f03b9c79be5..a4ad36c40b8c8 100644 --- a/packages/@aws-cdk/aws-s3/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-s3/lib/pipeline-action.ts @@ -23,11 +23,11 @@ export interface CommonPipelineSourceActionProps extends codepipeline.CommonActi */ bucketKey: string; - // TODO: use CloudWatch events instead /** - * Whether or not AWS CodePipeline should poll for source changes + * Whether AWS CodePipeline should poll for source changes. + * If this is `false`, the Pipeline will use CloudWatch Events to detect source changes instead. * - * @default true + * @default false */ pollForSourceChanges?: boolean; } @@ -53,11 +53,16 @@ export class PipelineSourceAction extends codepipeline.SourceAction { configuration: { S3Bucket: props.bucket.bucketName, S3ObjectKey: props.bucketKey, - PollForSourceChanges: props.pollForSourceChanges || true + PollForSourceChanges: props.pollForSourceChanges || false, }, ...props, }); + if (!props.pollForSourceChanges) { + props.bucket.onObjectPut(props.stage.pipeline.uniqueId + 'SourceEventRule', + props.stage.pipeline, props.bucketKey); + } + // pipeline needs permissions to read from the S3 bucket props.bucket.grantRead(props.stage.pipeline.role); } diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 76044d9f465f0..3b131ae275203 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -60,6 +60,7 @@ }, "dependencies": { "@aws-cdk/aws-codepipeline-api": "^0.18.1", + "@aws-cdk/aws-events": "^0.18.1", "@aws-cdk/aws-iam": "^0.18.1", "@aws-cdk/aws-kms": "^0.18.1", "@aws-cdk/aws-s3-notifications": "^0.18.1", @@ -68,9 +69,10 @@ "homepage": "https://github.com/awslabs/aws-cdk", "peerDependencies": { "@aws-cdk/aws-codepipeline-api": "^0.18.1", + "@aws-cdk/aws-events": "^0.18.1", "@aws-cdk/aws-iam": "^0.18.1", "@aws-cdk/aws-kms": "^0.18.1", "@aws-cdk/aws-s3-notifications": "^0.18.1", "@aws-cdk/cdk": "^0.18.1" } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-s3/test/test.notifications.ts b/packages/@aws-cdk/aws-s3/test/test.notifications.ts index 69e183f98049a..eac7130029135 100644 --- a/packages/@aws-cdk/aws-s3/test/test.notifications.ts +++ b/packages/@aws-cdk/aws-s3/test/test.notifications.ts @@ -298,5 +298,50 @@ export = { }); test.done(); - } + }, + + 'CloudWatch Events': { + 'onPutItem contains the Bucket ARN itself when path is undefined'(test: Test) { + const stack = new cdk.Stack(); + const bucket = s3.BucketRef.import(stack, 'Bucket', { + bucketName: 'MyBucket', + }); + bucket.onObjectPut('PutRule'); + + expect(stack).to(haveResource('AWS::Events::Rule', { + "EventPattern": { + "source": [ + "aws.s3", + ], + "detail": { + "eventSource": [ + "s3.amazonaws.com", + ], + "eventName": [ + "PutObject", + ], + "resources": { + "ARN": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::MyBucket", + ], + ], + }, + ], + }, + }, + }, + "State": "ENABLED", + })); + + test.done(); + }, + }, };