Skip to content

Commit 40d8da2

Browse files
[Nexthop] Add basic utilities for FBOSS Image Builder
Provide basic tools and utiulities for the distro_cli build system. - Add custom exception classes for structured error handling - Define shared constants for Docker configuration and build settings - Implement path resolution utilities for components - Define test data fixture for image manifest validation These core utilities provide the base infrastructure required by all downstream components including Docker integration, artifact handling, and build orchestration.
1 parent 681d624 commit 40d8da2

File tree

9 files changed

+191
-47
lines changed

9 files changed

+191
-47
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
"""Constants for FBOSS image builder."""
9+
10+
# Docker image names
11+
FBOSS_BUILDER_IMAGE = "fboss_builder"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
"""Exception definitions for FBOSS image builder."""
9+
10+
11+
class FbossImageError(Exception):
12+
"""Base exception for all fboss-image errors.
13+
14+
All custom exceptions in the fboss-image tool should inherit from this.
15+
This allows the top-level CLI handler to catch and format errors appropriately
16+
before returning a non-zero exit code.
17+
"""
18+
19+
20+
class BuildError(FbossImageError):
21+
"""Build command failed.
22+
23+
Raised when a component build fails, including:
24+
- Build script/command not found
25+
- Build process returned non-zero exit code
26+
- Build output validation failed
27+
"""
28+
29+
30+
class ManifestError(FbossImageError):
31+
"""Manifest parsing or validation failed.
32+
33+
Raised when:
34+
- Manifest file not found or invalid JSON
35+
- Required fields missing
36+
- Invalid manifest structure
37+
"""
38+
39+
40+
class ArtifactError(FbossImageError):
41+
"""Artifact operation failed.
42+
43+
Raised when:
44+
- Expected artifact not found after build
45+
- Artifact download failed
46+
- Artifact cache operation failed
47+
"""
48+
49+
50+
class ComponentError(FbossImageError):
51+
"""Component-specific error.
52+
53+
Raised when:
54+
- Component not found in manifest
55+
- Component configuration invalid
56+
- Component builder not implemented
57+
"""
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
"""Path resolution utilities for FBOSS image builder."""
9+
10+
from pathlib import Path
11+
12+
13+
def get_root_dir(dir_name: str = "fboss-image") -> Path:
14+
"""Find the root directory containing the specified directory.
15+
16+
This works by walking up from the current file until we find
17+
the specified directory, then returning its parent.
18+
19+
Args:
20+
dir_name: Name of the directory to search for (default: "fboss-image")
21+
22+
Returns:
23+
Path to root directory (parent of dir_name)
24+
25+
Raises:
26+
RuntimeError: If the root directory cannot be determined
27+
"""
28+
current = Path(__file__).resolve()
29+
30+
# Walk up the directory tree looking for dir_name
31+
for parent in current.parents:
32+
if (parent / dir_name).is_dir():
33+
return parent
34+
35+
raise RuntimeError(
36+
f"Could not find root from {current}. "
37+
f"Expected to find '{dir_name}' directory in parent path."
38+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"distribution_formats": {
3+
"onie": "FBOSS-k6.4.3.bin",
4+
"usb": "FBOSS-k6.4.3.iso"
5+
},
6+
"kernel": {
7+
"download": "https://artifact.github.com/nexthop-ai/fboss/fboss-oss_kernel_v6.4.3.tar"
8+
},
9+
"other_dependencies": [
10+
{"download": "https://artifact.github.com/nexthop-ai/fsdb/fsdb.rpm"},
11+
{"download": "file:vendor_debug_tools/tools.rpm"}
12+
],
13+
"fboss-platform-stack": {
14+
"execute": ["../../../../fboss/fboss/oss/scripts/build.py", "platformstack"]
15+
},
16+
"bsps": [
17+
{"execute": ["../../../../vendor_bsp/build.make"]},
18+
{"execute": ["../../../../fboss/oss/reference_bsp/build.py"]}
19+
],
20+
"sai": {
21+
"execute": ["../../../../fboss_brcm_sai/build.sh"]
22+
},
23+
"fboss-forwarding-stack": {
24+
"execute": ["../../../../fboss/fboss/oss/scripts/build.py", "forwardingstack"]
25+
},
26+
"image_build_hooks": {
27+
"after_pkgs": "additional_os_pkgs.xml"
28+
}
29+
}

