Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(codebuild): Fleet L2 #29754

Merged
merged 26 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
80af386
chore(codebuild): images
nmussy Mar 26, 2024
21af1c0
chore: add missing `@link` JSDoc tags
nmussy Mar 26, 2024
5e7e84e
chore: deprecate Win 2022 image
nmussy Mar 26, 2024
663f833
chore: Windows image integ tests
nmussy Mar 26, 2024
a70bfae
feat(codebuild): fleet (wip)
nmussy Apr 6, 2024
5764582
chore: fleet unit tests
nmussy Apr 7, 2024
233989f
fix: working integ
nmussy Apr 7, 2024
ad59b22
chore: README
nmussy Apr 7, 2024
a1b675d
chore: refactor, environmentType check, additional unit tests, integ …
nmussy Apr 10, 2024
a8a04c8
chore: docs/new lines
nmussy Apr 10, 2024
a05fab4
chore: README fix
nmussy Apr 10, 2024
00e5ad9
docs: fixes
nmussy Apr 13, 2024
42d5574
docs: fixes
nmussy Apr 13, 2024
23dd3fe
docs: remove double space
nmussy Apr 13, 2024
346ad1f
chore: refactor
nmussy Apr 13, 2024
8391abb
Merge remote-tracking branch 'upstream/main' into feat-codebuild-fleet
nmussy Apr 18, 2024
2c49ca1
Merge remote-tracking branch 'origin/chore-codebuild-images' into fea…
nmussy Apr 18, 2024
411de2d
docs: FleetComputeType
nmussy Apr 18, 2024
cfd336e
chore: integs
nmussy Apr 18, 2024
7503899
chore: validate Windows 2022 usage with fleets
nmussy Apr 20, 2024
b59d41e
chore: update test name
nmussy Apr 23, 2024
e8d651b
Merge remote-tracking branch 'upstream/main' into feat-codebuild-fleet
nmussy Apr 24, 2024
513cd2b
fix: remove lazy
nmussy Apr 24, 2024
908942f
docs: computeType
nmussy May 6, 2024
ff3664e
docs: environmentType
nmussy May 6, 2024
117f24b
Merge branch 'main' into feat-codebuild-fleet
mergify[bot] Jun 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as cdk from 'aws-cdk-lib';
import * as integ from '@aws-cdk/integ-tests-alpha';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'aws-cdk-project-fleet');

const fleet = new codebuild.Fleet(stack, 'MyFleet', {
baseCapacity: 1,
computeType: codebuild.FleetComputeType.SMALL,
environmentType: codebuild.FleetEnvironmentType.LINUX_CONTAINER,
});

new codebuild.Project(stack, 'MyProject', {
buildSpec: codebuild.BuildSpec.fromObject({
version: '0.2',
phases: {
build: {
commands: ['echo "Nothing to do!"'],
},
},
}),
environment: {
fleet,
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
// TODO probably an error, add check
// computeType: codebuild.ComputeType.SMALL,
},
});

new integ.IntegTest(app, 'FleetIntegTest', {
testCases: [stack],
});

app.synth();
193 changes: 193 additions & 0 deletions packages/aws-cdk-lib/aws-codebuild/lib/fleet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { Construct } from 'constructs';
import { CfnFleet } from './codebuild.generated';
import { Arn, ArnFormat, IResource, Resource, Token } from '../../core';

/**
* Construction properties of a CodeBuild {@link Fleet}.
*/
export interface FleetProps {
/**
* The name of the Fleet.
*
* @default - CloudFormation generated name
*/
readonly fleetName?: string;

/**
* The number of machines allocated to the compute fleet.
* Defines the number of builds that can run in parallel.
*
* Minimum value of 1.
nmussy marked this conversation as resolved.
Show resolved Hide resolved
*/
readonly baseCapacity: number;

/**
* The instance type of the compute fleet.
*/
readonly computeType: FleetComputeType;

/**
* The environment type of the fleet.
*/
readonly environmentType: FleetEnvironmentType;
}

/**
* Represents a {@link Fleet} for a reserved capacity CodeBuild project.
*/
export interface IFleet extends IResource {
/**
* The ARN of the fleet
nmussy marked this conversation as resolved.
Show resolved Hide resolved
* @attribute
*/
readonly fleetArn: string;

/**
* The name of the fleet
nmussy marked this conversation as resolved.
Show resolved Hide resolved
* @attribute
*/
readonly fleetName: string;
}

