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",