From 7cb2c272d7f831f574561ff1583f9fb649a6347d Mon Sep 17 00:00:00 2001 From: Michael Sambol Date: Sat, 24 Feb 2024 12:56:46 -0700 Subject: [PATCH] fix(ecr): incorrect format for rule pattern --- .../aws-ecr-integ-stack.assets.json | 6 +- .../aws-ecr-integ-stack.template.json | 84 +++++++++ .../test/integ.basic.js.snapshot/cdk.out | 2 +- ...efaultTestDeployAssert4F7FBFB4.assets.json | 2 +- .../test/integ.basic.js.snapshot/integ.json | 2 +- .../integ.basic.js.snapshot/manifest.json | 37 +++- .../test/integ.basic.js.snapshot/tree.json | 168 +++++++++++++++++- .../test/aws-ecr/test/integ.basic.ts | 13 ++ packages/aws-cdk-lib/aws-ecr/README.md | 21 +++ .../aws-cdk-lib/aws-ecr/lib/repository.ts | 4 +- .../aws-ecr/test/repository.test.ts | 27 +++ 11 files changed, 353 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json index b1ebebaa8af67..3275419691531 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.assets.json @@ -1,7 +1,7 @@ { - "version": "35.0.0", + "version": "36.0.0", "files": { - "830646461dc1fed84d30409177199864dfe0b167864b2fcdca22f4b9aad8a063": { + "af3377e6e3d575073bc8b4d7e7dacd1c1b33dfc0c606091f6e6ce84b4ff183ca": { "source": { "path": "aws-ecr-integ-stack.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "830646461dc1fed84d30409177199864dfe0b167864b2fcdca22f4b9aad8a063.json", + "objectKey": "af3377e6e3d575073bc8b4d7e7dacd1c1b33dfc0c606091f6e6ce84b4ff183ca.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json index ed4ac96c9385e..ef655e9119f43 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/aws-ecr-integ-stack.template.json @@ -73,6 +73,90 @@ }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "RepoOnEvent13B6ADDB": { + "Type": "AWS::ECR::Repository", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "RepoOnEventOnEventTargetLambda2934FA99": { + "Type": "AWS::Events::Rule", + "Properties": { + "EventPattern": { + "source": [ + "aws.ecr" + ], + "detail": { + "repository-name": [ + { + "Ref": "RepoOnEvent13B6ADDB" + } + ] + } + }, + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "LambdaFunctionBF21E41F", + "Arn" + ] + }, + "Id": "OnEventTargetLambda" + } + ] + } + }, + "LambdaFunctionServiceRoleC555A460": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "LambdaFunctionBF21E41F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "# dummy func" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "LambdaFunctionServiceRoleC555A460", + "Arn" + ] + }, + "Runtime": "python3.12" + }, + "DependsOn": [ + "LambdaFunctionServiceRoleC555A460" + ] } }, "Outputs": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out index c5cb2e5de6344..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"35.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json index 4a4a176d22fed..eaa4d77f5282d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/cdkecrintegtestbasicDefaultTestDeployAssert4F7FBFB4.assets.json @@ -1,5 +1,5 @@ { - "version": "35.0.0", + "version": "36.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json index 530efded1be3b..2144156a73f14 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "35.0.0", + "version": "36.0.0", "testCases": { "cdk-ecr-integ-test-basic/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json index f53c26891f1ed..2ba77511dc9ef 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "35.0.0", + "version": "36.0.0", "artifacts": { "aws-ecr-integ-stack.assets": { "type": "cdk:asset-manifest", @@ -18,7 +18,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}/830646461dc1fed84d30409177199864dfe0b167864b2fcdca22f4b9aad8a063.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/af3377e6e3d575073bc8b4d7e7dacd1c1b33dfc0c606091f6e6ce84b4ff183ca.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -64,6 +64,30 @@ "data": "RepositoryURI" } ], + "/aws-ecr-integ-stack/RepoOnEvent/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "RepoOnEvent13B6ADDB" + } + ], + "/aws-ecr-integ-stack/RepoOnEvent/OnEventTargetLambda/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "RepoOnEventOnEventTargetLambda2934FA99" + } + ], + "/aws-ecr-integ-stack/LambdaFunction/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaFunctionServiceRoleC555A460" + } + ], + "/aws-ecr-integ-stack/LambdaFunction/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaFunctionBF21E41F" + } + ], "/aws-ecr-integ-stack/BootstrapVersion": [ { "type": "aws:cdk:logicalId", @@ -75,6 +99,15 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } + ], + "RepoOnEventTargetLambdaBD80F099": [ + { + "type": "aws:cdk:logicalId", + "data": "RepoOnEventTargetLambdaBD80F099", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } ] }, "displayName": "aws-ecr-integ-stack" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json index 53559b0722b55..07580c3acab1e 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.js.snapshot/tree.json @@ -141,14 +141,14 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_ecr.CfnRepository", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_ecr.Repository", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "RepositoryURI": { @@ -159,6 +159,166 @@ "version": "10.3.0" } }, + "RepoOnEvent": { + "id": "RepoOnEvent", + "path": "aws-ecr-integ-stack/RepoOnEvent", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecr-integ-stack/RepoOnEvent/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECR::Repository", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "OnEventTargetLambda": { + "id": "OnEventTargetLambda", + "path": "aws-ecr-integ-stack/RepoOnEvent/OnEventTargetLambda", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-ecr-integ-stack/RepoOnEvent/OnEventTargetLambda/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Events::Rule", + "aws:cdk:cloudformation:props": { + "eventPattern": { + "source": [ + "aws.ecr" + ], + "detail": { + "repository-name": [ + { + "Ref": "RepoOnEvent13B6ADDB" + } + ] + } + }, + "state": "ENABLED", + "targets": [ + { + "id": "OnEventTargetLambda", + "arn": { + "Fn::GetAtt": [ + "LambdaFunctionBF21E41F", + "Arn" + ] + } + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "LambdaFunction": { + "id": "LambdaFunction", + "path": "aws-ecr-integ-stack/LambdaFunction", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-ecr-integ-stack/LambdaFunction/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-ecr-integ-stack/LambdaFunction/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecr-integ-stack/LambdaFunction/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-ecr-integ-stack/LambdaFunction/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "# dummy func" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "LambdaFunctionServiceRoleC555A460", + "Arn" + ] + }, + "runtime": "python3.12" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "aws-ecr-integ-stack/BootstrapVersion", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts index 199c1d7f152ae..9d0a8c64113cb 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecr/test/integ.basic.ts @@ -2,6 +2,7 @@ import * as cdk from 'aws-cdk-lib'; import { IntegTest } from '@aws-cdk/integ-tests-alpha'; import * as ecr from 'aws-cdk-lib/aws-ecr'; import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-ecr-integ-stack'); @@ -28,6 +29,18 @@ new cdk.CfnOutput(stack, 'RepositoryURI', { value: repo.repositoryUri, }); +const repoOnEvent = new ecr.Repository(stack, 'RepoOnEvent'); +const lambdaFunction = new lambda.Function(stack, 'LambdaFunction', { + runtime: lambda.Runtime.PYTHON_3_12, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', +}); +repoOnEvent.onEvent('OnEventTargetLambda', { + target: { + bind: () => ({ arn: lambdaFunction.functionArn, id: 'OnEventTargetLambda' }), + }, +}); + new IntegTest(app, 'cdk-ecr-integ-test-basic', { testCases: [stack], }); diff --git a/packages/aws-cdk-lib/aws-ecr/README.md b/packages/aws-cdk-lib/aws-ecr/README.md index bba3db8e0ac27..467f1a5fa1cc1 100644 --- a/packages/aws-cdk-lib/aws-ecr/README.md +++ b/packages/aws-cdk-lib/aws-ecr/README.md @@ -208,3 +208,24 @@ repository.addToResourcePolicy(new iam.PolicyStatement({ principals: [new iam.AnyPrincipal()], })); ``` + +## CloudWatch event rules + +You can publish repository events to a CloudWatch event rule with `onEvent`: + +```ts +import * as lambda from 'aws-cdk-lib/aws-lambda'; + +const repo = new ecr.Repository(this, 'Repo'); +const lambdaFunction = new lambda.Function(stack, 'LambdaFunction', { + runtime: lambda.Runtime.PYTHON_3_12, + code: lambda.Code.fromInline('# dummy func'), + handler: 'index.handler', +}); + +repo.onEvent('OnEventTargetLambda', { + target: { + bind: () => ({ arn: lambdaFunction.functionArn, id: 'OnEventTargetLambda' }), + }, +}); +``` diff --git a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts index 720bf9002331c..f86b2d11b5b9f 100644 --- a/packages/aws-cdk-lib/aws-ecr/lib/repository.ts +++ b/packages/aws-cdk-lib/aws-ecr/lib/repository.ts @@ -317,7 +317,9 @@ export abstract class RepositoryBase extends Resource implements IRepository { const rule = new events.Rule(this, id, options); rule.addEventPattern({ source: ['aws.ecr'], - resources: [this.repositoryArn], + detail: { + 'repository-name': [this.repositoryName], + }, }); rule.addTarget(options.target); return rule; diff --git a/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts b/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts index 700a8e6ca8ea0..0c703ad7763da 100644 --- a/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts +++ b/packages/aws-cdk-lib/aws-ecr/test/repository.test.ts @@ -980,6 +980,33 @@ describe('repository', () => { }, }); }); + + test('grant read adds appropriate permissions', () => { + // GIVEN + const stack = new cdk.Stack(); + const repo = new ecr.Repository(stack, 'TestRepo'); + + // WHEN + repo.onEvent('EcrOnEventRule', { + target: { + bind: () => ({ arn: 'ARN', id: '' }), + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + 'EventPattern': { + 'source': [ + 'aws.ecr', + ], + 'detail': { + 'repository-name': [ + { 'Ref': 'TestRepo08D311A0' }, + ], + }, + }, + }); + }); }); describe('repository name validation', () => {