/**
* Fleet for a reserved capacity CodeBuild project.
*
* Fleets allow for process builds or tests to run immediately and reduces build durations,
* by reserving compute resources for your projects.
*
* You will be charged for the resources in the fleet, even if they are idle.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/fleets.html
*/
export class Fleet extends Resource implements IFleet {
/**
* Creates a Fleet construct that represents an external fleet.
*
* @param scope The scope creating construct (usually `this`).
* @param id The construct's id.
* @param fleetArn arn of external fleet.
nmussy marked this conversation as resolved.
Show resolved Hide resolved
*/
public static fromFleetArn(scope: Construct, id: string, fleetArn: string): IFleet {
class Import extends Resource implements IFleet {
public readonly fleetName = Arn.split(fleetArn, ArnFormat.SLASH_RESOURCE_NAME).resourceName!.split(':')[0];
public readonly fleetArn = fleetArn;
}

return new Import(scope, id);
}

/**
* The ARN of the fleet
*/
public readonly fleetArn: string;

/**
* The name of the fleet
*/
public readonly fleetName: string;

constructor(scope: Construct, id: string, props: FleetProps) {
if (props.fleetName && !Token.isUnresolved(props.fleetName)) {
if (props.fleetName.length < 2) {
throw new Error(`Fleet name can not be shorter than 2 characters but has ${props.fleetName.length} characters.`);
}
if (props.fleetName.length > 128) {
throw new Error(`Fleet name can not be longer than 128 characters but has ${props.fleetName.length} characters.`);
}
}

super(scope, id, { physicalName: props.fleetName });

if ((props.baseCapacity ?? 1) < 1) {
throw new Error('baseCapacity must be greater than or equal to 1');
}
nmussy marked this conversation as resolved.
Show resolved Hide resolved

// TODO check computeType and environmentType compatibility

const resource = new CfnFleet(this, 'Resource', {
name: props.fleetName,
baseCapacity: props.baseCapacity,
computeType: props.computeType,
environmentType: props.environmentType,
});

this.fleetArn = this.getResourceArnAttribute(resource.attrArn, {
service: 'codebuild',
resource: 'fleet',
resourceName: this.physicalName,
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
});
this.fleetName = this.getResourceNameAttribute(resource.ref);
}
}

/**
* The environment type of the fleet
*/
export enum FleetEnvironmentType {
/**
* ARM container environment
*/
ARM_CONTAINER = 'ARM_CONTAINER',

/**
* Linux container environment
*/
LINUX_CONTAINER = 'LINUX_CONTAINER',

/**
* Linux GPU container environment
*/
LINUX_GPU_CONTAINER = 'LINUX_GPU_CONTAINER',

/**
* Windows Server 2019 container environment
*/
WINDOWS_SERVER_2019_CONTAINER = 'WINDOWS_SERVER_2019_CONTAINER',

/**
* Windows Server 2022 container environment
*/
WINDOWS_SERVER_2022_CONTAINER = 'WINDOWS_SERVER_2022_CONTAINER',
}

