Skip to content

Commit d25b5ea

Browse files
committed
CodeBuild Setup for GitHub Docker Image Builds
1 parent 483d379 commit d25b5ea

File tree

11 files changed

+321
-62
lines changed

11 files changed

+321
-62
lines changed

tests/ci/cdk/app.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,8 @@
1111
from cdk.windows_docker_image_build_stack import WindowsDockerImageBuildStack
1212
from cdk.ecr_stack import EcrStack
1313
from util.metadata import (
14-
LINUX_X86_ECR_REPO,
15-
LINUX_AARCH_ECR_REPO,
16-
WINDOWS_X86_ECR_REPO,
1714
PIPELINE_ACCOUNT,
1815
PIPELINE_REGION,
19-
DEPLOY_ACCOUNT,
20-
DEPLOY_REGION,
2116
)
2217

2318
# Initialize app.
@@ -29,23 +24,4 @@
2924
env=Environment(account=PIPELINE_ACCOUNT, region=PIPELINE_REGION),
3025
)
3126

32-
if DEPLOY_ACCOUNT and DEPLOY_REGION:
33-
# Initialize env.
34-
env = Environment(account=DEPLOY_ACCOUNT, region=DEPLOY_REGION)
35-
36-
# Define AWS ECR stacks.
37-
# ECR holds the docker images, which are pre-built to accelerate the code builds/tests of git pull requests.
38-
EcrStack(app, "aws-lc-ecr-linux-x86", LINUX_X86_ECR_REPO, env=env)
39-
EcrStack(app, "aws-lc-ecr-linux-aarch", LINUX_AARCH_ECR_REPO, env=env)
40-
EcrStack(app, "aws-lc-ecr-windows-x86", WINDOWS_X86_ECR_REPO, env=env)
41-
42-
# Define CodeBuild Batch job for building Docker images.
43-
LinuxDockerImageBatchBuildStack(app, "aws-lc-docker-image-build-linux", env=env)
44-
45-
# AWS CodeBuild cannot build Windows Docker images because DIND (Docker In Docker) is not supported on Windows.
46-
# Windows Docker images are created by running commands in Windows EC2 instance.
47-
WindowsDockerImageBuildStack(app, "aws-lc-docker-image-build-windows", env=env)
48-
49-
add_ci_stacks(app, env=env)
50-
5127
app.synth()

tests/ci/cdk/cdk/aws_lc_github_actions_stack.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44

55
from aws_cdk import (
66
Duration,
7-
Stack,
87
aws_codebuild as codebuild,
98
aws_iam as iam,
10-
aws_s3_assets,
119
aws_logs as logs,
10+
aws_ecr as ecr,
1211
Environment,
1312
)
1413
from constructs import Construct
@@ -18,7 +17,7 @@
1817
from util.iam_policies import (
1918
code_build_publish_metrics_in_json,
2019
)
21-
from util.metadata import LINUX_X86_ECR_REPO, LINUX_AARCH_ECR_REPO, WINDOWS_X86_ECR_REPO
20+
from util.metadata import AMAZONLINUX_ECR_REPO, CENTOS_ECR_REPO, FEDORA_ECR_REPO, LINUX_X86_ECR_REPO, LINUX_AARCH_ECR_REPO, UBUNTU_ECR_REPO, WINDOWS_X86_ECR_REPO
2221

2322
class AwsLcGitHubActionsStack(AwsLcBaseCiStack):
2423
"""Define a stack used to execute AWS-LC self-hosted GitHub Actions Runners."""
@@ -32,6 +31,12 @@ def __init__(
3231
) -> None:
3332
super().__init__(scope, id, env=env, timeout=180, **kwargs)
3433