fboss-image/kernel/configs/fboss-reference.config

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ CONFIG_INIT_ENV_ARG_LIMIT=32
2929
# CONFIG_COMPILE_TEST is not set
3030
# CONFIG_WERROR is not set
3131
CONFIG_LOCALVERSION=""
32-
CONFIG_LOCALVERSION_AUTO=y
32+
# CONFIG_LOCALVERSION_AUTO is not set
3333
CONFIG_BUILD_SALT=""
3434
CONFIG_HAVE_KERNEL_GZIP=y
3535
CONFIG_HAVE_KERNEL_BZIP2=y
@@ -234,7 +234,7 @@ CONFIG_RD_ZSTD=y
234234
CONFIG_BOOT_CONFIG=y
235235
# CONFIG_BOOT_CONFIG_FORCE is not set
236236
CONFIG_BOOT_CONFIG_EMBED=y
237-
CONFIG_BOOT_CONFIG_EMBED_FILE="common.bootconfig"
237+
CONFIG_BOOT_CONFIG_EMBED_FILE="facebook/config/common.bootconfig"
238238
CONFIG_INITRAMFS_PRESERVE_MTIME=y
239239
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
240240
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
@@ -5583,7 +5583,7 @@ CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
55835583
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y
55845584
# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
55855585
CONFIG_SYSTEM_TRUSTED_KEYRING=y
5586-
CONFIG_SYSTEM_TRUSTED_KEYS="kmod-hsm.pem"
5586+
CONFIG_SYSTEM_TRUSTED_KEYS="facebook/fbinfra-kmod-hsm.pem"
55875587
# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
55885588
# CONFIG_SECONDARY_TRUSTED_KEYRING is not set
55895589
# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set

fboss-image/kernel/scripts/merge_config.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import pathlib
1313
import re
1414

15+
1516
def main(argv):
1617
if len(argv) != 3:
1718
sys.stderr.write("Usage: merge_config.py <config_path> <overlay_path>\n")
@@ -20,8 +21,8 @@ def main(argv):
2021
cfg_lines = pathlib.Path(cfg_path).read_text().splitlines()
2122
overlay_lines = pathlib.Path(overlay_path).read_text().splitlines()
2223

23-
VAL_RE = re.compile(r'^CONFIG_([^=]+)=(.*)$')
24-
NS_RE = re.compile(r'^#\s*CONFIG_([^\s]+)\s+is\s+not\s+set\s*$')
24+
VAL_RE = re.compile(r"^CONFIG_([^=]+)=(.*)$")
25+
NS_RE = re.compile(r"^#\s*CONFIG_([^\s]+)\s+is\s+not\s+set\s*$")
2526

2627
# Build updates from overlay, keeping only the last occurrence per key
2728
updates = {}
@@ -31,14 +32,16 @@ def main(argv):
3132
if not line:
3233
continue
3334
# Skip pure comments except the canonical "not set" form
34-
if line.startswith('#') and not NS_RE.match(line):
35+
if line.startswith("#") and not NS_RE.match(line):
3536
continue
3637
m_val = VAL_RE.match(line)
3738
m_ns = None if m_val else NS_RE.match(line)
3839
if not (m_val or m_ns):
3940
continue
4041
key = (m_val or m_ns).group(1)
41-
repl = f'CONFIG_{key}={m_val.group(2)}' if m_val else f'# CONFIG_{key} is not set'
42+
repl = (
43+
f"CONFIG_{key}={m_val.group(2)}" if m_val else f"# CONFIG_{key} is not set"
44+
)
4245
if key in updates:
4346
try:
4447
order.remove(key)
@@ -61,8 +64,9 @@ def main(argv):
6164
for k in order:
6265
out.append(updates[k])
6366

64-
pathlib.Path(cfg_path).write_text('\n'.join(out) + '\n')
67+
pathlib.Path(cfg_path).write_text("\n".join(out) + "\n")
6568
return 0
6669

70+
6771
if __name__ == "__main__":
6872
sys.exit(main(sys.argv))

fboss-image/kernel/scripts/prepare_config.sh

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,39 @@ PROGNAME=$(basename "$0")
2323
SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
2424