/**
* Build machine compute type.
*/
export enum FleetComputeType {
/**
* TODO
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
*/
SMALL = 'BUILD_GENERAL1_SMALL',

/**
* TODO
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
*/
MEDIUM = 'BUILD_GENERAL1_MEDIUM',

/**
* TODO
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
*/
LARGE = 'BUILD_GENERAL1_LARGE',

/**
* TODO
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
*/
X_LARGE = 'BUILD_GENERAL1_XLARGE',

/**
* TODO
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
*/
X2_LARGE = 'BUILD_GENERAL1_2XLARGE',

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see note above. worth moving all the enums into enums.ts ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 change: 1 addition & 0 deletions packages/aws-cdk-lib/aws-codebuild/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export * from './linux-arm-build-image';
export * from './linux-lambda-build-image';
export * from './linux-arm-lambda-build-image';
export * from './compute-type';
export * from './fleet';

// AWS::CodeBuild CloudFormation Resources:
export * from './codebuild.generated';
24 changes: 24 additions & 0 deletions packages/aws-cdk-lib/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CfnProject } from './codebuild.generated';
import { CodePipelineArtifacts } from './codepipeline-artifacts';
import { ComputeType } from './compute-type';
import { IFileSystemLocation } from './file-location';
import { IFleet } from './fleet';
import { LinuxArmLambdaBuildImage } from './linux-arm-lambda-build-image';
import { LinuxLambdaBuildImage } from './linux-lambda-build-image';
import { NoArtifacts } from './no-artifacts';
Expand Down Expand Up @@ -1379,6 +1380,7 @@ export class Project extends ProjectBase {
: undefined,
certificate: env.certificate?.bucket.arnForObjects(env.certificate.objectKey),
privilegedMode: env.privileged || false,
fleet: Lazy.any({ produce: () => this.configureFleet(env) }),
computeType: env.computeType || this.buildImage.defaultComputeType,
environmentVariables: hasEnvironmentVars
? Project.serializeEnvVariables(vars, props.checkSecretsInPlainTextEnvVariables ?? true, this)
Expand Down Expand Up @@ -1410,6 +1412,15 @@ export class Project extends ProjectBase {
: this._secondaryArtifacts;
}

private configureFleet({ fleet }: BuildEnvironment): CfnProject.ProjectFleetProperty | undefined {
if (!fleet) {
return undefined;
}

const { fleetArn } = fleet;
return { fleetArn };
}

/**
* If configured, set up the VPC-related properties
*
Expand Down Expand Up @@ -1612,6 +1623,19 @@ export interface BuildEnvironment {
*/
readonly computeType?: ComputeType;

/**
* Fleet resource for a reserved capacity CodeBuild project.
*
* Fleets allow for process builds or tests to run immediately and reduces build durations,
* by reserving compute resources for your projects.
*
* You will be charged for the resources in the fleet, even if they are idle.
*
* @default - No fleet will be attached to the project, which will remain on-demand.
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/fleets.html
*/
readonly fleet?: IFleet;

/**
* Indicates how the project builds Docker images. Specify true to enable
* running the Docker daemon inside a Docker container. This value must be
Expand Down
105 changes: 105 additions & 0 deletions packages/aws-cdk-lib/aws-codebuild/test/fleet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Match, Template } from '../../assertions';
import * as cdk from '../../core';
import * as codebuild from '../lib';

test('can construct a default fleet', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new codebuild.Fleet(stack, 'Fleet', {
computeType: codebuild.FleetComputeType.SMALL,
environmentType: codebuild.FleetEnvironmentType.LINUX_CONTAINER,
baseCapacity: 1,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Fleet', {
Name: Match.absent(),
BaseCapacity: 1,
ComputeType: 'BUILD_GENERAL1_SMALL',
EnvironmentType: 'LINUX_CONTAINER',
});
});

test('can construct a fleet with properties', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new codebuild.Fleet(stack, 'Fleet', {
fleetName: 'MyFleet',
baseCapacity: 2,
computeType: codebuild.FleetComputeType.SMALL,
environmentType: codebuild.FleetEnvironmentType.LINUX_CONTAINER,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Fleet', {
Name: 'MyFleet',
BaseCapacity: 2,
ComputeType: 'BUILD_GENERAL1_SMALL',
EnvironmentType: 'LINUX_CONTAINER',
});
});

test('can import with a concrete ARN', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const fleet = codebuild.Fleet.fromFleetArn(stack, 'Fleet',
'arn:aws:codebuild:us-east-1:123456789012:fleet/MyFleet:298f98fb-ba69-4381-a663-c8d517dd61be',
);

// THEN
expect(fleet.fleetArn).toEqual(
'arn:aws:codebuild:us-east-1:123456789012:fleet/MyFleet:298f98fb-ba69-4381-a663-c8d517dd61be',
);
expect(fleet.fleetName).toEqual('MyFleet');
});

test('throws if fleet name is longer than 128 characters', () => {
// GIVEN
const stack = new cdk.Stack();

// THEN
expect(() => {
new codebuild.Fleet(stack, 'Fleet', {
fleetName: 'a'.repeat(129),
computeType: codebuild.FleetComputeType.SMALL,
environmentType: codebuild.FleetEnvironmentType.LINUX_CONTAINER,
baseCapacity: 1,
});
}).toThrow(/Fleet name can not be longer than 128 characters but has 129 characters./);
});

test('throws if fleet name is shorter than 2 characters', () => {
// GIVEN
const stack = new cdk.Stack();

// THEN
expect(() => {
new codebuild.Fleet(stack, 'Fleet', {
fleetName: 'a',
computeType: codebuild.FleetComputeType.SMALL,
environmentType: codebuild.FleetEnvironmentType.LINUX_CONTAINER,
baseCapacity: 1,
});
}).toThrow(/Fleet name can not be shorter than 2 characters but has 1 characters./);
});

test('throws if baseCapacity is less than 1', () => {
// GIVEN
const stack = new cdk.Stack();

// THEN
expect(() => {
new codebuild.Fleet(stack, 'Fleet', {
fleetName: 'MyFleet',
computeType: codebuild.FleetComputeType.SMALL,
environmentType: codebuild.FleetEnvironmentType.LINUX_CONTAINER,
baseCapacity: 0,
});
}).toThrow(/baseCapacity must be greater than or equal to 1/);
});
Loading