34+
# TODO: First 3 indices ordering is important for now as they are referenced directly for now.
35+
repo_names = [LINUX_X86_ECR_REPO, LINUX_AARCH_ECR_REPO, WINDOWS_X86_ECR_REPO, UBUNTU_ECR_REPO,
36+
AMAZONLINUX_ECR_REPO, CENTOS_ECR_REPO, FEDORA_ECR_REPO]
37+
ecr_repos = [ecr.Repository.from_repository_name(self, x.replace('/', '-'), repository_name=x)
38+
for x in repo_names]
39+
3540
# Define a IAM role for this stack.
3641
metrics_policy = iam.PolicyDocument.from_json(
3742
code_build_publish_metrics_in_json(env)
@@ -55,12 +60,7 @@ def __init__(
5560
"ecr:BatchCheckLayerAvailability",
5661
"ecr:GetDownloadUrlForLayer",
5762
],
58-
resources=[
59-
"arn:aws:ecr:{}:{}:repository/{}"
60-
.format(env.region, env.account, repo) for repo in [LINUX_X86_ECR_REPO,
61-
LINUX_AARCH_ECR_REPO,
62-
WINDOWS_X86_ECR_REPO]
63-
],
63+
resources=[x.repository_arn for x in ecr_repos],
6464
),
6565
],
6666
)
@@ -105,16 +105,18 @@ def __init__(
105105
environment_variables={
106106
"AWS_ACCOUNT_ID": codebuild.BuildEnvironmentVariable(value=env.account),
107107
"AWS_ECR_REPO_LINUX_X86": codebuild.BuildEnvironmentVariable(
108-
value="{}.dkr.ecr.{}.amazonaws.com/{}".format(env.account, env.region, LINUX_X86_ECR_REPO)
108+
value=ecr_repos[0].repository_uri
109109
),
110110
"AWS_ECR_REPO_LINUX_AARCH": codebuild.BuildEnvironmentVariable(
111-
value="{}.dkr.ecr.{}.amazonaws.com/{}".format(env.account, env.region, LINUX_AARCH_ECR_REPO)
111+
value=ecr_repos[1].repository_uri
112112
),
113113
"AWS_ECR_REPO_WINDOWS_X86": codebuild.BuildEnvironmentVariable(
114-
value="{}.dkr.ecr.{}.amazonaws.com/{}".format(env.account, env.region, WINDOWS_X86_ECR_REPO)
114+
value=ecr_repos[2].repository_uri
115115
),
116+
"ECR_REGISTRY_URL": codebuild.BuildEnvironmentVariable(value=ecr_repos[0].registry_uri),
116117
},
117118
),
119+
# TODO: We can do away with this if we use aws-actions/amazon-ecr-login@v2, just need to migrate
118120
build_spec=codebuild.BuildSpec.from_object({
119121
"version": 0.2,
120122
"phases": {

tests/ci/cdk/cdk/aws_lc_github_ci_stack.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
from aws_cdk import (
66
Duration,
7-
Stack,
87
aws_codebuild as codebuild,
98
aws_iam as iam,
10-
aws_s3_assets,
119
aws_logs as logs,
1210
Environment,
1311
)
@@ -20,17 +18,8 @@
2018
code_build_publish_metrics_in_json,
2119
code_build_cloudwatch_logs_policy_in_json,
2220
)
23-
from util.metadata import (
24-
GITHUB_PUSH_CI_BRANCH_TARGETS,
25-
GITHUB_REPO_OWNER,
26-
GITHUB_REPO_NAME,
27-
PRE_PROD_ACCOUNT,
28-
STAGING_GITHUB_REPO_OWNER,
29-
STAGING_GITHUB_REPO_NAME,
30-
)
3121
from util.build_spec_loader import BuildSpecLoader
3222

33-
3423
class AwsLcGitHubCIStack(AwsLcBaseCiStack):
3524
"""Define a stack used to batch execute AWS-LC tests in GitHub."""
3625

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0 OR ISC
3+
import itertools
4+
import typing
5+
6+
from aws_cdk import (
7+
Duration,
8+
aws_codebuild as codebuild,
9+
aws_iam as iam,
10+
aws_logs as logs,
11+
aws_ecr as ecr,
12+
Environment,
13+
)
14+
from constructs import Construct
15+
16+
from cdk.aws_lc_base_ci_stack import AwsLcBaseCiStack
17+
from util.iam_policies import (
18+
code_build_publish_metrics_in_json,
19+
)
20+
from util.metadata import UBUNTU_ECR_REPO, AMAZONLINUX_ECR_REPO, CENTOS_ECR_REPO, FEDORA_ECR_REPO
21+
22+
class AwsLcGitHubDockerActionsStack(AwsLcBaseCiStack):
23+
"""Define a stack used to execute AWS-LC self-hosted GitHub Actions Runners on Docker Images."""
24+
25+
def __init__(
26+
self,
27+
scope: Construct,
28+
id: str,
29+
env: typing.Union[Environment, typing.Dict[str, typing.Any]],
30+
**kwargs
31+
) -> None:
32+
super().__init__(scope, id, env=env, timeout=180, **kwargs)
33+
34+
# Define a IAM role for this stack.
35+
metrics_policy = iam.PolicyDocument.from_json(
36+
code_build_publish_metrics_in_json(env)
37+
)
38+
39+
repo_names = [UBUNTU_ECR_REPO, AMAZONLINUX_ECR_REPO, CENTOS_ECR_REPO, FEDORA_ECR_REPO]
40+
ecr_repos = [ecr.Repository.from_repository_name(self, x.replace('/', '-'), repository_name=x)
41+
for x in repo_names]
42+
43+
staging_repo = ecr.Repository(self, "aws-lc-ecr-staging",
44+
image_tag_mutability=ecr.TagMutability.IMMUTABLE,
45+
lifecycle_rules=[ecr.LifecycleRule(
46+
max_image_age=Duration.days(7),
47+
)])
48+
49+
ecr_repos.append(staging_repo)
50+
51+
inline_policies = {
52+
"metrics_policy": metrics_policy,
53+
"ecr": iam.PolicyDocument(
54+
statements=[
55+
iam.PolicyStatement(
56+
effect=iam.Effect.ALLOW,
57+
actions=[
58+
"ecr:GetAuthorizationToken",
59+
],
60+
resources=["*"],
61+
),
62+
iam.PolicyStatement(
63+
effect=iam.Effect.ALLOW,
64+
actions=[
65+
"ecr:BatchGetImage",
66+
"ecr:BatchCheckLayerAvailability",
67+
"ecr:CompleteLayerUpload",
68+
"ecr:GetDownloadUrlForLayer",
69+
"ecr:InitiateLayerUpload",
70+
"ecr:PutImage",
71+
"ecr:UploadLayerPart",
72+
],
73+
resources=[x for x in itertools.chain([
74+
x.repository_arn for x in ecr_repos
75+
], [ecr.Repository.from_repository_name(self, "quay-io", "quay.io/*").repository_arn])],
76+
),
77+
],
78+
)
79+
}
80+
role = iam.Role(
81+
scope=self,
82+
id="{}-role".format(id),
83+
assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"),
84+
inline_policies=inline_policies,
85+
)
86+
87+
logging_options = codebuild.LoggingOptions(
88+
cloud_watch=codebuild.CloudWatchLoggingOptions(log_group=logs.LogGroup(
89+
self, id="{}-logs".format(id)))
90+
)
91+
92+
# Override base class provided configuration
93+
self.git_hub_source = codebuild.Source.git_hub(
94+
owner=self.github_repo_owner,
95+
repo=self.github_repo_name,
96+
webhook=True,
97+
webhook_filters=[
98+
codebuild.FilterGroup.in_event_of(
99+
codebuild.EventAction.WORKFLOW_JOB_QUEUED
100+
),
101+
],
102+
)
103+
104+
# Define CodeBuild.
105+
project = codebuild.Project(
106+
scope=self,
107+
id=id,
108+
project_name=id,
109+
source=self.git_hub_source,
110+
role=role,
111+
timeout=Duration.minutes(self.timeout),
112+
logging=logging_options,
113+
environment=codebuild.BuildEnvironment(
114+
compute_type=codebuild.ComputeType.SMALL,
115+
privileged=True,
116+
build_image=codebuild.LinuxBuildImage.STANDARD_7_0,
117+
environment_variables={
118+
"AWS_ACCOUNT_ID": codebuild.BuildEnvironmentVariable(value=env.account),
119+
"ECR_REGISTRY_URL": codebuild.BuildEnvironmentVariable(value=staging_repo.registry_uri),
120+
"ECR_STAGING_REPO": codebuild.BuildEnvironmentVariable(value=staging_repo.repository_uri),
121+
},
122+
),
123+
)
124+
125+
cfn_project = project.node.default_child
126+
cfn_project.add_property_override("Triggers.PullRequestBuildPolicy", self.pull_request_policy)

tests/ci/cdk/cdk/ecr_stack.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
# SPDX-License-Identifier: Apache-2.0 OR ISC
33

4-
from aws_cdk import Stack, Duration, aws_ecr as ecr, aws_iam as iam
4+
import dataclasses
5+
import typing
6+
from aws_cdk import Environment, RemovalPolicy, Stack, Duration, aws_ecr as ecr, aws_iam as iam
57
from constructs import Construct
8+
from util.metadata import AMAZONLINUX_ECR_REPO, CENTOS_ECR_REPO, FEDORA_ECR_REPO, UBUNTU_ECR_REPO
69

710

811
class EcrStack(Stack):
@@ -30,3 +33,76 @@ def __init__(self, scope: Construct, id: str, repo_name: str, **kwargs) -> None:
3033
tag_status=ecr.TagStatus.UNTAGGED,
3134
max_image_age=Duration.days(1),
3235
)
36+
37+
38+
@dataclasses.dataclass
39+
class EcrRepoDataClass:
40+
cdk_id: str
41+
ecr_name: str
42+
43+
44+
class PrivateEcrStackV2(Stack):
45+
def __init__(self,
46+
scope: Construct,
47+
id: str,
48+
env: typing.Union[Environment, typing.Dict[str, typing.Any]],
49+
**kwargs) -> None:
50+
super().__init__(scope, id, env=env, **kwargs)
51+
52+
ecr.CfnRepositoryCreationTemplate(self, "pull-through-cache-template",
53+
applied_for=["PULL_THROUGH_CACHE"],
54+
description="Used to create pull through cache repositories",
55+
prefix="ROOT",
56+
image_tag_mutability="MUTABLE",
57+
encryption_configuration={
58+
"encryptionType": "AES256"
59+
},
60+
lifecycle_policy="""
61+
{
62+
"rules": [
63+
{
64+
"rulePriority": 1,
65+
"description": "Expire images older than 30 days",
66+
"selection": {
67+
"tagStatus": "untagged",
68+
"countType": "sinceImagePushed",
69+
"countUnit": "days",
70+
"countNumber": 30
71+
},
72+
"action": {
73+
"type": "expire"
74+
}
75+
}
76+
]
77+
}
78+
""")
79+
80+
quay_io_prefixes = ["centos"]
81+
for repo in quay_io_prefixes:
82+
ecr.CfnPullThroughCacheRule(self, f"quay-io-{repo}",
83+
ecr_repository_prefix=f"quay.io/{repo}",
84+
upstream_registry_url="quay.io",
85+
upstream_repository_prefix=repo)
86+
87+
for x in [
88+
EcrRepoDataClass("aws-lc-ecr-ubuntu", UBUNTU_ECR_REPO),
89+
EcrRepoDataClass("aws-lc-ecr-amazonlinux", AMAZONLINUX_ECR_REPO),
90+
EcrRepoDataClass("aws-lc-ecr-fedora", FEDORA_ECR_REPO),
91+
EcrRepoDataClass("aws-lc-ecr-centos", CENTOS_ECR_REPO),
92+
]:
93+
EcrPrivateRepo(self, x.cdk_id, repo_name=x.ecr_name)
94+
95+
96+
class EcrPrivateRepo(Construct):
97+
"""Define private ECR repository to store container images."""
98+
99+
def __init__(self, scope: Construct, id: str, repo_name: str, **kwargs) -> None:
100+
super().__init__(scope, id, **kwargs)
101+
102+
self.repo = ecr.Repository(
103+
scope=self, id=id, repository_name=repo_name, removal_policy=RemovalPolicy.RETAIN)
104+
self.repo.add_lifecycle_rule(
105+
description="Remove untagged images after 1 day",
106+
tag_status=ecr.TagStatus.UNTAGGED,
107+
max_image_age=Duration.days(90),
108+
)

tests/ci/cdk/pipeline/ci_stage.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from constructs import Construct
1616

1717
from cdk.aws_lc_base_ci_stack import AwsLcBaseCiStack
18-
from cdk.aws_lc_github_actions_stack import AwsLcGitHubActionsStack
1918
from pipeline.ci_util import add_ci_stacks
2019
from pipeline.codebuild_batch_step import CodeBuildBatchStep
2120
from util.metadata import (
@@ -48,8 +47,7 @@ def __init__(
4847
@property
4948
def stacks(self) -> typing.List[AwsLcBaseCiStack]:
5049
return [
51-
child for child in self.node.children if isinstance(child, AwsLcBaseCiStack) and
52-
not isinstance(child, AwsLcGitHubActionsStack)
50+
child for child in self.node.children if isinstance(child, AwsLcBaseCiStack)
5351
]
5452

5553
def add_stage_to_pipeline(

0 commit comments

Comments
 (0)