2525
usage() {
26-
echo "Usage: $PROGNAME <kernel_source_path> <fboss_reference_config> [local_overrides_config]"
27-
echo ""
28-
echo "Create kernel config by merging FBOSS reference config with kernel defaults"
29-
echo ""
30-
echo "Arguments:"
31-
echo " kernel_source_path Path to Linux kernel source directory"
32-
echo " fboss_reference_config Path to FBOSS reference kernel config file"
33-
echo " local_overrides_config Optional path to local config overrides file"
34-
echo ""
35-
echo "Examples:"
36-
echo " $PROGNAME /path/to/linux-6.4.3 fboss-reference.config"
37-
echo " $PROGNAME /path/to/linux-6.4.3 fboss-reference.config fboss-local-overrides.config"
26+
echo "Usage: $PROGNAME <kernel_source_path> <fboss_reference_config> [local_overrides_config]"
27+
echo ""
28+
echo "Create kernel config by merging FBOSS reference config with kernel defaults"
29+
echo ""
30+
echo "Arguments:"
31+
echo " kernel_source_path Path to Linux kernel source directory"
32+
echo " fboss_reference_config Path to FBOSS reference kernel config file"
33+
echo " local_overrides_config Optional path to local config overrides file"
34+
echo ""
35+
echo "Examples:"
36+
echo " $PROGNAME /path/to/linux-6.4.3 fboss-reference.config"
37+
echo " $PROGNAME /path/to/linux-6.4.3 fboss-reference.config fboss-local-overrides.config"
3838
}
3939

4040
validate_inputs() {
41-
local kernel_source="$1"
42-
local fboss_config="$2"
41+
local kernel_source="$1"
42+
local fboss_config="$2"
4343

44-
if [ ! -d "$kernel_source" ]; then
45-
echo "$PROGNAME: Error: Kernel source directory '$kernel_source' does not exist"
46-
exit 1
47-
fi
44+
if [ ! -d "$kernel_source" ]; then
45+
echo "$PROGNAME: Error: Kernel source directory '$kernel_source' does not exist"
46+
exit 1
47+
fi
4848

49-
if [ ! -f "$fboss_config" ]; then
50-
echo "$PROGNAME: Error: FBOSS reference config '$fboss_config' does not exist"
51-
exit 2
52-
fi
49+
if [ ! -f "$fboss_config" ]; then
50+
echo "$PROGNAME: Error: FBOSS reference config '$fboss_config' does not exist"
51+
exit 2
52+
fi
5353
}
5454

5555
# Main script logic
5656
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
57-
usage
58-
exit 3
57+
usage
58+
exit 3
5959
fi
6060

6161
KERNEL_SOURCE="$1"
@@ -67,8 +67,8 @@ validate_inputs "$KERNEL_SOURCE" "$FBOSS_CONFIG"
6767
echo "Preparing kernel config..."
6868
echo "Kernel source: $KERNEL_SOURCE"
6969
echo "FBOSS config: $FBOSS_CONFIG"
70-
if [[ -n "$LOCAL_OVERRIDES" ]]; then
71-
echo "Local overrides: $LOCAL_OVERRIDES"
70+
if [[ -n $LOCAL_OVERRIDES ]]; then
71+
echo "Local overrides: $LOCAL_OVERRIDES"
7272
fi
7373

7474
# Create default config
@@ -81,8 +81,8 @@ make defconfig
8181
python3 "$SCRIPTS_DIR/merge_config.py" ".config" "$FBOSS_CONFIG"
8282

8383
# Apply local overrides (third layer)
84-
if [[ -n "$LOCAL_OVERRIDES" ]]; then
85-
python3 "$SCRIPTS_DIR/merge_config.py" ".config" "$LOCAL_OVERRIDES"
84+
if [[ -n $LOCAL_OVERRIDES ]]; then
85+
python3 "$SCRIPTS_DIR/merge_config.py" ".config" "$LOCAL_OVERRIDES"
8686
fi
8787

8888
# Reconcile any dependency-driven defaults after all merges

fboss-image/kernel/specs/kernel.spec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ Source0: https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-%{version}.tar.xz
2828
Source1: fboss-reference.config
2929
Source2: fboss-local-overrides.config
3030

