photoncloud-monorepo/baremetal/image-builder/build-images.sh

399 lines
12 KiB
Bash
Executable file

#!/usr/bin/env bash
# ==============================================================================
# PlasmaCloud NixOS Netboot Image Builder
# ==============================================================================
# This script builds netboot images for bare-metal provisioning of PlasmaCloud.
#
# Usage:
# ./build-images.sh [--profile PROFILE] [--output-dir DIR] [--help]
#
# Options:
# --profile PROFILE Build specific profile (control-plane, worker, all-in-one, all)
# --output-dir DIR Output directory for built artifacts (default: ./artifacts)
# --help Show this help message
#
# Examples:
# ./build-images.sh # Build all profiles
# ./build-images.sh --profile control-plane # Build control plane only
# ./build-images.sh --profile all # Build all profiles
# ./build-images.sh --output-dir /srv/pxe # Custom output directory
# ==============================================================================
set -euo pipefail
# ==============================================================================
# CONFIGURATION
# ==============================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
DEFAULT_OUTPUT_DIR="$SCRIPT_DIR/artifacts"
PXE_ASSETS_DIR="$REPO_ROOT/chainfire/baremetal/pxe-server/assets"
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ==============================================================================
# FUNCTIONS
# ==============================================================================
# Print colored messages
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Print banner
print_banner() {
echo ""
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ PlasmaCloud NixOS Netboot Image Builder ║"
echo "║ Building bare-metal provisioning images ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""
}
# Print usage
print_usage() {
cat << EOF
Usage: $0 [OPTIONS]
Build NixOS netboot images for PlasmaCloud bare-metal provisioning.
OPTIONS:
--profile PROFILE Build specific profile:
- control-plane: All 8 PlasmaCloud services
- worker: Compute-focused services (PlasmaVMC, PrismNET)
- all-in-one: All services for single-node deployment
- all: Build all profiles (default)
--output-dir DIR Output directory for artifacts (default: ./artifacts)
--help Show this help message
EXAMPLES:
# Build all profiles
$0
# Build control plane only
$0 --profile control-plane
# Build to custom output directory
$0 --output-dir /srv/pxe/images
PROFILES:
control-plane - Full control plane with all 8 services
worker - Worker node with PlasmaVMC and PrismNET
all-in-one - Single-node deployment with all services
OUTPUT:
The script generates the following artifacts for each profile:
- bzImage Linux kernel
- initrd Initial ramdisk
- netboot.ipxe iPXE boot script
EOF
}
# Build a single netboot profile
build_profile() {
local profile=$1
local output_dir=$2
print_info "Building netboot image for profile: $profile"
# Create profile output directory
local profile_dir="$output_dir/$profile"
mkdir -p "$profile_dir"
# Build the netboot ramdisk
print_info " Building initial ramdisk..."
if ! nix build "$REPO_ROOT#nixosConfigurations.netboot-$profile.config.system.build.netbootRamdisk" \
--out-link "$profile_dir/initrd-link" 2>&1 | tee "$profile_dir/build.log"; then
print_error "Failed to build initrd for $profile (see $profile_dir/build.log)"
return 1
fi
# Build the kernel
print_info " Building kernel..."
if ! nix build "$REPO_ROOT#nixosConfigurations.netboot-$profile.config.system.build.kernel" \
--out-link "$profile_dir/kernel-link" 2>&1 | tee -a "$profile_dir/build.log"; then
print_error "Failed to build kernel for $profile (see $profile_dir/build.log)"
return 1
fi
# Copy artifacts
print_info " Copying artifacts..."
cp -f "$profile_dir/initrd-link/initrd" "$profile_dir/initrd"
cp -f "$profile_dir/kernel-link/bzImage" "$profile_dir/bzImage"
# Resolve init path from the build (avoids hardcoding store paths)
local init_path="/init"
if toplevel=$(nix eval --raw "$REPO_ROOT#nixosConfigurations.netboot-$profile.config.system.build.toplevel" 2>/dev/null); then
if [ -n "$toplevel" ]; then
init_path="${toplevel}/init"
fi
else
print_warning "Failed to resolve init path for $profile; using /init"
fi
# Generate iPXE boot script
print_info " Generating iPXE boot script..."
cat > "$profile_dir/netboot.ipxe" << EOF
#!ipxe
# PlasmaCloud Netboot - $profile
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
# Set variables
set boot-server \${boot-url}
# Display info
echo Loading PlasmaCloud ($profile profile)...
echo Kernel: bzImage
echo Initrd: initrd
echo
# Load kernel and initrd
kernel \${boot-server}/$profile/bzImage init=${init_path} console=ttyS0,115200 console=tty0 loglevel=4
initrd \${boot-server}/$profile/initrd
# Boot
boot
EOF
# Calculate sizes
local kernel_size=$(du -h "$profile_dir/bzImage" | cut -f1)
local initrd_size=$(du -h "$profile_dir/initrd" | cut -f1)
local total_size=$(du -sh "$profile_dir" | cut -f1)
# Print summary
print_success "Profile $profile built successfully!"
print_info " Kernel: $kernel_size"
print_info " Initrd: $initrd_size"
print_info " Total: $total_size"
print_info " Location: $profile_dir"
echo ""
}
# Copy artifacts to PXE server assets directory
copy_to_pxe_server() {
local output_dir=$1
if [ ! -d "$PXE_ASSETS_DIR" ]; then
print_warning "PXE assets directory not found: $PXE_ASSETS_DIR"
print_warning "Skipping copy to PXE server"
return 0
fi
print_info "Copying artifacts to PXE server: $PXE_ASSETS_DIR"
for profile in control-plane worker all-in-one; do
local profile_dir="$output_dir/$profile"
if [ -d "$profile_dir" ]; then
local pxe_profile_dir="$PXE_ASSETS_DIR/nixos/$profile"
mkdir -p "$pxe_profile_dir"
cp -f "$profile_dir/bzImage" "$pxe_profile_dir/"
cp -f "$profile_dir/initrd" "$pxe_profile_dir/"
cp -f "$profile_dir/netboot.ipxe" "$pxe_profile_dir/"
# Create symlinks for convenience
ln -sf "$profile/bzImage" "$PXE_ASSETS_DIR/nixos/bzImage-$profile"
ln -sf "$profile/initrd" "$PXE_ASSETS_DIR/nixos/initrd-$profile"
print_success " Copied $profile to PXE server"
fi
done
}
# Verify build outputs
verify_outputs() {
local output_dir=$1
local profile=$2
local profile_dir="$output_dir/$profile"
local errors=0
# Check for required files
if [ ! -f "$profile_dir/bzImage" ]; then
print_error "Missing bzImage for $profile"
((errors++))
fi
if [ ! -f "$profile_dir/initrd" ]; then
print_error "Missing initrd for $profile"
((errors++))
fi
if [ ! -f "$profile_dir/netboot.ipxe" ]; then
print_error "Missing netboot.ipxe for $profile"
((errors++))
fi
# Check file sizes (should be reasonable)
if [ -f "$profile_dir/bzImage" ]; then
local kernel_size=$(stat -c%s "$profile_dir/bzImage")
if [ "$kernel_size" -lt 1000000 ]; then # Less than 1MB is suspicious
print_warning "Kernel size seems too small: $kernel_size bytes"
((errors++))
fi
fi
if [ -f "$profile_dir/initrd" ]; then
local initrd_size=$(stat -c%s "$profile_dir/initrd")
if [ "$initrd_size" -lt 10000000 ]; then # Less than 10MB is suspicious
print_warning "Initrd size seems too small: $initrd_size bytes"
((errors++))
fi
fi
return $errors
}
# Print final summary
print_summary() {
local output_dir=$1
local profiles=("$@")
shift # Remove first argument (output_dir)
echo ""
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ Build Summary ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""
for profile in "${profiles[@]}"; do
if [ "$profile" == "$output_dir" ]; then
continue
fi
local profile_dir="$output_dir/$profile"
if [ -d "$profile_dir" ]; then
echo "Profile: $profile"
echo " Location: $profile_dir"
if [ -f "$profile_dir/bzImage" ]; then
echo " Kernel: $(du -h "$profile_dir/bzImage" | cut -f1)"
fi
if [ -f "$profile_dir/initrd" ]; then
echo " Initrd: $(du -h "$profile_dir/initrd" | cut -f1)"
fi
echo ""
fi
done
echo "Next Steps:"
echo " 1. Deploy images to PXE server (if not done automatically)"
echo " 2. Configure DHCP/iPXE boot infrastructure"
echo " 3. Boot target machines via PXE"
echo " 4. Use nixos-anywhere for installation"
echo ""
echo "For more information, see:"
echo " - baremetal/image-builder/README.md"
echo " - docs/por/T032-baremetal-provisioning/design.md"
echo ""
}
# ==============================================================================
# MAIN
# ==============================================================================
main() {
local profile="all"
local output_dir="$DEFAULT_OUTPUT_DIR"
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--profile)
profile="$2"
shift 2
;;
--output-dir)
output_dir="$2"
shift 2
;;
--help)
print_usage
exit 0
;;
*)
print_error "Unknown option: $1"
print_usage
exit 1
;;
esac
done
# Validate profile
if [[ ! "$profile" =~ ^(control-plane|worker|all-in-one|all)$ ]]; then
print_error "Invalid profile: $profile"
print_usage
exit 1
fi
print_banner
# Create output directory
mkdir -p "$output_dir"
# Build profiles
local profiles_to_build=()
if [ "$profile" == "all" ]; then
profiles_to_build=("control-plane" "worker" "all-in-one")
else
profiles_to_build=("$profile")
fi
local build_errors=0
for p in "${profiles_to_build[@]}"; do
if ! build_profile "$p" "$output_dir"; then
((build_errors++))
fi
done
# Verify outputs
print_info "Verifying build outputs..."
local verify_errors=0
for p in "${profiles_to_build[@]}"; do
if ! verify_outputs "$output_dir" "$p"; then
((verify_errors++))
fi
done
# Copy to PXE server if available
copy_to_pxe_server "$output_dir"
# Print summary
print_summary "$output_dir" "${profiles_to_build[@]}"
# Exit with error if any builds failed
if [ $build_errors -gt 0 ]; then
print_error "Build completed with $build_errors error(s)"
exit 1
fi
if [ $verify_errors -gt 0 ]; then
print_warning "Build completed with $verify_errors warning(s)"
fi
print_success "All builds completed successfully!"
}
# Run main function
main "$@"