From 1263846b6d2b1534c1104e7ba70250bc95e67c0f Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Tue, 28 Apr 2020 16:52:13 -0700 Subject: [PATCH] feat(codebuild): add support for test reports This adds support for CodeBuild's test reports feature. It includes a new Layer 2 class, ReportGroup. The default project's IAM role has been augmented with permissions to create and use test result groups whose names begin with the project's name (as that is what the default report group created when putting a simple name in the 'reports' buildspec section is called). There is a new boolean property when creating the project, addCreateReportGroupPermissions, that can be used to turn off this behavor. Fixes #7367 --- packages/@aws-cdk/aws-codebuild/README.md | 62 +++++++ packages/@aws-cdk/aws-codebuild/lib/index.ts | 1 + .../@aws-cdk/aws-codebuild/lib/project.ts | 30 ++++ .../aws-codebuild/lib/report-group-utils.ts | 17 ++ .../aws-codebuild/lib/report-group.ts | 152 ++++++++++++++++++ .../aws-codebuild/test/integ.caching.ts | 1 + .../test/integ.defaults.lit.expected.json | 35 +++- .../test/integ.docker-asset.lit.ts | 1 + .../test/integ.docker-registry.lit.ts | 1 + .../aws-codebuild/test/integ.ecr.lit.ts | 1 + .../aws-codebuild/test/integ.github.ts | 1 + .../test/integ.project-bucket.ts | 1 + .../test/integ.project-buildspec-artifacts.ts | 1 + .../integ.project-file-system-location.ts | 1 + ...teg.project-secondary-sources-artifacts.ts | 1 + .../aws-codebuild/test/integ.project-vpc.ts | 1 + .../aws-codebuild/test/test.codebuild.ts | 67 ++++++++ .../aws-codebuild/test/test.project.ts | 44 +++++ .../aws-codebuild/test/test.report-group.ts | 148 +++++++++++++++++ ...yed-through-codepipeline.lit.expected.json | 68 +++++++- ...line-code-build-multiple-inputs-outputs.ts | 4 +- .../test/integ.pipeline-code-commit-build.ts | 4 +- .../test/integ.pipeline-ecs-deploy.ts | 1 + .../test/integ.pipeline-events.ts | 4 +- .../test/test.pipeline.ts | 3 + .../integ.project-events.expected.json | 35 +++- .../test/__snapshots__/synth.test.js.snap | 33 ++++ 27 files changed, 712 insertions(+), 6 deletions(-) create mode 100644 packages/@aws-cdk/aws-codebuild/lib/report-group-utils.ts create mode 100644 packages/@aws-cdk/aws-codebuild/lib/report-group.ts create mode 100644 packages/@aws-cdk/aws-codebuild/test/test.report-group.ts diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index b61d736c11d29..73b4632722fc4 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -269,6 +269,68 @@ any attempt to save more than one will result in an error. You can use the [`list-source-credentials` AWS CLI operation](https://docs.aws.amazon.com/cli/latest/reference/codebuild/list-source-credentials.html) to inspect what credentials are stored in your account. +## Test reports + +You can specify a test report in your buildspec: + +```typescript +const project = new codebuild.Project(this, 'Project', { + buildSpec: codebuild.BuildSpec.fromObject({ + // ... + reports: { + myReport: { + files: '**/*', + 'base-directory': 'build/test-results', + }, + }, + }), +}); +``` + +This will create a new test report group, +with the name `-myReport`. + +The project's role in the CDK will always be granted permissions to create and use report groups +with names starting with the project's name; +if you'd rather not have those permissions added, +you can opt out of it when creating the project: + +```typescript +const project = new codebuild.Project(this, 'Project', { + // ... + grantReportGroupPermissions: false, +}); +``` + +Alternatively, you can specify an ARN of an existing resource group, +instead of a simple name, in your buildspec: + +```typescript +// create a new ReportGroup +const reportGroup = new codebuild.ReportGroup(this, 'ReportGroup'); + +const project = new codebuild.Project(this, 'Project', { + buildSpec: codebuild.BuildSpec.fromObject({ + // ... + reports: { + [reportGroup.reportGroupArn]: { + files: '**/*', + 'base-directory': 'build/test-results', + }, + }, + }), +}); +``` + +If you do that, you need to grant the project's role permissions to write reports to that report group: + +```typescript +reportGroup.grantWrite(project); +``` + +For more information on the test reports feature, +see the [AWS CodeBuild documentation](https://docs.aws.amazon.com/codebuild/latest/userguide/test-reporting.html). + ## Events CodeBuild projects can be used either as a source for events or be triggered diff --git a/packages/@aws-cdk/aws-codebuild/lib/index.ts b/packages/@aws-cdk/aws-codebuild/lib/index.ts index a1d569e5656d1..8ecdd6a743476 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/index.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/index.ts @@ -1,6 +1,7 @@ export * from './events'; export * from './pipeline-project'; export * from './project'; +export * from './report-group'; export * from './source'; export * from './source-credentials'; export * from './artifacts'; diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 7d1fef9a3f6dc..985177e86d8eb 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -16,6 +16,7 @@ import { CodePipelineArtifacts } from './codepipeline-artifacts'; import { IFileSystemLocation } from './file-location'; import { NoArtifacts } from './no-artifacts'; import { NoSource } from './no-source'; +import { renderReportGroupArn } from './report-group-utils'; import { ISource } from './source'; import { CODEPIPELINE_SOURCE_ARTIFACTS_TYPE, NO_SOURCE_TYPE } from './source-types'; @@ -519,6 +520,21 @@ export interface CommonProjectProps { * @default - no file system locations */ readonly fileSystemLocations?: IFileSystemLocation[]; + + /** + * Add permissions to this project's role to create and use test report groups with name starting with the name of this project. + * + * That is the standard report group that gets created when a simple name + * (in contrast to an ARN) + * is used in the 'reports' section of the buildspec of this project. + * This is usually harmless, but you can turn these off if you don't plan on using test + * reports in this project. + * + * @default true + * + * @see https://docs.aws.amazon.com/codebuild/latest/userguide/test-report-group-naming.html + */ + readonly grantReportGroupPermissions?: boolean; } export interface ProjectProps extends CommonProjectProps { @@ -762,6 +778,20 @@ export class Project extends ProjectBase { this.projectName = this.getResourceNameAttribute(resource.ref); this.addToRolePolicy(this.createLoggingPermission()); + // add permissions to create and use test report groups + // with names starting with the project's name, + // unless the customer explicitly opts out of it + if (props.grantReportGroupPermissions !== false) { + this.addToRolePolicy(new iam.PolicyStatement({ + actions: [ + 'codebuild:CreateReportGroup', + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + ], + resources: [renderReportGroupArn(this, `${this.projectName}-*`)], + })); + } if (props.encryptionKey) { this.encryptionKey = props.encryptionKey; diff --git a/packages/@aws-cdk/aws-codebuild/lib/report-group-utils.ts b/packages/@aws-cdk/aws-codebuild/lib/report-group-utils.ts new file mode 100644 index 0000000000000..c830737d14649 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/lib/report-group-utils.ts @@ -0,0 +1,17 @@ +import * as cdk from '@aws-cdk/core'; + +// this file contains a bunch of functions shared +// between Project and ResourceGroup, +// which we don't want to make part of the public API of this module + +export function renderReportGroupArn(scope: cdk.Construct, reportGroupName: string): string { + return cdk.Stack.of(scope).formatArn(reportGroupArnComponents(reportGroupName)); +} + +export function reportGroupArnComponents(reportGroupName: string): cdk.ArnComponents { + return { + service: 'codebuild', + resource: 'report-group', + resourceName: reportGroupName, + }; +} diff --git a/packages/@aws-cdk/aws-codebuild/lib/report-group.ts b/packages/@aws-cdk/aws-codebuild/lib/report-group.ts new file mode 100644 index 0000000000000..aa643b3e65cce --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/lib/report-group.ts @@ -0,0 +1,152 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import { CfnReportGroup } from './codebuild.generated'; +import { renderReportGroupArn, reportGroupArnComponents } from './report-group-utils'; + +/** + * The interface representing the ReportGroup resource - + * either an existing one, imported using the + * {@link ReportGroup.fromReportGroupName} method, + * or a new one, created with the {@link ReportGroup} class. + */ +export interface IReportGroup extends cdk.IResource { + /** + * The ARN of the ReportGroup. + * + * @attribute + */ + readonly reportGroupArn: string; + + /** + * The name of the ReportGroup. + * + * @attribute + */ + readonly reportGroupName: string; + + /** + * Grants the given entity permissions to write + * (that is, upload reports to) + * this report group. + */ + grantWrite(identity: iam.IGrantable): iam.Grant; +} + +abstract class ReportGroupBase extends cdk.Resource implements IReportGroup { + public abstract readonly reportGroupArn: string; + public abstract readonly reportGroupName: string; + protected abstract readonly exportBucket?: s3.IBucket; + + public grantWrite(identity: iam.IGrantable): iam.Grant { + const ret = iam.Grant.addToPrincipal({ + grantee: identity, + actions: [ + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + ], + resourceArns: [this.reportGroupArn], + }); + + if (this.exportBucket) { + this.exportBucket.grantWrite(identity); + } + + return ret; + } +} + +/** + * Construction properties for {@link ReportGroup}. + */ +export interface ReportGroupProps { + /** + * The physical name of the report group. + * + * @default - CloudFormation-generated name + */ + readonly reportGroupName?: string; + + /** + * An optional S3 bucket to export the reports to. + * + * @default - the reports will not be exported + */ + readonly exportBucket?: s3.IBucket; + + /** + * Whether to output the report files into the export bucket as-is, + * or create a ZIP from them before doing the export. + * Ignored if {@link exportBucket} has not been provided. + * + * @default - false (the files will not be ZIPped) + */ + readonly zipExport?: boolean; + + /** + * What to do when this resource is deleted from a stack. + * As CodeBuild does not allow deleting a ResourceGroup that has reports inside of it, + * this is set to retain the resource by default. + * + * @default RemovalPolicy.RETAIN + */ + readonly removalPolicy?: cdk.RemovalPolicy; +} + +/** + * The ReportGroup resource class. + */ +export class ReportGroup extends ReportGroupBase { + + /** + * Reference an existing ReportGroup, + * defined outside of the CDK code, + * by name. + */ + public static fromReportGroupName(scope: cdk.Construct, id: string, reportGroupName: string): IReportGroup { + class Import extends ReportGroupBase { + public readonly reportGroupName = reportGroupName; + public readonly reportGroupArn = renderReportGroupArn(scope, reportGroupName); + protected readonly exportBucket = undefined; + } + + return new Import(scope, id); + } + + public readonly reportGroupArn: string; + public readonly reportGroupName: string; + protected readonly exportBucket?: s3.IBucket; + + constructor(scope: cdk.Construct, id: string, props: ReportGroupProps = {}) { + super(scope, id, { + physicalName: props.reportGroupName, + }); + + const resource = new CfnReportGroup(this, 'Resource', { + type: 'TEST', + exportConfig: { + exportConfigType: props.exportBucket ? 'S3' : 'NO_EXPORT', + s3Destination: props.exportBucket + ? { + bucket: props.exportBucket.bucketName, + encryptionDisabled: props.exportBucket.encryptionKey ? false : undefined, + encryptionKey: props.exportBucket.encryptionKey?.keyArn, + packaging: props.zipExport ? 'ZIP' : undefined, + } + : undefined, + }, + }); + resource.applyRemovalPolicy(props.removalPolicy, { + default: cdk.RemovalPolicy.RETAIN, + }); + this.reportGroupArn = this.getResourceArnAttribute(resource.attrArn, + reportGroupArnComponents(this.physicalName)); + this.reportGroupName = this.getResourceNameAttribute( + // there is no separate name attribute, + // so use Fn::Select + Fn::Split to make one + cdk.Fn.select(1, cdk.Fn.split('/', resource.ref)), + ); + this.exportBucket = props.exportBucket; + } +} diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts b/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts index 249f470212653..f2436d6cc4504 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.caching.ts @@ -22,6 +22,7 @@ new codebuild.Project(stack, 'MyProject', { paths: ['/root/.cache/pip/**/*'], }, }), + grantReportGroupPermissions: false, }); app.synth(); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.defaults.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.defaults.lit.expected.json index 03af104a5737e..aae808b132920 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.defaults.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.defaults.lit.expected.json @@ -78,6 +78,39 @@ ] } ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "MyProject39F7B0AE" + }, + "-*" + ] + ] + } } ], "Version": "2012-10-17" @@ -115,4 +148,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.ts b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.ts index d5bad90630949..a57ac77c1063d 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.ts @@ -15,6 +15,7 @@ class TestStack extends cdk.Stack { }, }, }), + grantReportGroupPermissions: false, /// !show environment: { buildImage: codebuild.LinuxBuildImage.fromAsset(this, 'MyImage', { diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts b/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts index d375dc2045cf2..efbbb01548647 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts @@ -18,6 +18,7 @@ class TestStack extends cdk.Stack { }, }, }), + grantReportGroupPermissions: false, /// !show environment: { buildImage: codebuild.LinuxBuildImage.fromDockerRegistry('my-registry/my-repo', { diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.ts b/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.ts index 20258a243e460..49319ff4d60c9 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.ts @@ -17,6 +17,7 @@ class TestStack extends cdk.Stack { }, }, }), + grantReportGroupPermissions: false, /// !show environment: { buildImage: codebuild.LinuxBuildImage.fromEcrRepository(ecrRepository, 'v1.0'), diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.github.ts b/packages/@aws-cdk/aws-codebuild/test/integ.github.ts index 22d99577468a3..e41cc722c0065 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.github.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.github.ts @@ -12,6 +12,7 @@ class TestStack extends cdk.Stack { }); new codebuild.Project(this, 'MyProject', { source, + grantReportGroupPermissions: false, }); } } diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts index 0a0eb19125b82..e78655e82f823 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-bucket.ts @@ -19,6 +19,7 @@ new codebuild.Project(stack, 'MyProject', { environment: { computeType: codebuild.ComputeType.LARGE, }, + grantReportGroupPermissions: false, }); app.synth(); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.ts index 6cf42d7a3e102..b72b25271086e 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-buildspec-artifacts.ts @@ -14,6 +14,7 @@ new codebuild.Project(stack, 'MyProject', { buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', }), + grantReportGroupPermissions: false, artifacts: codebuild.Artifacts.s3({ bucket, diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-file-system-location.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-file-system-location.ts index da84c9b39f4e4..02a6d747d361b 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-file-system-location.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-file-system-location.ts @@ -31,6 +31,7 @@ new codebuild.Project(stack, 'MyProject', { mountOptions: 'nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2', mountPoint: '/media', })], + grantReportGroupPermissions: false, }); app.synth(); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.ts index a69b389473fe2..cc552733c7912 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-secondary-sources-artifacts.ts @@ -29,6 +29,7 @@ new codebuild.Project(stack, 'MyProject', { identifier: 'AddArtifact1', }), ], + grantReportGroupPermissions: false, }); app.synth(); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts b/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts index 7784fed4779da..1e0b9cc2bc424 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts +++ b/packages/@aws-cdk/aws-codebuild/test/integ.project-vpc.ts @@ -25,6 +25,7 @@ new codebuild.Project(stack, 'MyProject', { }, }, }), + grantReportGroupPermissions: false, securityGroups: [securityGroup], vpc, }); diff --git a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts index 29cf00c193719..7badf7c9b8f46 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts @@ -99,6 +99,28 @@ export = { }, ], }, + { + 'Action': [ + 'codebuild:CreateReportGroup', + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + ], + 'Effect': 'Allow', + 'Resource': { + 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':codebuild:', + { 'Ref': 'AWS::Region' }, + ':', + { 'Ref': 'AWS::AccountId' }, + ':report-group/', + { 'Ref': 'MyProject39F7B0AE' }, + '-*', + ]], + }, + }, ], 'Version': '2012-10-17', }, @@ -248,6 +270,28 @@ export = { }, ], }, + { + 'Action': [ + 'codebuild:CreateReportGroup', + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + ], + 'Effect': 'Allow', + 'Resource': { + 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':codebuild:', + { 'Ref': 'AWS::Region' }, + ':', + { 'Ref': 'AWS::AccountId' }, + ':report-group/', + { 'Ref': 'MyProject39F7B0AE' }, + '-*', + ]], + }, + }, ], 'Version': '2012-10-17', }, @@ -423,6 +467,28 @@ export = { }, ], }, + { + 'Action': [ + 'codebuild:CreateReportGroup', + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + ], + 'Effect': 'Allow', + 'Resource': { + 'Fn::Join': ['', [ + 'arn:', + { 'Ref': 'AWS::Partition' }, + ':codebuild:', + { 'Ref': 'AWS::Region' }, + ':', + { 'Ref': 'AWS::AccountId' }, + ':report-group/', + { 'Ref': 'MyProject39F7B0AE' }, + '-*', + ]], + }, + }, ], 'Version': '2012-10-17', }, @@ -737,6 +803,7 @@ export = { new codebuild.PipelineProject(stack, 'MyProject', { encryptionKey: key, + grantReportGroupPermissions: false, }); expect(stack).to(haveResourceLike('AWS::IAM::Policy', { diff --git a/packages/@aws-cdk/aws-codebuild/test/test.project.ts b/packages/@aws-cdk/aws-codebuild/test/test.project.ts index 08c62c0c4093a..19dd60ed5e758 100644 --- a/packages/@aws-cdk/aws-codebuild/test/test.project.ts +++ b/packages/@aws-cdk/aws-codebuild/test/test.project.ts @@ -496,4 +496,48 @@ export = { test.done(); }, + + 'CodeBuild test reports group': { + 'adds the appropriate permissions when reportGroup.grantWrite() is called'(test: Test) { + const stack = new cdk.Stack(); + + const reportGroup = new codebuild.ReportGroup(stack, 'ReportGroup'); + + const project = new codebuild.Project(stack, 'Project', { + buildSpec: codebuild.BuildSpec.fromObject({ + version: '0.2', + reports: { + [reportGroup.reportGroupArn]: { + files: '**/*', + }, + }, + }), + grantReportGroupPermissions: false, + }); + reportGroup.grantWrite(project); + + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': [ + {}, + { + 'Action': [ + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + ], + 'Resource': { + 'Fn::GetAtt': [ + 'ReportGroup8A84C76D', + 'Arn', + ], + }, + }, + ], + }, + })); + + test.done(); + }, + }, }; diff --git a/packages/@aws-cdk/aws-codebuild/test/test.report-group.ts b/packages/@aws-cdk/aws-codebuild/test/test.report-group.ts new file mode 100644 index 0000000000000..1e942413cc08f --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/test.report-group.ts @@ -0,0 +1,148 @@ +import { ABSENT, expect, haveResourceLike, ResourcePart } from '@aws-cdk/assert'; +import * as iam from '@aws-cdk/aws-iam'; +import * as kms from '@aws-cdk/aws-kms'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as cdk from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import * as codebuild from '../lib'; + +// tslint:disable:object-literal-key-quotes +/* eslint-disable quotes */ + +export = { + 'Test Reports Groups': { + 'get created with type=TEST and exportConfig=NO_EXPORT by default'(test: Test) { + const stack = new cdk.Stack(); + + new codebuild.ReportGroup(stack, 'ReportGroup'); + + expect(stack).to(haveResourceLike('AWS::CodeBuild::ReportGroup', { + "Type": "TEST", + "ExportConfig": { + "ExportConfigType": "NO_EXPORT", + "S3Destination": ABSENT, + }, + })); + + test.done(); + }, + + 'can be imported by name'(test: Test) { + const stack = new cdk.Stack(); + + const reportGroup = codebuild.ReportGroup.fromReportGroupName(stack, + 'ReportGroup', 'my-report-group'); + + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.AnyPrincipal(), + }); + role.addToPolicy(new iam.PolicyStatement({ + actions: ['codebuild:*'], + resources: [reportGroup.reportGroupArn], + })); + + test.equal(reportGroup.reportGroupName, 'my-report-group'); + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + "PolicyDocument": { + "Statement": [ + { + "Action": "codebuild:*", + "Resource": { + "Fn::Join": ["", [ + "arn:", + { "Ref": "AWS::Partition" }, + ":codebuild:", + { "Ref": "AWS::Region" }, + ":", + { "Ref": "AWS::AccountId" }, + ":report-group/my-report-group", + ]], + }, + }, + ], + }, + })); + + test.done(); + }, + + 'specify exportConfig=S3 when providing an exportBucket'(test: Test) { + const stack = new cdk.Stack(); + + new codebuild.ReportGroup(stack, 'ReportGroup', { + exportBucket: s3.Bucket.fromBucketName(stack, 'Bucket', 'my-bucket'), + }); + + expect(stack).to(haveResourceLike('AWS::CodeBuild::ReportGroup', { + "Type": "TEST", + "ExportConfig": { + "ExportConfigType": "S3", + "S3Destination": { + "Bucket": "my-bucket", + "EncryptionKey": ABSENT, + "EncryptionDisabled": ABSENT, + "Packaging": ABSENT, + }, + }, + })); + + test.done(); + }, + + 'specify encryptionKey in ExportConfig.S3Destination if exportBucket has a Key'(test: Test) { + const stack = new cdk.Stack(); + + new codebuild.ReportGroup(stack, 'ReportGroup', { + exportBucket: s3.Bucket.fromBucketAttributes(stack, 'Bucket', { + bucketName: 'my-bucket', + encryptionKey: kms.Key.fromKeyArn(stack, 'Key', + 'arn:aws:kms:us-east-1:123456789012:key/my-key'), + }), + zipExport: true, + }); + + expect(stack).to(haveResourceLike('AWS::CodeBuild::ReportGroup', { + "Type": "TEST", + "ExportConfig": { + "ExportConfigType": "S3", + "S3Destination": { + "Bucket": "my-bucket", + "EncryptionDisabled": false, + "EncryptionKey": "arn:aws:kms:us-east-1:123456789012:key/my-key", + "Packaging": "ZIP", + }, + }, + })); + + test.done(); + }, + + 'get created with RemovalPolicy.RETAIN by default'(test: Test) { + const stack = new cdk.Stack(); + + new codebuild.ReportGroup(stack, 'ReportGroup'); + + expect(stack).to(haveResourceLike('AWS::CodeBuild::ReportGroup', { + "DeletionPolicy": "Retain", + "UpdateReplacePolicy": "Retain", + }, ResourcePart.CompleteDefinition)); + + test.done(); + }, + + 'can be created with RemovalPolicy.DESTROY'(test: Test) { + const stack = new cdk.Stack(); + + new codebuild.ReportGroup(stack, 'ReportGroup', { + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + expect(stack).to(haveResourceLike('AWS::CodeBuild::ReportGroup', { + "DeletionPolicy": "Delete", + "UpdateReplacePolicy": "Delete", + }, ResourcePart.CompleteDefinition)); + + test.done(); + }, + }, +}; diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json index 36763b820707d..437b7116f5501 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json @@ -1441,6 +1441,39 @@ } ] }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "CdkBuildProject9382C38D" + }, + "-*" + ] + ] + } + }, { "Action": [ "s3:GetObject*", @@ -1625,6 +1658,39 @@ } ] }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "LambdaBuildProject7E2DAB11" + }, + "-*" + ] + ] + } + }, { "Action": [ "s3:GetObject*", @@ -1731,4 +1797,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts index 4103f477d4bd4..c09c1a3328545 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.ts @@ -45,7 +45,9 @@ pipeline.addStage({ ], }); -const project = new codebuild.PipelineProject(stack, 'MyBuildProject'); +const project = new codebuild.PipelineProject(stack, 'MyBuildProject', { + grantReportGroupPermissions: false, +}); const buildAction = new cpactions.CodeBuildAction({ actionName: 'Build1', project, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts index 7299b08576528..33311f6a5a7d6 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.ts @@ -21,7 +21,9 @@ const sourceAction = new cpactions.CodeCommitSourceAction({ trigger: cpactions.CodeCommitTrigger.POLL, }); -const project = new codebuild.PipelineProject(stack, 'MyBuildProject'); +const project = new codebuild.PipelineProject(stack, 'MyBuildProject', { + grantReportGroupPermissions: false, +}); const buildAction = new cpactions.CodeBuildAction({ actionName: 'build', project, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.ts index 0f7f41277463e..4f5eba55965c1 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.ts @@ -72,6 +72,7 @@ const project = new codebuild.PipelineProject(stack, 'EcsProject', { value: repository.repositoryUri, }, }, + grantReportGroupPermissions: false, }); // needed for `docker push` repository.grantPullPush(project); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts index 4b7d258c0d67d..399ebad05aa4f 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.ts @@ -18,7 +18,9 @@ const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline'); const repository = new codecommit.Repository(stack, 'CodeCommitRepo', { repositoryName: 'foo', }); -const project = new codebuild.PipelineProject(stack, 'BuildProject'); +const project = new codebuild.PipelineProject(stack, 'BuildProject', { + grantReportGroupPermissions: false, +}); const sourceOutput = new codepipeline.Artifact('Source'); const sourceAction = new cpactions.CodeCommitSourceAction({ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts index 73f3de20ee7e2..64f9c44a41147 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/test.pipeline.ts @@ -911,6 +911,9 @@ export = { { // log permissions from the CodeBuild Project Construct... }, + { + // report group permissions from the CodeBuild Project construct... + }, { 'Action': [ 's3:GetObject*', diff --git a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json index 6f5b91fffaefa..178bba14bbc1c 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json @@ -155,6 +155,39 @@ ] } ] + }, + { + "Action": [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "MyProject39F7B0AE" + }, + "-*" + ] + ] + } } ], "Version": "2012-10-17" @@ -386,4 +419,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/decdk/test/__snapshots__/synth.test.js.snap b/packages/decdk/test/__snapshots__/synth.test.js.snap index c181ff0751808..e625105f784f6 100644 --- a/packages/decdk/test/__snapshots__/synth.test.js.snap +++ b/packages/decdk/test/__snapshots__/synth.test.js.snap @@ -1744,6 +1744,39 @@ Object { }, ], }, + Object { + "Action": Array [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases", + ], + "Effect": "Allow", + "Resource": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":codebuild:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":report-group/", + Object { + "Ref": "BuildProject097C5DB7", + }, + "-*", + ], + ], + }, + }, Object { "Action": Array [ "kms:Decrypt",