31-
%global container_scripts_dir /var/FBOSS/fboss/fboss-image/kernel/scripts
31+
# Default in-container scripts dir (override via --define container_scripts_dir if needed)
32+
%{!?container_scripts_dir:%global container_scripts_dir /src/fboss-image/kernel/scripts}
3233

3334
# Build requirements
3435
BuildRequires: gcc, make

fboss-image/kernel/test/test_prepare_config.sh

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ trap cleanup EXIT
3333
# Use the first kernel version from FBOSS_KERNEL_VERSIONS array
3434
TEST_KERNEL_VERSION="${FBOSS_KERNEL_VERSIONS[0]}"
3535
python3 "$KERNEL_SCRIPTS_DIR/generate_config_overrides.py" \
36-
"$KERNEL_CONFIGS_DIR/fboss-local-overrides.yaml" \
37-
"$TEST_KERNEL_VERSION" \
38-
"$OVR_CONFIG"
36+
"$KERNEL_CONFIGS_DIR/fboss-local-overrides.yaml" \
37+
"$TEST_KERNEL_VERSION" \
38+
"$OVR_CONFIG"
3939

4040
mkdir -p "$FAKE_KERNEL_DIR"
4141

4242
# Minimal fake kernel Makefile providing defconfig and olddefconfig
43-
cat > "$FAKE_KERNEL_DIR/Makefile" << 'EOF'
43+
cat >"$FAKE_KERNEL_DIR/Makefile" <<'EOF'
4444
ARCH ?= x86_64
4545
4646
defconfig:
@@ -60,7 +60,9 @@ cd "$FAKE_KERNEL_DIR"
6060

6161
# Helper: assert a grep matches
6262
assert_grep() {
63-
local pattern="$1"; local file="$2"; local msg="$3"
63+
local pattern="$1"
64+
local file="$2"
65+
local msg="$3"
6466
if ! grep -qE "$pattern" "$file"; then
6567
echo "FAIL: $msg" >&2
6668
echo "Searched pattern: $pattern" >&2
@@ -72,7 +74,9 @@ assert_grep() {
7274

7375
# Helper: assert a grep does NOT match
7476
assert_not_grep() {
75-
local pattern="$1"; local file="$2"; local msg="$3"
77+
local pattern="$1"
78+
local file="$2"
79+
local msg="$3"
7680
if grep -qE "$pattern" "$file"; then
7781
echo "FAIL: $msg" >&2
7882
echo "Unexpectedly matched pattern: $pattern" >&2
@@ -85,9 +89,9 @@ assert_not_grep() {
8589
CFG=".config"
8690

8791
# 1) FBOSS reference presence (pick stable, known entries)
88-
assert_grep '^CONFIG_NET_NS=y$' "$CFG" "FBOSS ref: NET_NS present"
89-
assert_grep '^CONFIG_BRIDGE=m$' "$CFG" "FBOSS ref: BRIDGE=m present"
90-
assert_grep '^CONFIG_VLAN_8021Q=m$' "$CFG" "FBOSS ref: VLAN_8021Q=m present"
92+
assert_grep '^CONFIG_NET_NS=y$' "$CFG" "FBOSS ref: NET_NS present"
93+
assert_grep '^CONFIG_BRIDGE=m$' "$CFG" "FBOSS ref: BRIDGE=m present"
94+
assert_grep '^CONFIG_VLAN_8021Q=m$' "$CFG" "FBOSS ref: VLAN_8021Q=m present"
9195

9296
# 2) Overrides are applied exactly (set to empty string) and no duplicates remain
9397
# CONFIG_BOOT_CONFIG_EMBED_FILE
@@ -118,7 +122,7 @@ else
118122
fi
119123

120124
# 3) Sanity: defconfig contributions still present
121-
assert_grep '^CONFIG_64BIT=y$' "$CFG" "defconfig: 64BIT present"
125+
assert_grep '^CONFIG_64BIT=y$' "$CFG" "defconfig: 64BIT present"
122126
assert_grep '^CONFIG_X86_64=y$' "$CFG" "defconfig: X86_64 present"
123127

124128
echo "All real-merge assertions passed."

0 commit comments

Comments
 (0)