Skip to content

Commit cf8c981

Browse files
committed
fix(infra): Fix race condition in parallel base image builds
The parallel execution of base image builds was causing a race condition, where dependent images (e.g., `base-builder`) were starting their build process before their base images (e.g., `base-clang`) had finished building in the same pipeline. This resulted in the builder pulling the previously existing, and incorrect, base image from the registry (e.g., an Ubuntu 20.04-based image instead of the new 24.04 version). This commit fixes the issue by introducing an `IMAGE_DEPENDENCIES` map that explicitly defines the build order. The `get_base_image_steps` function now uses this map to add `waitFor` clauses to the Google Cloud Build steps, ensuring that images are built sequentially according to their dependency graph.
1 parent 28e6d65 commit cf8c981

File tree

1 file changed

+38
-5
lines changed

1 file changed

+38
-5
lines changed

infra/build/functions/base_images.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@
4747
# This version will receive the ':v1' tag.
4848
DEFAULT_VERSION = 'legacy'
4949

50+
# Defines the dependency graph for base images.
51+
IMAGE_DEPENDENCIES = {
52+
'base-clang': ['base-image'],
53+
'base-clang-full': ['base-clang'],
54+
'base-builder': ['base-clang'],
55+
'base-builder-go': ['base-builder'],
56+
'base-builder-javascript': ['base-builder'],
57+
'base-builder-jvm': ['base-builder'],
58+
'base-builder-python': ['base-builder'],
59+
'base-builder-ruby': ['base-builder'],
60+
'base-builder-rust': ['base-builder'],
61+
'base-builder-swift': ['base-builder'],
62+
'base-runner': ['base-image'],
63+
'base-runner-debug': ['base-runner'],
64+
'indexer': ['base-clang-full'],
65+
}
66+
5067

5168
class ImageConfig:
5269
"""Configuration for a specific base image version."""
@@ -85,6 +102,8 @@ def _resolve_dockerfile(self) -> str:
85102
if os.path.exists(versioned_dockerfile):
86103
logging.info('Using versioned Dockerfile: %s', versioned_dockerfile)
87104
return versioned_dockerfile
105+
raise FileNotFoundError(
106+
f'Versioned Dockerfile not found for {self.name}:{self.version}')
88107

89108
legacy_dockerfile = os.path.join(self.path, 'Dockerfile')
90109
logging.info('Using legacy Dockerfile: %s', legacy_dockerfile)
@@ -156,6 +175,8 @@ def full_image_name_with_tag(self) -> str:
156175
def get_base_image_steps(images: Sequence[ImageConfig]) -> list[dict]:
157176
"""Returns build steps for a given list of image configurations."""
158177
steps = [build_lib.get_git_clone_step()]
178+
build_ids = {}
179+
159180
for image_config in images:
160181
# The final tag is ':v1' for the default version, or the version name
161182
# (e.g., ':ubuntu-24-04') for others.
@@ -167,11 +188,23 @@ def get_base_image_steps(images: Sequence[ImageConfig]) -> list[dict]:
167188
tags.append(f'{IMAGE_NAME_PREFIX}{image_config.name}:latest')
168189

169190
dockerfile_path = os.path.join('oss-fuzz', image_config.dockerfile_path)
170-
steps.append(
171-
build_lib.get_docker_build_step(tags,
172-
image_config.path,
173-
dockerfile_path=dockerfile_path,
174-
build_args=image_config.build_args))
191+
step = build_lib.get_docker_build_step(
192+
tags,
193+
image_config.path,
194+
dockerfile_path=dockerfile_path,
195+
build_args=image_config.build_args)
196+
197+
# Check for dependencies and add 'waitFor' if necessary.
198+
dependencies = IMAGE_DEPENDENCIES.get(image_config.name, [])
199+
wait_for = [
200+
build_ids[dep] for dep in dependencies if dep in build_ids
201+
]
202+
if wait_for:
203+
step['waitFor'] = wait_for
204+
205+
build_ids[image_config.name] = step['id']
206+
steps.append(step)
207+
175208
return steps
176209

177210

0 commit comments

Comments
 (0)