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

Add AWS CodeBuild Project #68

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion assets/build-image/ubuntu_22_04/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
gawk wget git diffstat unzip texinfo gcc build-essential chrpath \
socat cpio python3 python3-pip python3-pexpect xz-utils debianutils \
iputils-ping python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev \
python3-subunit mesa-common-dev zstd liblz4-tool file locales
python3-subunit mesa-common-dev zstd liblz4-tool file locales xterm

# Install packages used elsewhere in the build
RUN apt-get install -y --no-install-recommends \
Expand Down
2 changes: 1 addition & 1 deletion lib/build-image-repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class BuildImageRepoStack extends cdk.Stack {

this.repository = new ecr.Repository(this, 'BuildImageRepo', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteImages: true,
emptyOnDelete: true,
});
}
}
2 changes: 2 additions & 0 deletions lib/constructs/source-repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export enum ProjectKind {
Renesas = 'renesas',
/** Build an IMX image using NXP layers. */
NxpImx = 'nxp-imx',
/** Build no pipeline, just CodeBuild project to connect with GitHub actions. */
CodeBuild = 'codebuild',
}

export interface SourceRepoProps extends cdk.StackProps {
Expand Down
265 changes: 265 additions & 0 deletions lib/embedded-linux-codebuild-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as events from "aws-cdk-lib/aws-events";
import * as targets from "aws-cdk-lib/aws-events-targets";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as iam from "aws-cdk-lib/aws-iam";
import * as efs from "aws-cdk-lib/aws-efs";
import * as kms from "aws-cdk-lib/aws-kms";

Check warning on line 8 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'kms' is defined but never used

Check warning on line 8 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'kms' is defined but never used

Check warning on line 8 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'kms' is defined but never used

Check warning on line 8 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'kms' is defined but never used

Check warning on line 8 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'kms' is defined but never used

Check warning on line 8 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'kms' is defined but never used
import * as s3 from "aws-cdk-lib/aws-s3";

import {
BuildSpec,
ComputeType,
FileSystemLocation,
LinuxBuildImage,
Project,
} from "aws-cdk-lib/aws-codebuild";
import { IRepository } from "aws-cdk-lib/aws-ecr";

import {
ISecurityGroup,
IVpc,
Peer,
Port,
SecurityGroup,
} from "aws-cdk-lib/aws-ec2";
import { ProjectKind } from "./constructs/source-repo";
import { VMImportBucket } from "./vm-import-bucket";
import { Asset } from "aws-cdk-lib/aws-s3-assets";

Check warning on line 29 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'Asset' is defined but never used

Check warning on line 29 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'Asset' is defined but never used

Check warning on line 29 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'Asset' is defined but never used

Check warning on line 29 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'Asset' is defined but never used

Check warning on line 29 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'Asset' is defined but never used

Check warning on line 29 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'Asset' is defined but never used
import { LogGroup, RetentionDays } from "aws-cdk-lib/aws-logs";
import { RemovalPolicy } from "aws-cdk-lib";

Check warning on line 31 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'RemovalPolicy' is defined but never used

Check warning on line 31 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'RemovalPolicy' is defined but never used

Check warning on line 31 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'RemovalPolicy' is defined but never used

Check warning on line 31 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'RemovalPolicy' is defined but never used

Check warning on line 31 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'RemovalPolicy' is defined but never used

Check warning on line 31 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'RemovalPolicy' is defined but never used

/**
* Properties to allow customizing the build.
*/
export interface EmbeddedLinuxCodebuildProjectProps
extends cdk.StackProps {
/** ECR Repository where the Build Host Image resides. */
readonly imageRepo: IRepository;
/** Tag for the Build Host Image */
readonly imageTag?: string;
/** VPC where the networking setup resides. */
readonly vpc: IVpc;
/** The type of project being built. */
readonly projectKind?: ProjectKind;
/** A name for the layer-repo that is created. Default is 'layer-repo' */
readonly layerRepoName?: string;
/** Additional policy statements to add to the build project. */
readonly buildPolicyAdditions?: iam.PolicyStatement[];
/** Access logging bucket to use */
readonly accessLoggingBucket?: s3.Bucket;
/** Access logging prefix to use */
readonly serverAccessLogsPrefix?: string;
/** Artifact bucket to use */
readonly artifactBucket?: s3.Bucket;
/** Output bucket to use */
readonly outputBucket?: s3.Bucket | VMImportBucket;
/** Prefix for S3 object within bucket */
readonly subDirectoryName?: string;
}

/**
* The stack for creating a build pipeline.
*
* See {@link EmbeddedLinuxCodebuildProjectProps} for configration options.
*/
export class EmbeddedLinuxCodebuildProjectStack extends cdk.Stack {
constructor(
scope: Construct,
id: string,
props: EmbeddedLinuxCodebuildProjectProps
) {
super(scope, id, props);

/** Set up networking access and EFS FileSystems. */

const projectSg = new SecurityGroup(this, "BuildProjectSecurityGroup", {
vpc: props.vpc,
description: "Security Group to allow attaching EFS",
});
projectSg.addIngressRule(
Peer.ipv4(props.vpc.vpcCidrBlock),
Port.tcp(2049),
"NFS Mount Port"
);

const sstateFS = this.addFileSystem("SState", props.vpc, projectSg);
const dlFS = this.addFileSystem("Downloads", props.vpc, projectSg);
const tmpFS = this.addFileSystem("Temp", props.vpc, projectSg);

let accessLoggingBucket: s3.IBucket;

if (props.accessLoggingBucket) {
accessLoggingBucket = props.accessLoggingBucket;
} else {
accessLoggingBucket = new s3.Bucket(this, "ArtifactAccessLogging", {

Check warning on line 96 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'accessLoggingBucket' is assigned a value but never used

Check warning on line 96 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'accessLoggingBucket' is assigned a value but never used

Check warning on line 96 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'accessLoggingBucket' is assigned a value but never used

Check warning on line 96 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (16.x)

'accessLoggingBucket' is assigned a value but never used

Check warning on line 96 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (18.x)

'accessLoggingBucket' is assigned a value but never used

Check warning on line 96 in lib/embedded-linux-codebuild-project.ts

View workflow job for this annotation

GitHub Actions / Run-CDK-Tests (20.x)

'accessLoggingBucket' is assigned a value but never used
versioned: true,
enforceSSL: true,
});
}

/** Create our CodeBuild Project. */
const project = new Project(
this,
"EmbeddedLinuxCodebuildProject",
{
buildSpec: BuildSpec.fromObject({
version: "0.2",
phases: {
build: {
commands: ['echo "DUMMY BUILDSPEC - can not be empty"'],
},
},
artifacts: {
files: ["**/*"],
"base-directory": ".",
},
}),
environment: {
computeType: ComputeType.X2_LARGE,
buildImage: LinuxBuildImage.fromEcrRepository(
props.imageRepo,
props.imageTag
),
privileged: true,
},
timeout: cdk.Duration.hours(4),
vpc: props.vpc,
securityGroups: [projectSg],
fileSystemLocations: [
FileSystemLocation.efs({
identifier: "tmp_dir",
location: tmpFS,
mountPoint: "/build-output",
}),
FileSystemLocation.efs({
identifier: "sstate_cache",
location: sstateFS,
mountPoint: "/sstate-cache",
}),
FileSystemLocation.efs({
identifier: "dl_dir",
location: dlFS,
mountPoint: "/downloads",
}),
],
logging: {
cloudWatch: {
logGroup: new LogGroup(this, "PipelineBuildLogs", {
retention: RetentionDays.TEN_YEARS,
}),
},
},
}
);

if (props.buildPolicyAdditions) {
props.buildPolicyAdditions.map((p) => project.addToRolePolicy(p));
}

project.addToRolePolicy(this.addProjectPolicies());

project.role?.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName("AWSCodeBuildAdminAccess")
);

/** Here we create the logic to check for presence of ECR image on the CodePipeline automatic triggering upon resource creation,
* and stop the execution if the image does not exist. */
const fnOnPipelineCreate = new lambda.Function(
this,
"OSImageCheckOnStart",
{
runtime: lambda.Runtime.PYTHON_3_10,
handler: "index.handler",
code: lambda.Code.fromInline(`
import boto3
import json

ecr_client = boto3.client('ecr')
codepipeline_client = boto3.client('codepipeline')

def handler(event, context):
print("Received event: " + json.dumps(event, indent=2))
response = ecr_client.describe_images(repositoryName='${props.imageRepo.repositoryName}', filter={'tagStatus': 'TAGGED'})
for i in response['imageDetails']:
if '${props.imageTag}' in i['imageTags']:
break
else:
print('OS image not found. Stopping execution.')
response = codepipeline_client.stop_pipeline_execution(
pipelineName=event['detail']['pipeline'],
pipelineExecutionId=event['detail']['execution-id'],
abandon=True,
reason='OS image not found in ECR repository. Stopping pipeline until image is present.')
`),
logRetention: RetentionDays.TEN_YEARS,
}
);

const pipelineCreateRule = new events.Rule(this, "OnPipelineStartRule", {
eventPattern: {
detailType: ["CodePipeline Pipeline Execution State Change"],
source: ["aws.codepipeline"],
detail: {
state: ["STARTED"],
"execution-trigger": {
"trigger-type": ["CreatePipeline"],
},
},
},
});
pipelineCreateRule.addTarget(
new targets.LambdaFunction(fnOnPipelineCreate)
);
}

/**
* Adds an EFS FileSystem to the VPC and SecurityGroup.
*
* @param name - A name to differentiate the filesystem.
* @param vpc - The VPC the Filesystem resides in.
* @param securityGroup - A SecurityGroup to allow access to the filesystem from.
* @returns The filesystem location URL.
*
*/
private addFileSystem(
name: string,
vpc: IVpc,
securityGroup: ISecurityGroup
): string {
const fs = new efs.FileSystem(
this,
`EmbeddedLinuxPipeline${name}Filesystem`,
{
vpc,
removalPolicy: cdk.RemovalPolicy.DESTROY,
}
);

fs.connections.allowFrom(securityGroup, Port.tcp(2049));

const fsId = fs.fileSystemId;
const region = cdk.Stack.of(this).region;

return `${fsId}.efs.${region}.amazonaws.com:/`;
}

private addProjectPolicies(): iam.PolicyStatement {
return new iam.PolicyStatement({
actions: [
"ec2:DescribeSecurityGroups",
"codestar-connections:GetConnection",
"codestar-connections:GetConnectionToken",
"codeconnections:GetConnectionToken",
"codeconnections:GetConnection",
"codeconnections:UseConnection",
"codebuild:ListConnectedOAuthAccounts",
"codebuild:ListRepositories",
"codebuild:PersistOAuthToken",
"codebuild:ImportSourceCredentials",
],
resources: ["*"],
});
}
}
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './build-image-data';
export * from './build-image-repo';
export * from './build-image-pipeline';
export * from './embedded-linux-pipeline';
export * from './embedded-linux-codebuild-project';
export * from './constructs/source-repo';
Loading
Loading