Skip to content

Commit 354694a

Browse files
committed
fix(codebuild): prevent fleet and batch build combination
1 parent 8b495f9 commit 354694a

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

packages/aws-cdk-lib/aws-codebuild/lib/project.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ export interface IProject extends IResource, iam.IGrantable, ec2.IConnectable, n
8383
/** The IAM service Role of this Project. Undefined for imported Projects. */
8484
readonly role?: iam.IRole;
8585

86+
/**
87+
* If true, batch builds have been enabled.
88+
*
89+
* @default false - batch builds have not been enabled.
90+
*/
91+
readonly isBatchBuildEnabled: boolean;
92+
8693
/**
8794
* Enable batch builds.
8895
*
@@ -268,6 +275,12 @@ abstract class ProjectBase extends Resource implements IProject {
268275
*/
269276
protected _connections: ec2.Connections | undefined;
270277

278+
/**
279+
* Actual value will be determined for a Project
280+
* using a getter depending on the effect of enableBatchBuilds
281+
*/
282+
public readonly isBatchBuildEnabled = false;
283+
271284
/**
272285
* Access the Connections object.
273286
* Will fail if this Project does not have a VPC set.
@@ -1045,6 +1058,7 @@ export class Project extends ProjectBase {
10451058
private readonly _secondarySources: CfnProject.SourceProperty[];
10461059
private readonly _secondarySourceVersions: CfnProject.ProjectSourceVersionProperty[];
10471060
private readonly _secondaryArtifacts: CfnProject.ArtifactsProperty[];
1061+
private readonly _environment?: CfnProject.EnvironmentProperty;
10481062
private _encryptionKey?: kms.IKey;
10491063
private readonly _fileSystemLocations: CfnProject.ProjectFileSystemLocationProperty[];
10501064
private _batchServiceRole?: iam.Role;
@@ -1107,6 +1121,8 @@ export class Project extends ProjectBase {
11071121
this.addFileSystemLocation(fileSystemLocation);
11081122
}
11091123

1124+
this._environment = this.renderEnvironment(props, environmentVariables);
1125+
11101126
const resource = new CfnProject(this, 'Resource', {
11111127
description: props.description,
11121128
source: {
@@ -1115,7 +1131,7 @@ export class Project extends ProjectBase {
11151131
},
11161132
artifacts: artifactsConfig.artifactsProperty,
11171133
serviceRole: this.role.roleArn,
1118-
environment: this.renderEnvironment(props, environmentVariables),
1134+
environment: this._environment,
11191135
fileSystemLocations: Lazy.any({ produce: () => this.renderFileSystemLocations() }),
11201136
// lazy, because we have a setter for it in setEncryptionKey
11211137
// The 'alias/aws/s3' default is necessary because leaving the `encryptionKey` field
@@ -1204,7 +1220,15 @@ export class Project extends ProjectBase {
12041220
this.node.addValidation({ validate: () => this.validateProject() });
12051221
}
12061222

1223+
public get isBatchBuildsEnabled(): boolean {
1224+
return !!this._batchServiceRole;
1225+
}
1226+
12071227
public enableBatchBuilds(): BatchBuildConfig | undefined {
1228+
if (this._environment?.fleet) {
1229+
throw new Error('Build batch is not supported for project using reserved capacity');
1230+
}
1231+
12081232
if (!this._batchServiceRole) {
12091233
this._batchServiceRole = new iam.Role(this, 'BatchServiceRole', {
12101234
assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
@@ -1426,6 +1450,13 @@ export class Project extends ProjectBase {
14261450
return undefined;
14271451
}
14281452

1453+
// !! Failsafe !! This should not occur with the current methods, given:
1454+
// * The fleet can only be set with the constructor
1455+
// * The batch builds can only be enabled with the `enableBatchBuilds` method
1456+
if (this.isBatchBuildEnabled) {
1457+
throw new Error('Build batch is not supported for project using reserved capacity');
1458+
}
1459+
14291460
// If the fleetArn is resolved, the fleet is imported and we cannot validate the environment type
14301461
if (Token.isUnresolved(fleet.fleetArn) && this.buildImage.type !== fleet.environmentType) {
14311462
throw new Error(`The environment type of the fleet (${fleet.environmentType}) must match the environment type of the build image (${this.buildImage.type})`);

packages/aws-cdk-lib/aws-codebuild/test/codebuild.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Match, Template } from '../../assertions';
22
import * as codecommit from '../../aws-codecommit';
3+
import * as codepipeline from '../../aws-codepipeline';
4+
import * as cpactions from '../../aws-codepipeline-actions';
35
import * as ec2 from '../../aws-ec2';
46
import * as kms from '../../aws-kms';
57
import * as s3 from '../../aws-s3';
@@ -1595,6 +1597,65 @@ test('environment variables can be overridden at the project level', () => {
15951597
});
15961598
});
15971599

1600+
test('throws when batch builds are enabled for a reserved capacity project', () => {
1601+
// GIVEN
1602+
const stack = new cdk.Stack();
1603+
const fleet = codebuild.Fleet.fromFleetArn(stack, 'Fleet', 'arn:aws:codebuild:us-east-1:123456789012:fleet/MyFleet:uuid');
1604+
const bucket = new s3.Bucket(stack, 'MyBucket');
1605+
1606+
// WHEN
1607+
const project = new codebuild.Project(stack, 'MyProject', {
1608+
source: codebuild.Source.s3({
1609+
bucket,
1610+
path: 'path/to/source.zip',
1611+
}),
1612+
environment: {
1613+
fleet,
1614+
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
1615+
},
1616+
});
1617+
1618+
// THEN
1619+
expect(() => {
1620+
project.enableBatchBuilds();
1621+
}).toThrow(/Build batch is not supported for project using reserved capacity/);
1622+
});
1623+
1624+
test('throws when pipeline are enabled for a reserved capacity project', () => {
1625+
// GIVEN
1626+
const stack = new cdk.Stack();
1627+
const fleet = codebuild.Fleet.fromFleetArn(stack, 'Fleet', 'arn:aws:codebuild:us-east-1:123456789012:fleet/MyFleet:uuid');
1628+
const sourceOutput = new codepipeline.Artifact();
1629+
const pipeline = new codepipeline.Pipeline(stack, 'Pipeline', {
1630+
artifactBucket: new s3.Bucket(stack, 'Bucket', {
1631+
versioned: true,
1632+
removalPolicy: cdk.RemovalPolicy.DESTROY,
1633+
}),
1634+
});
1635+
1636+
// WHEN
1637+
const project = new codebuild.PipelineProject(stack, 'MyProject', {
1638+
environment: {
1639+
fleet,
1640+
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
1641+
},
1642+
});
1643+
1644+
// THEN
1645+
expect(() => {
1646+
pipeline.addStage({
1647+
stageName: 'Build',
1648+
actions: [new cpactions.CodeBuildAction({
1649+
actionName: 'Build',
1650+
project,
1651+
executeBatchBuild: true,
1652+
input: sourceOutput,
1653+
role: pipeline.role,
1654+
})],
1655+
});
1656+
}).toThrow(/Build batch is not supported for project using reserved capacity/);
1657+
});
1658+
15981659
test('.metricXxx() methods can be used to obtain Metrics for CodeBuild projects', () => {
15991660
const stack = new cdk.Stack();
16001661

@@ -2043,11 +2104,13 @@ test('enableBatchBuilds()', () => {
20432104
repo: 'testrepo',
20442105
}),
20452106
});
2107+
expect(project.isBatchBuildsEnabled).toBeFalsy();
20462108

20472109
const returnVal = project.enableBatchBuilds();
20482110
if (!returnVal?.role) {
20492111
throw new Error('Expecting return value with role');
20502112
}
2113+
expect(project.isBatchBuildsEnabled).toBeTruthy();
20512114

20522115
Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
20532116
BuildBatchConfig: {

0 commit comments

Comments
 (0)