Skip to content

Commit bd735c3

Browse files
[CDRIVER-6142] Refactor dependency/environment setup in Earthfile (#2165)
* Consolidate several small scripts into a single __tool * Significant Earthfile refactor This change rewrites much of Earthfile's dependency-installation handling code to be simpler to modify, and more flexible to use. Previously, each environment had its own target that needed to know how to install all of our dependencies for the specific platform. This refactor inverts the relationship: Instead, each dependency has an Earthly function that knows how to install itself based on whatever platform it happens to be running within. This allows the FROM line to be a plain image, rather that one of our handcrafted `env.foo` targets. This will make introducing and testing additional environments far more simple in the future. This change also allows us to remove the tools/build.earth utility, since we can now fit everything in the top-level file. * Update Earthly-generated tasks * Add Ubuntu 24.04 * Enable building both with/without Snappy in Earthly * Tasks with/without Snappy * Add a trailhead for obtaining the detailed Earthfile documentation * Refactor Earthly C++ installation to rely on the same C compiler param
1 parent eafa879 commit bd735c3

File tree

12 files changed

+2270
-1024
lines changed

12 files changed

+2270
-1024
lines changed

.evergreen/config_generator/components/earthly.py

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,27 @@
2121

2222
T = TypeVar('T')
2323

24-
_ENV_PARAM_NAME = 'MONGOC_EARTHLY_ENV'
24+
_ENV_PARAM_NAME = 'MONGOC_EARTHLY_FROM'
25+
_ECR_HOST = '901841024863.dkr.ecr.us-east-1.amazonaws.com'
2526
_CC_PARAM_NAME = 'MONGOC_EARTHLY_C_COMPILER'
2627
'The name of the EVG expansion for the Earthly c_compiler argument'
2728

28-
29-
EnvKey = Literal[
30-
'u20',
31-
'u22',
32-
'almalinux8',
33-
'almalinux9',
34-
'almalinux10',
35-
'alpine3.19',
36-
'alpine3.20',
37-
'alpine3.21',
38-
'alpine3.22',
29+
EnvImage = Literal[
30+
'ubuntu:20.04',
31+
'ubuntu:22.04',
32+
'ubuntu:24.04',
33+
'almalinux:8',
34+
'almalinux:9',
35+
'almalinux:10',
36+
'alpine:3.19',
37+
'alpine:3.20',
38+
'alpine:3.21',
39+
'alpine:3.22',
3940
'archlinux',
40-
'centos9',
41-
'centos10',
41+
'quay.io/centos/centos:stream9',
42+
'quay.io/centos/centos:stream10',
4243
]
43-
"Identifiers for environments. These correspond to special 'env.*' targets in the Earthfile."
44+
'Base environment images to be built.'
4445
CompilerName = Literal['gcc', 'clang']
4546
'The name of the compiler program that is used for the build. Passed via --c_compiler to Earthly.'
4647

@@ -51,53 +52,66 @@
5152
"Options for the TLS backend configuration parameter (AKA 'ENABLE_SSL')"
5253
CxxVersion = Literal['master', 'r4.1.0', 'none']
5354
'C++ driver refs that are under CI test'
55+
SnappyOption = Literal['false', 'true']
56+
"""Should we enable Snappy compression in this build?"""
5457

5558
# A separator character, since we cannot use whitespace
5659
_SEPARATOR = '\N{NO-BREAK SPACE}\N{BULLET}\N{NO-BREAK SPACE}'
5760

5861

59-
def os_split(env: EnvKey) -> tuple[str, None | str]:
62+
def os_split(env: EnvImage) -> tuple[str, None | str]:
6063
"""Convert the environment key into a pretty name+version pair"""
6164
match env:
62-
# Match 'alpine3.18' 'alpine53.123' etc.
63-
case alp if mat := re.match(r'alpine(\d+\.\d+)', alp):
65+
# Match 'alpine:3.18' 'alpine:53.123' etc.
66+
case alp if mat := re.match(r'alpine:(\d+\.\d+)', alp):
6467
return ('Alpine', mat[1])
6568
case 'archlinux':
6669
return 'ArchLinux', None
67-
# Match 'u22', 'u20', 'u71' etc.
68-
case ubu if mat := re.match(r'u(\d\d)', ubu):
69-
return 'Ubuntu', f'{mat[1]}.04'
70-
# Match 'centos9', 'centos10', etc.
71-
case cent if mat := re.match(r'centos(\d+)', cent):
70+
# Match 'ubuntu:<version>'.
71+
case ubu if mat := re.match(r'ubuntu:(\d\d.*)', ubu):
72+
return 'Ubuntu', f'{mat[1]}'
73+
# Match 'centos:9', 'centos:stream10', etc.
74+
case cent if mat := re.match(r'.*centos:(?:stream)?(\d+)', cent):
7275
return 'CentOS', f'{mat[1]}'
73-
# Match 'almalinux8', 'almalinux10', etc.
74-
case alm if mat := re.match(r'almalinux(\d+)', alm):
76+
# Match 'almalinux:8', 'almalinux:10', etc.
77+
case alm if mat := re.match(r'almalinux:(\d+.*)', alm):
7578
return 'AlmaLinux', f'{mat[1]}'
7679
case _:
7780
raise ValueError(f'Failed to split OS env key {env=} into a name+version pair (unrecognized)')
7881

7982

83+
def from_container_image(img: EnvImage) -> str:
84+
"""
85+
Modify an unqualified FROM container identifier to route to our ECR host
86+
87+
NOTE: This will be potentially unnecessary pending the completion of DEVPROD-21478
88+
"""
89+
if '/' in img or img.startswith('+'):
90+
return img
91+
return f'{_ECR_HOST}/dockerhub/library/{img}'
92+
93+
8094
class EarthlyVariant(NamedTuple):
8195
"""
8296
Define a "variant" that runs under a set of Earthly parameters. These are
8397
turned into real EVG variants later on. The Earthly arguments are passed via
8498
expansion parameters.
8599
"""
86100

87-
env: EnvKey
88-
c_compiler: CompilerName
101+
from_: EnvImage
102+
compiler: CompilerName
89103

90104
@property
91105
def display_name(self) -> str:
92106
"""The pretty name for this variant"""
93107
base: str
94-
match os_split(self.env):
108+
match os_split(self.from_):
95109
case name, None:
96110
base = name
97111
case name, version:
98112
base = f'{name} {version}'
99113
toolchain: str
100-
match self.c_compiler:
114+
match self.compiler:
101115
case 'clang':
102116
toolchain = 'LLVM/Clang'
103117
case 'gcc':
@@ -110,7 +124,7 @@ def task_selector_tag(self) -> str:
110124
The task tag that is used to select the tasks that want to run on this
111125
variant.
112126
"""
113-
return f'{self.env}-{self.c_compiler}'
127+
return f'{self.from_}-{self.compiler}'
114128

115129
@property
116130
def expansions(self) -> Mapping[str, str]:
@@ -119,8 +133,8 @@ def expansions(self) -> Mapping[str, str]:
119133
from this object.
120134
"""
121135
return {
122-
_CC_PARAM_NAME: self.c_compiler,
123-
_ENV_PARAM_NAME: self.env,
136+
_CC_PARAM_NAME: self.compiler,
137+
_ENV_PARAM_NAME: from_container_image(self.from_),
124138
}
125139

126140
def as_evg_variant(self) -> BuildVariant:
@@ -145,6 +159,7 @@ class Configuration(NamedTuple):
145159
sasl: SASLOption
146160
tls: TLSOption
147161
test_mongocxx_ref: CxxVersion
162+
snappy: SnappyOption
148163

149164
@property
150165
def suffix(self) -> str:
@@ -169,7 +184,7 @@ class DockerLoginAmazonECR(Function):
169184
],
170185
args=[
171186
'-c',
172-
'aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 901841024863.dkr.ecr.us-east-1.amazonaws.com',
187+
f'aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin {_ECR_HOST}',
173188
],
174189
),
175190
]
@@ -206,11 +221,11 @@ def earthly_exec(
206221
'./tools/earthly.sh',
207222
args=[
208223
# Use Amazon ECR as pull-through cache for DockerHub to avoid rate limits.
209-
'--buildkit-image=901841024863.dkr.ecr.us-east-1.amazonaws.com/dockerhub/earthly/buildkitd:v0.8.3',
224+
f'--buildkit-image={_ECR_HOST}/dockerhub/earthly/buildkitd:v0.8.3',
210225
*(f'--secret={k}' for k in (secrets or ())),
211226
f'+{target}',
212227
# Use Amazon ECR as pull-through cache for DockerHub to avoid rate limits.
213-
'--default_search_registry=901841024863.dkr.ecr.us-east-1.amazonaws.com/dockerhub',
228+
f'--default_search_registry={_ECR_HOST}/dockerhub/library',
214229
*(f'--{arg}={val}' for arg, val in (args or {}).items()),
215230
],
216231
command_type=EvgCommandType(kind),
@@ -243,20 +258,27 @@ def earthly_task(
243258
earthly_args = config._asdict()
244259
earthly_args |= {
245260
# Add arguments that come from parameter expansions defined in the build variant
246-
'env': f'${{{_ENV_PARAM_NAME}}}',
247-
'c_compiler': f'${{{_CC_PARAM_NAME}}}',
261+
'from': f'${{{_ENV_PARAM_NAME}}}',
262+
'compiler': f'${{{_CC_PARAM_NAME}}}',
263+
# Always include a C++ compiler in the build environment for better test coverage
264+
'with_cxx': 'true',
248265
}
249266
return EvgTask(
250267
name=name,
251268
commands=[
252269
DockerLoginAmazonECR.call(),
253-
# First, just build the "env-warmup" which will prepare the build environment.
270+
# First, just build the "build-environment" which will prepare the build environment.
254271
# This won't generate any output, but allows EVG to track it as a separate build step
255272
# for timing and logging purposes. The subequent build step will cache-hit the
256273
# warmed-up build environments.
257274
earthly_exec(
258275
kind='setup',
259-
target='env-warmup',
276+
target='build-environment',
277+
args=earthly_args,
278+
),
279+
earthly_exec(
280+
kind='setup',
281+
target='configure',
260282
args=earthly_args,
261283
),
262284
# Now execute the main tasks:

0 commit comments

Comments
 (0)