Skip to content

Commit

Permalink
feat(codebuild): improved support for ARM build images (#19052)
Browse files Browse the repository at this point in the history
Fixes #18916
Fixes #9817

### Motivation 
CDK currently has poor and hidden support for using ARM build images for CodeBuild that do not match what you can do with the Console. Currently, CDK has under LinuxBuildImage two constants not mentioned in the documentation. The constants internally map to a hidden ArmBuildImage class, which provides support for the standard CodeBuild ARM build images. That is the extent of the support, making ARM a second class citizen compared to x86-64 Linux and Windows build images as, for example, you can't use custom aarch64 ECR images.

### Changes
This pull request addresses the missing support by:
- renaming the previously hidden class ArmBuildImage to LinuxArmBuildImage (in case there are Windows ARM Build Images in the future).
- exporting LinuxArmBuildImage so it can be used.
- adding the two ARM constants present in LinuxBuildImage also to LinuxArmBuildImage. The constants are also left under LinuxBuildImage to not break backwards compatibility.
- adding the method fromEcrRepository() to support custom ARM build images.
- making the LinuxArmBuildImage closer to the LinuxBuildImage and WindowsBuildImage (built with props instead of just image name).
- updating documentation to show examples of ARM and highlighting the LinuxBuildImage is for x86-64.

### Testing
The unit test for ARM image is still valid.
----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
tmmvn committed Mar 7, 2022
1 parent 5de7b86 commit 4eac4de
Show file tree
Hide file tree
Showing 5 changed files with 458 additions and 37 deletions.
9 changes: 7 additions & 2 deletions packages/@aws-cdk/aws-codebuild/README.md
Expand Up @@ -280,11 +280,12 @@ can use the `environment` property to customize the build environment:
## Images

The CodeBuild library supports both Linux and Windows images via the
`LinuxBuildImage` and `WindowsBuildImage` classes, respectively.
`LinuxBuildImage` (or `LinuxArmBuildImage`), and `WindowsBuildImage` classes, respectively.

You can specify one of the predefined Windows/Linux images by using one
of the constants such as `WindowsBuildImage.WIN_SERVER_CORE_2019_BASE`,
`WindowsBuildImage.WINDOWS_BASE_2_0` or `LinuxBuildImage.STANDARD_2_0`.
`WindowsBuildImage.WINDOWS_BASE_2_0`, `LinuxBuildImage.STANDARD_2_0`, or
`LinuxArmBuildImage.AMAZON_LINUX_2_ARM`.

Alternatively, you can specify a custom image using one of the static methods on
`LinuxBuildImage`:
Expand All @@ -302,6 +303,10 @@ or one of the corresponding methods on `WindowsBuildImage`:
* `WindowsBuildImage.fromEcrRepository(repo[, tag, imageType])`
* `WindowsBuildImage.fromAsset(parent, id, props, [, imageType])`

or one of the corresponding methods on `LinuxArmBuildImage`:

* `LinuxArmBuildImage.fromEcrRepository(repo[, tag])`

Note that the `WindowsBuildImage` version of the static methods accepts an optional parameter of type `WindowsImageType`,
which can be either `WindowsImageType.STANDARD`, the default, or `WindowsImageType.SERVER_2019`:

Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-codebuild/lib/index.ts
Expand Up @@ -11,6 +11,7 @@ export * from './build-spec';
export * from './file-location';
export * from './linux-gpu-build-image';
export * from './untrusted-code-boundary-policy';
export * from './linux-arm-build-image';

// AWS::CodeBuild CloudFormation Resources:
export * from './codebuild.generated';
105 changes: 105 additions & 0 deletions packages/@aws-cdk/aws-codebuild/lib/linux-arm-build-image.ts
@@ -0,0 +1,105 @@
import * as ecr from '@aws-cdk/aws-ecr';
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import { BuildSpec } from './build-spec';
import { runScriptLinuxBuildSpec } from './private/run-script-linux-build-spec';
import { BuildEnvironment, ComputeType, IBuildImage, ImagePullPrincipalType } from './project';

/**
* Construction properties of {@link LinuxArmBuildImage}.
* Module-private, as the constructor of {@link LinuxArmBuildImage} is private.
*/
interface LinuxArmBuildImageProps {
readonly imageId: string;
readonly imagePullPrincipalType?: ImagePullPrincipalType;
readonly secretsManagerCredentials?: secretsmanager.ISecret;
readonly repository?: ecr.IRepository;
}

/**
* A CodeBuild image running aarch64 Linux.
*
* This class has a bunch of public constants that represent the CodeBuild ARM images.
*
* You can also specify a custom image using the static method:
*
* - LinuxBuildImage.fromEcrRepository(repo[, tag])
*
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html
*/
export class LinuxArmBuildImage implements IBuildImage {
/** Image "aws/codebuild/amazonlinux2-aarch64-standard:1.0". */
public static readonly AMAZON_LINUX_2_STANDARD_1_0 = LinuxArmBuildImage.fromCodeBuildImageId('aws/codebuild/amazonlinux2-aarch64-standard:1.0');
/** Image "aws/codebuild/amazonlinux2-aarch64-standard:2.0". */
public static readonly AMAZON_LINUX_2_STANDARD_2_0 = LinuxArmBuildImage.fromCodeBuildImageId('aws/codebuild/amazonlinux2-aarch64-standard:2.0');

/**
* Returns an ARM image running Linux from an ECR repository.
*
* NOTE: if the repository is external (i.e. imported), then we won't be able to add
* a resource policy statement for it so CodeBuild can pull the image.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
*
* @param repository The ECR repository
* @param tag Image tag (default "latest")
* @returns An aarch64 Linux build image from an ECR repository.
*/
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage {
return new LinuxArmBuildImage({
imageId: repository.repositoryUriForTag(tag),
imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE,
repository,
});
}

/**
* Uses a Docker image provided by CodeBuild.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html
*
* @param id The image identifier
* @example 'aws/codebuild/amazonlinux2-aarch64-standard:1.0'
* @returns A Docker image provided by CodeBuild.
*/
public static fromCodeBuildImageId(id: string): IBuildImage {
return new LinuxArmBuildImage({
imageId: id,
imagePullPrincipalType: ImagePullPrincipalType.CODEBUILD,
});
}

public readonly type = 'ARM_CONTAINER';
public readonly defaultComputeType = ComputeType.LARGE;
public readonly imageId: string;
public readonly imagePullPrincipalType?: ImagePullPrincipalType;
public readonly secretsManagerCredentials?: secretsmanager.ISecret;
public readonly repository?: ecr.IRepository;

private constructor(props: LinuxArmBuildImageProps) {
this.imageId = props.imageId;
this.imagePullPrincipalType = props.imagePullPrincipalType;
this.secretsManagerCredentials = props.secretsManagerCredentials;
this.repository = props.repository;
}

/**
* Validates by checking the BuildEnvironment computeType as aarch64 images only support ComputeType.SMALL and
* ComputeType.LARGE
* @param buildEnvironment BuildEnvironment
*/
public validate(buildEnvironment: BuildEnvironment): string[] {
const ret = [];
if (buildEnvironment.computeType &&
buildEnvironment.computeType !== ComputeType.SMALL &&
buildEnvironment.computeType !== ComputeType.LARGE) {
ret.push(`ARM images only support ComputeTypes '${ComputeType.SMALL}' and '${ComputeType.LARGE}' - ` +
`'${buildEnvironment.computeType}' was given`);
}
return ret;
}

public runScriptBuildspec(entrypoint: string): BuildSpec {
return runScriptLinuxBuildSpec(entrypoint);
}
}
52 changes: 17 additions & 35 deletions packages/@aws-cdk/aws-codebuild/lib/project.ts
Expand Up @@ -1642,32 +1642,6 @@ export interface IBindableBuildImage extends IBuildImage {
bind(scope: CoreConstruct, project: IProject, options: BuildImageBindOptions): BuildImageConfig;
}

class ArmBuildImage implements IBuildImage {
public readonly type = 'ARM_CONTAINER';
public readonly defaultComputeType = ComputeType.LARGE;
public readonly imagePullPrincipalType = ImagePullPrincipalType.CODEBUILD;
public readonly imageId: string;

constructor(imageId: string) {
this.imageId = imageId;
}

public validate(buildEnvironment: BuildEnvironment): string[] {
const ret = [];
if (buildEnvironment.computeType &&
buildEnvironment.computeType !== ComputeType.SMALL &&
buildEnvironment.computeType !== ComputeType.LARGE) {
ret.push(`ARM images only support ComputeTypes '${ComputeType.SMALL}' and '${ComputeType.LARGE}' - ` +
`'${buildEnvironment.computeType}' was given`);
}
return ret;
}

public runScriptBuildspec(entrypoint: string): BuildSpec {
return runScriptLinuxBuildSpec(entrypoint);
}
}

/**
* The options when creating a CodeBuild Docker build image
* using {@link LinuxBuildImage.fromDockerRegistry}
Expand Down Expand Up @@ -1695,8 +1669,12 @@ interface LinuxBuildImageProps {
readonly repository?: ecr.IRepository;
}

// Keep around to resolve a circular dependency until removing deprecated ARM image constants from LinuxBuildImage
// eslint-disable-next-line no-duplicate-imports, import/order
import { LinuxArmBuildImage } from './linux-arm-build-image';

/**
* A CodeBuild image running Linux.
* A CodeBuild image running x86-64 Linux.
*
* This class has a bunch of public constants that represent the most popular images.
*
Expand All @@ -1723,9 +1701,13 @@ export class LinuxBuildImage implements IBuildImage {
/** The Amazon Linux 2 x86_64 standard image, version `3.0`. */
public static readonly AMAZON_LINUX_2_3 = LinuxBuildImage.codeBuildImage('aws/codebuild/amazonlinux2-x86_64-standard:3.0');

public static readonly AMAZON_LINUX_2_ARM: IBuildImage = new ArmBuildImage('aws/codebuild/amazonlinux2-aarch64-standard:1.0');
/** Image "aws/codebuild/amazonlinux2-aarch64-standard:2.0". */
public static readonly AMAZON_LINUX_2_ARM_2: IBuildImage = new ArmBuildImage('aws/codebuild/amazonlinux2-aarch64-standard:2.0');
/** @deprecated Use LinuxArmBuildImage.AMAZON_LINUX_2_STANDARD_1_0 instead. */
public static readonly AMAZON_LINUX_2_ARM = LinuxArmBuildImage.AMAZON_LINUX_2_STANDARD_1_0;
/**
* Image "aws/codebuild/amazonlinux2-aarch64-standard:2.0".
* @deprecated Use LinuxArmBuildImage.AMAZON_LINUX_2_STANDARD_2_0 instead.
* */
public static readonly AMAZON_LINUX_2_ARM_2 = LinuxArmBuildImage.AMAZON_LINUX_2_STANDARD_2_0;

/** @deprecated Use {@link STANDARD_2_0} and specify runtime in buildspec runtime-versions section */
public static readonly UBUNTU_14_04_BASE = LinuxBuildImage.codeBuildImage('aws/codebuild/ubuntu-base:14.04');
Expand Down Expand Up @@ -1789,7 +1771,7 @@ export class LinuxBuildImage implements IBuildImage {
public static readonly UBUNTU_14_04_DOTNET_CORE_2_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/dot-net:core-2.1');

/**
* @returns a Linux build image from a Docker Hub image.
* @returns a x86-64 Linux build image from a Docker Hub image.
*/
public static fromDockerRegistry(name: string, options: DockerImageOptions = {}): IBuildImage {
return new LinuxBuildImage({
Expand All @@ -1800,7 +1782,7 @@ export class LinuxBuildImage implements IBuildImage {
}

/**
* @returns A Linux build image from an ECR repository.
* @returns A x86-64 Linux build image from an ECR repository.
*
* NOTE: if the repository is external (i.e. imported), then we won't be able to add
* a resource policy statement for it so CodeBuild can pull the image.
Expand All @@ -1819,7 +1801,7 @@ export class LinuxBuildImage implements IBuildImage {
}

/**
* Uses an Docker image asset as a Linux build image.
* Uses an Docker image asset as a x86-64 Linux build image.
*/
public static fromAsset(scope: Construct, id: string, props: DockerImageAssetProps): IBuildImage {
const asset = new DockerImageAsset(scope, id, props);
Expand Down Expand Up @@ -1961,7 +1943,7 @@ export class WindowsBuildImage implements IBuildImage {
}

/**
* @returns A Linux build image from an ECR repository.
* @returns A Windows build image from an ECR repository.
*
* NOTE: if the repository is external (i.e. imported), then we won't be able to add
* a resource policy statement for it so CodeBuild can pull the image.
Expand Down Expand Up @@ -2125,4 +2107,4 @@ export enum ProjectNotificationEvents {

function isBindableBuildImage(x: unknown): x is IBindableBuildImage {
return typeof x === 'object' && !!x && !!(x as any).bind;
}
}

0 comments on commit 4eac4de

Please sign in to comment.