Skip to content

Commit 7c2ebe3

Browse files
[Nexthop] Add build entrypoint for FBOSS Image Builder
Add build entrypoint orchestration for component-based builds. - Implement build entrypoint for coordinating component build workflows - Add support for build configuration and execution management - Include comprehensive unit tests for entrypoint functionality
1 parent 4136957 commit 7c2ebe3

File tree

5 files changed

+444
-0
lines changed

5 files changed

+444
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# Copyright (c) 2004-present, Facebook, Inc.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree. An additional grant
6+
# of patent rights can be found in the PATENTS file in the same directory.
7+
8+
"""Test build_entrypoint.py behavior."""
9+
10+
import subprocess
11+
import tarfile
12+
import unittest
13+
from pathlib import Path
14+
15+
from distro_cli.lib.constants import FBOSS_BUILDER_IMAGE
16+
from distro_cli.lib.docker.container import run_container
17+
from distro_cli.lib.paths import get_abs_path
18+
from distro_cli.tests.test_helpers import ensure_test_docker_image, enter_tempdir
19+
20+
21+
class TestBuildEntrypoint(unittest.TestCase):
22+
"""Test build_entrypoint.py as universal build entry point."""
23+
24+
@classmethod
25+
def setUpClass(cls):
26+
"""Ensure fboss_builder image exists before running tests."""
27+
ensure_test_docker_image()
28+
29+
def test_entrypoint_without_dependencies(self):
30+
"""Test build_entrypoint.py executes build command when no dependencies exist."""
31+
with enter_tempdir("entrypoint_no_deps_") as tmpdir_path:
32+
output_file = tmpdir_path / "build_output.txt"
33+
34+
# Mount tools directory (contains build_entrypoint.py)
35+
# No /deps mount - simulates build without dependencies
36+
tools_dir = get_abs_path("fboss-image/distro_cli/tools")
37+
exit_code = run_container(
38+
image=FBOSS_BUILDER_IMAGE,
39+
command=[
40+
"python3",
41+
"/tools/build_entrypoint.py",
42+
"sh",
43+
"-c",
44+
"echo 'build completed' > /output/build_output.txt",
45+
],
46+
volumes={tools_dir: Path("/tools"), tmpdir_path: Path("/output")},
47+
ephemeral=True,
48+
)
49+
50+
self.assertEqual(exit_code, 0, "Build should succeed without dependencies")
51+
self.assertTrue(output_file.exists(), "Build output should be created")
52+
self.assertEqual(output_file.read_text().strip(), "build completed")
53+
54+
def test_entrypoint_with_empty_dependencies(self):
55+
"""Test build_entrypoint.py handles empty /deps directory gracefully."""
56+
with enter_tempdir("entrypoint_empty_deps_") as tmpdir_path:
57+
output_file = tmpdir_path / "build_output.txt"
58+
deps_dir = tmpdir_path / "deps"
59+
deps_dir.mkdir(exist_ok=True)
60+
61+
# Mount empty /deps directory
62+
tools_dir = get_abs_path("fboss-image/distro_cli/tools")
63+
exit_code = run_container(
64+
image=FBOSS_BUILDER_IMAGE,
65+
command=[
66+
"python3",
67+
"/tools/build_entrypoint.py",
68+
"sh",
69+
"-c",
70+
"echo 'build with empty deps' > /output/build_output.txt",
71+
],
72+
volumes={
73+
tools_dir: Path("/tools"),
74+
tmpdir_path: Path("/output"),
75+
deps_dir: Path("/deps"),
76+
},
77+
ephemeral=True,
78+
)
79+
80+
self.assertEqual(
81+
exit_code, 0, "Build should succeed with empty dependencies"
82+
)
83+
self.assertTrue(output_file.exists())
84+
self.assertEqual(output_file.read_text().strip(), "build with empty deps")
85+
86+
def test_entrypoint_with_compressed_dependency(self):
87+
"""Test build_entrypoint.py can extract zstd-compressed dependency tarballs."""
88+
with enter_tempdir("entrypoint_compressed_deps_") as tmpdir_path:
89+
# Create a dummy file to include in the tarball
90+
deps_content_dir = tmpdir_path / "deps_content"
91+
deps_content_dir.mkdir()
92+
dummy_file = deps_content_dir / "test.txt"
93+
dummy_file.write_text("compressed dependency content")
94+
95+
# Create uncompressed tarball first
96+
uncompressed_tar = tmpdir_path / "dependency.tar"
97+
with tarfile.open(uncompressed_tar, "w") as tar:
98+
tar.add(dummy_file, arcname="test.txt")
99+
100+
# Compress with zstd
101+
compressed_tar = tmpdir_path / "dependency.tar.zst"
102+
subprocess.run(
103+
["zstd", str(uncompressed_tar), "-o", str(compressed_tar)],
104+
check=True,
105+
capture_output=True,
106+
)
107+
uncompressed_tar.unlink() # Remove uncompressed version
108+
109+
# Create deps directory and move compressed tarball there
110+
deps_dir = tmpdir_path / "deps"
111+
deps_dir.mkdir()
112+
final_dep = deps_dir / "dependency.tar.zst"
113+
compressed_tar.rename(final_dep)
114+
115+
output_file = tmpdir_path / "build_output.txt"
116+
117+
# Run build_entrypoint.py with compressed dependency
118+
tools_dir = get_abs_path("fboss-image/distro_cli/tools")
119+
exit_code = run_container(
120+
image=FBOSS_BUILDER_IMAGE,
121+
command=[
122+
"python3",
123+
"/tools/build_entrypoint.py",
124+
"sh",
125+
"-c",
126+
"echo 'build with compressed deps' > /output/build_output.txt",
127+
],
128+
volumes={
129+
tools_dir: Path("/tools"),
130+
tmpdir_path: Path("/output"),
131+
deps_dir: Path("/deps"),
132+
},
133+
ephemeral=True,
134+
)
135+
136+
self.assertEqual(
137+
exit_code, 0, "Build should succeed with compressed dependencies"
138+
)
139+
self.assertTrue(output_file.exists())
140+
self.assertEqual(
141+
output_file.read_text().strip(), "build with compressed deps"
142+
)
143+
144+
def test_entrypoint_with_uncompressed_dependency(self):
145+
"""Test build_entrypoint.py can extract uncompressed dependency tarballs."""
146+
with enter_tempdir("entrypoint_uncompressed_deps_") as tmpdir_path:
147+
# Create a dummy file to include in the tarball
148+
deps_content_dir = tmpdir_path / "deps_content"
149+
deps_content_dir.mkdir()
150+
dummy_file = deps_content_dir / "test.txt"
151+
dummy_file.write_text("uncompressed dependency content")
152+
153+
# Create uncompressed tarball
154+
deps_dir = tmpdir_path / "deps"
155+
deps_dir.mkdir()
156+
uncompressed_tar = deps_dir / "dependency.tar"
157+
with tarfile.open(uncompressed_tar, "w") as tar:
158+
tar.add(dummy_file, arcname="test.txt")
159+
160+
output_file = tmpdir_path / "build_output.txt"
161+
162+
# Run build_entrypoint.py with uncompressed dependency
163+
tools_dir = get_abs_path("fboss-image/distro_cli/tools")
164+
exit_code = run_container(
165+
image=FBOSS_BUILDER_IMAGE,
166+
command=[
167+
"python3",
168+
"/tools/build_entrypoint.py",
169+
"sh",
170+
"-c",
171+
"echo 'build with uncompressed deps' > /output/build_output.txt",
172+
],
173+
volumes={
174+
tools_dir: Path("/tools"),
175+
tmpdir_path: Path("/output"),
176+
deps_dir: Path("/deps"),
177+
},
178+
ephemeral=True,
179+
)
180+
181+
self.assertEqual(
182+
exit_code, 0, "Build should succeed with uncompressed dependencies"
183+
)
184+
self.assertTrue(output_file.exists())
185+
self.assertEqual(
186+
output_file.read_text().strip(), "build with uncompressed deps"
187+
)
188+
189+
190+
if __name__ == "__main__":
191+
unittest.main(verbosity=2)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
# Stub kernel build script for testing
3+
# Creates a minimal kernel artifact tarball for testing purposes
4+
set -e
5+
6+
# Parse arguments
7+
OUTPUT_DIR="/output"
8+
9+
echo "Stub kernel build - creating test artifact"
10+
11+
# Create a minimal kernel RPM structure
12+
mkdir -p "$OUTPUT_DIR"
13+
cd "$OUTPUT_DIR"
14+
15+
# Create a dummy kernel RPM file
16+
echo "dummy kernel rpm" >kernel-test.rpm
17+
18+
tar -cf kernel-test.rpms.tar kernel-test.rpm
19+
echo "Stub kernel artifact created: $OUTPUT_DIR/kernel-test.rpms.tar"
20+
21+
# Clean up
22+
rm kernel-test.rpm
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"kernel": {
3+
"execute": ["kernel/stub_build.sh"],
4+
"artifact": "kernel-test.rpms.tar"
5+
},
6+
"distribution_formats": {
7+
"usb": "output/test-stub-component.iso"
8+
}
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2004-present, Facebook, Inc.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree. An additional grant
6+
# of patent rights can be found in the PATENTS file in the same directory.
7+
8+
"""FBOSS Distribution CLI tools package."""

0 commit comments

Comments
 (0)