From 93490e9902f748adf4f7712b9ad39b8aa6cade3a Mon Sep 17 00:00:00 2001 From: Jan Wendland Date: Tue, 2 Nov 2021 09:31:10 +0100 Subject: [PATCH 1/7] fix(codepipeline-actions): reintroduce option to trigger on all tags to EcrSourceAction The EcrSourceAction could previously be used to trigger on changes to all tags of an image. As part of the fix aws#13818, the imageTag was defaulted to latest if not provided. Therefore it was no longer possible to call the underlying onCloudTrailImagePushed function with an undefined imageTag to watch changes on all tags. Reintroduce triggering on all tags by passing an empty string as the imageTag. Fixes aws#13818 --- .../lib/ecr/source-action.ts | 12 +- ...pipeline-ecr-source-all-tags.expected.json | 415 ++++++++++++++++++ .../integ.pipeline-ecr-source-all-tags.ts | 32 ++ .../integ.pipeline-ecr-source.expected.json | 3 +- 4 files changed, 458 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json create mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts index eb40c994ccd98..1b1ff447d4fd4 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts @@ -35,7 +35,8 @@ export interface EcrSourceVariables { */ export interface EcrSourceActionProps extends codepipeline.CommonAwsActionProps { /** - * The image tag that will be checked for changes. + * The image tag that will be checked for changes. Provide an empty string to + * trigger on changes to any tag. * * @default 'latest' */ @@ -93,9 +94,14 @@ export class EcrSourceAction extends Action { resources: [this.props.repository.repositoryArn], })); + let imageTag: string | undefined; + if (this.props.imageTag !== '') { + imageTag = this.props.imageTag ?? 'latest'; + } + this.props.repository.onCloudTrailImagePushed(Names.nodeUniqueId(stage.pipeline.node) + 'SourceEventRule', { target: new targets.CodePipeline(stage.pipeline), - imageTag: this.props.imageTag ?? 'latest', + imageTag, }); // the Action Role also needs to write to the Pipeline's bucket @@ -104,7 +110,7 @@ export class EcrSourceAction extends Action { return { configuration: { RepositoryName: this.props.repository.repositoryName, - ImageTag: this.props.imageTag, + ImageTag: imageTag, }, }; } diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json new file mode 100644 index 0000000000000..b7a88e93992f9 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json @@ -0,0 +1,415 @@ +{ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyPipelineRoleC0D47CA4": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineRoleDefaultPolicy34F09EFA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE", + "Arn" + ] + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelineRoleDefaultPolicy34F09EFA", + "Roles": [ + { + "Ref": "MyPipelineRoleC0D47CA4" + } + ] + } + }, + "MyPipelineAED38ECF": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineRoleC0D47CA4", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "ECR", + "Version": "1" + }, + "Configuration": { + "RepositoryName": { + "Ref": "MyEcrRepo767466D0" + } + }, + "Name": "ECR_Source", + "OutputArtifacts": [ + { + "Name": "Artifact_Source_ECR_Source" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Approval", + "Owner": "AWS", + "Provider": "Manual", + "Version": "1" + }, + "Name": "ManualApproval", + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Approve" + } + ], + "ArtifactStore": { + "Location": { + "Ref": "MyBucketF68F3FF0" + }, + "Type": "S3" + } + }, + "DependsOn": [ + "MyPipelineRoleDefaultPolicy34F09EFA", + "MyPipelineRoleC0D47CA4" + ] + }, + "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineSourceECRSourceCodePipelineActionRoleDefaultPolicy7646B7FE": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ecr:DescribeImages", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyEcrRepo767466D0", + "Arn" + ] + } + }, + { + "Action": [ + "s3:DeleteObject*", + "s3:PutObject", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelineSourceECRSourceCodePipelineActionRoleDefaultPolicy7646B7FE", + "Roles": [ + { + "Ref": "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE" + } + ] + } + }, + "MyPipelineEventsRoleFAB99F32": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineEventsRoleDefaultPolicyF045F033": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "codepipeline:StartPipelineExecution", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codepipeline:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "MyPipelineAED38ECF" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelineEventsRoleDefaultPolicyF045F033", + "Roles": [ + { + "Ref": "MyPipelineEventsRoleFAB99F32" + } + ] + } + }, + "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyEcrRepo767466D0": { + "Type": "AWS::ECR::Repository", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "MyEcrRepoawscdkcodepipelineecrsourceMyPipeline63CF3194SourceEventRule911FDB6D": { + "Type": "AWS::Events::Rule", + "Properties": { + "EventPattern": { + "source": [ + "aws.ecr" + ], + "detail-type": [ + "AWS API Call via CloudTrail" + ], + "detail": { + "requestParameters": { + "repositoryName": [ + { + "Ref": "MyEcrRepo767466D0" + } + ] + }, + "eventName": [ + "PutImage" + ] + } + }, + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codepipeline:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "MyPipelineAED38ECF" + } + ] + ] + }, + "Id": "Target0", + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineEventsRoleFAB99F32", + "Arn" + ] + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts new file mode 100644 index 0000000000000..80f0814c69dcb --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts @@ -0,0 +1,32 @@ +import * as codepipeline from '@aws-cdk/aws-codepipeline'; +import * as ecr from '@aws-cdk/aws-ecr'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import * as cpactions from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-ecr-source'); + +const bucket = new s3.Bucket(stack, 'MyBucket', { + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); +const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline', { + artifactBucket: bucket, +}); + +const repository = new ecr.Repository(stack, 'MyEcrRepo', { + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); +const sourceStage = pipeline.addStage({ stageName: 'Source' }); +sourceStage.addAction(new cpactions.EcrSourceAction({ + actionName: 'ECR_Source', + output: new codepipeline.Artifact(), + repository, + imageTag: '', +})); + +const approveStage = pipeline.addStage({ stageName: 'Approve' }); +approveStage.addAction(new cpactions.ManualApprovalAction({ actionName: 'ManualApproval' })); + +app.synth(); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json index c200ab454d71a..2164c15ae1ede 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json @@ -113,7 +113,8 @@ "Configuration": { "RepositoryName": { "Ref": "MyEcrRepo767466D0" - } + }, + "ImageTag": "latest" }, "Name": "ECR_Source", "OutputArtifacts": [ From f109bbaae0a823c8dd606e2e5ef5ab9725b7868c Mon Sep 17 00:00:00 2001 From: Jan W Date: Thu, 4 Nov 2021 22:30:48 +0100 Subject: [PATCH 2/7] Apply suggestions from code review Co-authored-by: Adam Ruka --- .../aws-codepipeline-actions/lib/ecr/source-action.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts index 1b1ff447d4fd4..10e3d365012e3 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts @@ -35,8 +35,8 @@ export interface EcrSourceVariables { */ export interface EcrSourceActionProps extends codepipeline.CommonAwsActionProps { /** - * The image tag that will be checked for changes. Provide an empty string to - * trigger on changes to any tag. + * The image tag that will be checked for changes. + * Provide an empty string to trigger on changes to any tag. * * @default 'latest' */ @@ -110,7 +110,7 @@ export class EcrSourceAction extends Action { return { configuration: { RepositoryName: this.props.repository.repositoryName, - ImageTag: imageTag, + ImageTag: this.props.imageTag ? this.props.imageTag : undefined, // `''` is falsy in JS/TS }, }; } From 5154fcd00519e07dc5bad24e786f0c8edcce10ca Mon Sep 17 00:00:00 2001 From: Jan W Date: Fri, 5 Nov 2021 08:33:07 +0100 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Adam Ruka --- .../@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts index 10e3d365012e3..751b3ebda46ce 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts @@ -101,7 +101,7 @@ export class EcrSourceAction extends Action { this.props.repository.onCloudTrailImagePushed(Names.nodeUniqueId(stage.pipeline.node) + 'SourceEventRule', { target: new targets.CodePipeline(stage.pipeline), - imageTag, + imageTag: this.props.imageTag === '' ? undefined : (this.props.imageTag ?? 'latest'), }); // the Action Role also needs to write to the Pipeline's bucket From 34e085c639f722c952641cdf596218cc9c31f3b8 Mon Sep 17 00:00:00 2001 From: Jan Wendland Date: Fri, 5 Nov 2021 09:31:32 +0100 Subject: [PATCH 4/7] Replace integration test with unit test --- .../test/ecr/ecr-source-action.test.ts | 57 +++ ...pipeline-ecr-source-all-tags.expected.json | 415 ------------------ .../integ.pipeline-ecr-source-all-tags.ts | 32 -- 3 files changed, 57 insertions(+), 447 deletions(-) delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json delete mode 100644 packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts index b51a566d4ef14..7feba60ba365b 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts @@ -3,6 +3,7 @@ import * as codebuild from '@aws-cdk/aws-codebuild'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as ecr from '@aws-cdk/aws-ecr'; import { Stack } from '@aws-cdk/core'; +import { ABSENT } from '../../../assert-internal/lib'; import * as cpactions from '../../lib'; /* eslint-disable quote-props */ @@ -60,6 +61,62 @@ describe('ecr source action', () => { }); + expect(stack).toHaveResourceLike('AWS::Events::Rule', { + 'EventPattern': { + 'detail': { + 'requestParameters': { + 'imageTag': ['latest'], + }, + }, + }, + }); + }); + + test('watches all tags when imageTag provided as empty string', () => { + const stack = new Stack(); + + const sourceOutput = new codepipeline.Artifact(); + const ecrSourceAction = new cpactions.EcrSourceAction({ + actionName: 'Source', + output: sourceOutput, + repository: ecr.Repository.fromRepositoryName(stack, 'Repo', 'repo'), + imageTag: '', + }); + + new codepipeline.Pipeline(stack, 'Pipeline', { + stages: [ + { + stageName: 'Source', + actions: [ecrSourceAction], + }, + { + stageName: 'Build', + actions: [ + new cpactions.CodeBuildAction({ + actionName: 'Build', + project: new codebuild.PipelineProject(stack, 'MyProject'), + input: sourceOutput, + environmentVariables: { + ImageDigest: { value: ecrSourceAction.variables.imageDigest }, + }, + }), + ], + }, + ], + }); + + expect(stack).toHaveResourceLike('AWS::Events::Rule', { + 'EventPattern': { + 'source': [ + 'aws.ecr', + ], + 'detail': { + 'requestParameters': { + 'imageTag': ABSENT, + }, + }, + }, + }); }); }); }); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json deleted file mode 100644 index b7a88e93992f9..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.expected.json +++ /dev/null @@ -1,415 +0,0 @@ -{ - "Resources": { - "MyBucketF68F3FF0": { - "Type": "AWS::S3::Bucket", - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "MyPipelineRoleC0D47CA4": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "codepipeline.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "MyPipelineRoleDefaultPolicy34F09EFA": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "s3:GetObject*", - "s3:GetBucket*", - "s3:List*", - "s3:DeleteObject*", - "s3:PutObject", - "s3:Abort*" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "MyBucketF68F3FF0", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "MyBucketF68F3FF0", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01", - "Arn" - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyPipelineRoleDefaultPolicy34F09EFA", - "Roles": [ - { - "Ref": "MyPipelineRoleC0D47CA4" - } - ] - } - }, - "MyPipelineAED38ECF": { - "Type": "AWS::CodePipeline::Pipeline", - "Properties": { - "RoleArn": { - "Fn::GetAtt": [ - "MyPipelineRoleC0D47CA4", - "Arn" - ] - }, - "Stages": [ - { - "Actions": [ - { - "ActionTypeId": { - "Category": "Source", - "Owner": "AWS", - "Provider": "ECR", - "Version": "1" - }, - "Configuration": { - "RepositoryName": { - "Ref": "MyEcrRepo767466D0" - } - }, - "Name": "ECR_Source", - "OutputArtifacts": [ - { - "Name": "Artifact_Source_ECR_Source" - } - ], - "RoleArn": { - "Fn::GetAtt": [ - "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE", - "Arn" - ] - }, - "RunOrder": 1 - } - ], - "Name": "Source" - }, - { - "Actions": [ - { - "ActionTypeId": { - "Category": "Approval", - "Owner": "AWS", - "Provider": "Manual", - "Version": "1" - }, - "Name": "ManualApproval", - "RoleArn": { - "Fn::GetAtt": [ - "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01", - "Arn" - ] - }, - "RunOrder": 1 - } - ], - "Name": "Approve" - } - ], - "ArtifactStore": { - "Location": { - "Ref": "MyBucketF68F3FF0" - }, - "Type": "S3" - } - }, - "DependsOn": [ - "MyPipelineRoleDefaultPolicy34F09EFA", - "MyPipelineRoleC0D47CA4" - ] - }, - "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::", - { - "Ref": "AWS::AccountId" - }, - ":root" - ] - ] - } - } - } - ], - "Version": "2012-10-17" - } - } - }, - "MyPipelineSourceECRSourceCodePipelineActionRoleDefaultPolicy7646B7FE": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "ecr:DescribeImages", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyEcrRepo767466D0", - "Arn" - ] - } - }, - { - "Action": [ - "s3:DeleteObject*", - "s3:PutObject", - "s3:Abort*" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "MyBucketF68F3FF0", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "MyBucketF68F3FF0", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyPipelineSourceECRSourceCodePipelineActionRoleDefaultPolicy7646B7FE", - "Roles": [ - { - "Ref": "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE" - } - ] - } - }, - "MyPipelineEventsRoleFAB99F32": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "events.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - } - } - }, - "MyPipelineEventsRoleDefaultPolicyF045F033": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "codepipeline:StartPipelineExecution", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":codepipeline:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":", - { - "Ref": "MyPipelineAED38ECF" - } - ] - ] - } - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "MyPipelineEventsRoleDefaultPolicyF045F033", - "Roles": [ - { - "Ref": "MyPipelineEventsRoleFAB99F32" - } - ] - } - }, - "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::", - { - "Ref": "AWS::AccountId" - }, - ":root" - ] - ] - } - } - } - ], - "Version": "2012-10-17" - } - } - }, - "MyEcrRepo767466D0": { - "Type": "AWS::ECR::Repository", - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "MyEcrRepoawscdkcodepipelineecrsourceMyPipeline63CF3194SourceEventRule911FDB6D": { - "Type": "AWS::Events::Rule", - "Properties": { - "EventPattern": { - "source": [ - "aws.ecr" - ], - "detail-type": [ - "AWS API Call via CloudTrail" - ], - "detail": { - "requestParameters": { - "repositoryName": [ - { - "Ref": "MyEcrRepo767466D0" - } - ] - }, - "eventName": [ - "PutImage" - ] - } - }, - "State": "ENABLED", - "Targets": [ - { - "Arn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":codepipeline:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":", - { - "Ref": "MyPipelineAED38ECF" - } - ] - ] - }, - "Id": "Target0", - "RoleArn": { - "Fn::GetAtt": [ - "MyPipelineEventsRoleFAB99F32", - "Arn" - ] - } - } - ] - } - } - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts deleted file mode 100644 index 80f0814c69dcb..0000000000000 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source-all-tags.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as codepipeline from '@aws-cdk/aws-codepipeline'; -import * as ecr from '@aws-cdk/aws-ecr'; -import * as s3 from '@aws-cdk/aws-s3'; -import * as cdk from '@aws-cdk/core'; -import * as cpactions from '../lib'; - -const app = new cdk.App(); - -const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-ecr-source'); - -const bucket = new s3.Bucket(stack, 'MyBucket', { - removalPolicy: cdk.RemovalPolicy.DESTROY, -}); -const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline', { - artifactBucket: bucket, -}); - -const repository = new ecr.Repository(stack, 'MyEcrRepo', { - removalPolicy: cdk.RemovalPolicy.DESTROY, -}); -const sourceStage = pipeline.addStage({ stageName: 'Source' }); -sourceStage.addAction(new cpactions.EcrSourceAction({ - actionName: 'ECR_Source', - output: new codepipeline.Artifact(), - repository, - imageTag: '', -})); - -const approveStage = pipeline.addStage({ stageName: 'Approve' }); -approveStage.addAction(new cpactions.ManualApprovalAction({ actionName: 'ManualApproval' })); - -app.synth(); From fd8554b94c241faca6005fe69bdf8edf79d8482e Mon Sep 17 00:00:00 2001 From: Jan Wendland Date: Fri, 5 Nov 2021 09:31:40 +0100 Subject: [PATCH 5/7] Fix integration test --- .../test/integ.pipeline-ecr-source.expected.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json index 2164c15ae1ede..53c605d58da68 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json @@ -113,8 +113,7 @@ "Configuration": { "RepositoryName": { "Ref": "MyEcrRepo767466D0" - }, - "ImageTag": "latest" + } }, "Name": "ECR_Source", "OutputArtifacts": [ @@ -416,4 +415,4 @@ } } } -} \ No newline at end of file +} From 6c90a282fe5f18bbd75fe60dabb0fc7bc2f6c35c Mon Sep 17 00:00:00 2001 From: Jan Wendland Date: Fri, 5 Nov 2021 09:32:31 +0100 Subject: [PATCH 6/7] Remove imageTag variable --- .../aws-codepipeline-actions/lib/ecr/source-action.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts index 751b3ebda46ce..01c878ca9e139 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts @@ -94,11 +94,6 @@ export class EcrSourceAction extends Action { resources: [this.props.repository.repositoryArn], })); - let imageTag: string | undefined; - if (this.props.imageTag !== '') { - imageTag = this.props.imageTag ?? 'latest'; - } - this.props.repository.onCloudTrailImagePushed(Names.nodeUniqueId(stage.pipeline.node) + 'SourceEventRule', { target: new targets.CodePipeline(stage.pipeline), imageTag: this.props.imageTag === '' ? undefined : (this.props.imageTag ?? 'latest'), From d7d5711f5db0e22e8bbd8211e37d1efa3b257112 Mon Sep 17 00:00:00 2001 From: Jan Wendland Date: Sat, 6 Nov 2021 21:09:44 +0100 Subject: [PATCH 7/7] Add check for absence of imageTag in source action configuration --- .../test/ecr/ecr-source-action.test.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts index 7feba60ba365b..99160cdd4193a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/ecr/ecr-source-action.test.ts @@ -1,9 +1,9 @@ import '@aws-cdk/assert-internal/jest'; +import { ABSENT } from '@aws-cdk/assert-internal'; import * as codebuild from '@aws-cdk/aws-codebuild'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as ecr from '@aws-cdk/aws-ecr'; import { Stack } from '@aws-cdk/core'; -import { ABSENT } from '../../../assert-internal/lib'; import * as cpactions from '../../lib'; /* eslint-disable quote-props */ @@ -117,6 +117,22 @@ describe('ecr source action', () => { }, }, }); + + expect(stack).toHaveResourceLike('AWS::CodePipeline::Pipeline', { + 'Stages': [ + { + 'Name': 'Source', + 'Actions': [ + { + 'Name': 'Source', + 'Configuration': { + 'ImageTag': ABSENT, + }, + }, + ], + }, + ], + }); }); }); });