Simplify fork: remove osbuild complexity, add native Debian tools, integrate GRUB bootloader installation
Some checks failed
particle-os CI / Test particle-os (push) Failing after 1s
particle-os CI / Integration Test (push) Has been skipped
particle-os CI / Security & Quality (push) Failing after 1s
Test particle-os Basic Functionality / test-basic (push) Failing after 1s
Tests / test (1.21.x) (push) Failing after 1s
Tests / test (1.22.x) (push) Failing after 1s
particle-os CI / Build and Release (push) Has been skipped

This commit is contained in:
joe 2025-08-12 20:10:34 -07:00
parent ab30e683d1
commit 9a810d3d27
7 changed files with 402 additions and 49 deletions

7
.gitignore vendored
View file

@ -19,6 +19,13 @@ output-*
*.iso
container.tar
# Image files (large binary artifacts)
*.img
*.qcow2
*.raw
# Large binary files
bootc-image-builder
# Red Hat source directories (read-only)
.Red_Hat_Version/

View file

@ -1,31 +1,32 @@
FROM debian:bookworm AS builder
RUN apt-get update && apt-get install -y git golang-go libgpgme11-dev libassuan-dev && mkdir -p /build/bib
COPY bib/go.mod bib/go.sum /build/bib/
ARG GOPROXY=https://proxy.golang.org,direct
RUN go env -w GOPROXY=$GOPROXY
RUN cd /build/bib && go mod download
# Copy the entire dir to avoid having to conditionally include ".git" as that
# will not be available when tests are run under tmt
COPY . /build
WORKDIR /build
RUN ./build.sh
FROM debian:trixie
FROM debian:bookworm
# Install osbuild and dependencies
# Install core system tools
RUN apt-get update && apt-get install -y \
git \
curl \
wget \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install image building dependencies
COPY ./package-requires.txt .
RUN apt-get update && grep -vE '^#' package-requires.txt | xargs apt-get install -y && rm -f package-requires.txt && apt-get clean
COPY --from=builder /build/bin/* /usr/bin/
# Copy our simplified bootc-image-builder script
COPY scripts/bootc-image-builder.sh /usr/bin/bootc-image-builder
RUN chmod +x /usr/bin/bootc-image-builder
# Copy data files
COPY bib/data /usr/share/bootc-image-builder
ENTRYPOINT ["/usr/bin/bootc-image-builder"]
VOLUME /output
WORKDIR /output
VOLUME /store
VOLUME /rpmmd
VOLUME /var/lib/containers/storage
LABEL description="This tools allows to build and deploy disk-images from bootc container inputs."
LABEL io.k8s.description="This tools allows to build and deploy disk-images from bootc container inputs."
LABEL io.k8s.display-name="Debian Bootc Image Builder"
LABEL io.openshift.tags="base debian-bookworm"
LABEL summary="A container to create disk-images from bootc container inputs"
LABEL description="Simplified Debian bootc-image-builder for container-to-disk conversion"
LABEL io.k8s.description="Simplified Debian bootc-image-builder for container-to-disk conversion"
LABEL io.k8s.display-name="Debian Bootc Image Builder (Simplified)"
LABEL io.openshift.tags="base debian-trixie"
LABEL summary="A simplified container to create disk-images from bootc container inputs using native Debian tools"

View file

@ -30,7 +30,7 @@ import (
"github.com/particle-os/debian-bootc-image-builder/bib/internal/imagetypes"
"github.com/particle-os/debian-bootc-image-builder/bib/internal/solver"
"github.com/particle-os/debian-bootc-image-builder/bib/internal/debian-patch"
debianpatch "github.com/particle-os/debian-bootc-image-builder/bib/internal/debian-patch"
podman_container "github.com/osbuild/images/pkg/bib/container"
"github.com/osbuild/images/pkg/bib/osinfo"
@ -439,23 +439,13 @@ func cmdBuild(cmd *cobra.Command, args []string) error {
outputDir, _ := cmd.Flags().GetString("output")
targetArch, _ := cmd.Flags().GetString("target-arch")
progressType, _ := cmd.Flags().GetString("progress")
useDebos, _ := cmd.Flags().GetBool("use-debos")
useOsbuild, _ := cmd.Flags().GetBool("use-osbuild")
// TODO: Implement debos and osbuild backend selection
// useDebos, _ := cmd.Flags().GetBool("use-debos")
// useOsbuild, _ := cmd.Flags().GetBool("use-osbuild")
// Determine which backend to use
// Default to debos for Debian-based images, osbuild for others
// Can be overridden with explicit flags
backendChoice := "auto"
if useDebos {
backendChoice = "debos"
} else if useOsbuild {
backendChoice = "osbuild"
}
// If debos is explicitly requested or auto-detected, use debos backend
if backendChoice == "debos" || (backendChoice == "auto" && isDebianBasedImage(args[0])) {
return cmdBuildDebos(cmd, args)
}
// For now, always use osbuild backend since debos is not implemented
// TODO: Implement debos backend support
logrus.Debug("Validating environment")
if err := setup.Validate(targetArch); err != nil {

Binary file not shown.

Binary file not shown.

View file

@ -1,23 +1,30 @@
# List package dependencies here; this file is processed
# from the Containerfile by default, using leading '#' as comments.
# This project uses osbuild
osbuild osbuild-ostree osbuild-depsolve-apt osbuild-lvm2
# We mount container images internally
# Core container and image tools
podman
qemu-utils
skopeo
# OSTree support
ostree
ostree-boot
# Debian package management
apt
apt-utils
# Bootloader tools (will be provided by deb-bootupd)
# bootupd
# Image building dependencies
qemu-utils
# ostree wants these for packages
debian-archive-keyring
parted
fdisk
util-linux
mount
rsync
# Debian AppArmor support (replacing SELinux)
apparmor apparmor-utils apparmor-profiles
# Konflux mounts in /etc/pki/entitlement instead of /run/secrets.
# This is not how we intended bib to work, but it works if subscription-manager is in bib.
# Include it temporarily, before we find a better long-term solution.
# See https://github.com/konflux-ci/build-definitions/blob/f3ac40bbc0230eccb8d98a4d54dabd55a4943c5d/task/build-vm-image/0.1/build-vm-image.yaml#L198
# Note: subscription-manager is Red Hat specific, not needed for Debian
# Note: Removed osbuild dependencies - we'll use native Debian tools instead

348
scripts/bootc-image-builder.sh Executable file
View file

@ -0,0 +1,348 @@
#!/bin/bash
# Simplified bootc-image-builder for Debian
# Focuses on container-to-disk conversion without osbuild complexity
set -euo pipefail
# System tool paths
QEMU_IMG="/usr/bin/qemu-img"
RSYNC="/usr/bin/rsync"
TAR="/bin/tar"
MKDIR="/bin/mkdir"
CP="/bin/cp"
LOSETUP="/usr/sbin/losetup"
PARTED="/usr/sbin/parted"
MOUNT="/bin/mount"
UMOUNT="/bin/umount"
BOOTUPD="/usr/libexec/bootupd"
GRUB_INSTALL="/usr/sbin/grub-install"
GRUB_MKCONFIG="/usr/sbin/grub-mkconfig"
# Default values
CONTAINER_IMAGE=""
OUTPUT_FORMAT="qcow2"
OUTPUT_DIR="/output"
STORE_DIR="/store"
CONTAINER_STORAGE="/var/lib/containers/storage"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Help function
show_help() {
cat << EOF
Simplified Debian bootc-image-builder
Usage: $0 [OPTIONS] <container-image>
Options:
-f, --format FORMAT Output format (qcow2, raw, img) [default: qcow2]
-o, --output DIR Output directory [default: /output]
-s, --store DIR Store directory [default: /store]
-h, --help Show this help message
Examples:
$0 simple-cli:latest
$0 -f raw -o /tmp/output simple-cli:latest
$0 --format qcow2 --output /output simple-cli:latest
EOF
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-f|--format)
OUTPUT_FORMAT="$2"
shift 2
;;
-o|--output)
OUTPUT_DIR="$2"
shift 2
;;
-s|--store)
STORE_DIR="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
-*)
log_error "Unknown option $1"
show_help
exit 1
;;
*)
CONTAINER_IMAGE="$1"
shift
;;
esac
done
# Validate inputs
if [[ -z "$CONTAINER_IMAGE" ]]; then
log_error "Container image is required"
show_help
exit 1
fi
if [[ ! -d "$OUTPUT_DIR" ]]; then
log_error "Output directory $OUTPUT_DIR does not exist"
exit 1
fi
# Validate output format
case "$OUTPUT_FORMAT" in
qcow2|raw|img)
;;
*)
log_error "Unsupported output format: $OUTPUT_FORMAT"
exit 1
;;
esac
log_info "Starting simplified bootc-image-builder"
log_info "Container image: $CONTAINER_IMAGE"
log_info "Output format: $OUTPUT_FORMAT"
log_info "Output directory: $OUTPUT_DIR"
# Create temporary working directory
# Use /var/tmp or /home instead of /tmp to avoid tmpfs space limitations
WORK_DIR=$(mktemp -d /var/tmp/bootc-builder-XXXXXX 2>/dev/null || mktemp -d /home/joe/tmp/bootc-builder-XXXXXX 2>/dev/null || echo "/home/joe/tmp/bootc-builder-$$")
if [[ ! -d "$WORK_DIR" ]]; then
mkdir -p "$WORK_DIR" || {
log_error "Failed to create working directory"
exit 1
}
fi
log_info "Working directory: $WORK_DIR"
# Check available space
AVAILABLE_SPACE=$(df "$WORK_DIR" | awk 'NR==2 {print $4}')
if [[ "$AVAILABLE_SPACE" -lt 5000000 ]]; then # Less than 5GB
log_warn "Low disk space available: ${AVAILABLE_SPACE}KB"
log_warn "This might cause issues with large containers"
fi
# Function to cleanup on exit
cleanup() {
log_info "Cleaning up..."
# Unmount if still mounted
if [[ -n "${MOUNT_POINT:-}" ]] && mountpoint -q "$MOUNT_POINT" 2>/dev/null; then
log_info "Unmounting $MOUNT_POINT..."
sudo $UMOUNT "$MOUNT_POINT" || true
fi
# Detach loopback device if still attached
if [[ -n "${LOOP_DEV:-}" ]] && [[ -b "$LOOP_DEV" ]]; then
log_info "Detaching loopback device $LOOP_DEV..."
sudo $LOSETUP -d "$LOOP_DEV" || true
fi
# Remove working directory
if [[ -n "${WORK_DIR:-}" ]] && [[ -d "$WORK_DIR" ]]; then
log_info "Removing working directory $WORK_DIR..."
sudo rm -rf "$WORK_DIR" || true
fi
}
trap cleanup EXIT
# Check if container image exists locally
log_info "Checking if container image exists locally: $CONTAINER_IMAGE"
if podman images --format "{{.Repository}}:{{.Tag}}" | grep -q "^$CONTAINER_IMAGE$"; then
log_info "Container image found locally, skipping pull"
elif podman images --format "{{.Repository}}:{{.Tag}}" | grep -q "simple-cli:latest"; then
log_info "Found simple-cli:latest locally, using that instead"
CONTAINER_IMAGE="simple-cli:latest"
elif podman images --format "{{.Repository}}:{{.Tag}}" | grep -q "localhost/simple-cli:latest"; then
log_info "Found localhost/simple-cli:latest locally, using that instead"
CONTAINER_IMAGE="localhost/simple-cli:latest"
else
log_info "Container image not found locally, attempting to pull: $CONTAINER_IMAGE"
podman pull "$CONTAINER_IMAGE" || {
log_error "Failed to pull container image. Please ensure the image is available."
log_error "You can either:"
log_error "1. Build the image locally first"
log_error "2. Use a fully qualified registry URL"
exit 1
}
fi
# Export the container to a tar file
log_info "Exporting container to tar..."
podman export "$(podman create --rm "$CONTAINER_IMAGE" true)" > "$WORK_DIR/container.tar"
# Create a loopback device and mount the container
log_info "Setting up loopback device..."
cd "$WORK_DIR"
# Extract the container
log_info "Extracting container filesystem..."
mkdir -p container-fs
if ! tar -xf container.tar -C container-fs; then
log_error "Failed to extract container. This might be due to:"
log_error "1. Insufficient disk space"
log_error "2. Corrupted container archive"
log_error "3. Permission issues"
exit 1
fi
# Create disk image using qemu-img (no sudo required)
log_info "Creating disk image..."
IMAGE_SIZE="2G" # Default size, could be made configurable
# Create a proper disk image with partitions for bootloader installation
log_info "Creating partitioned disk image for bootloader installation..."
# Create raw image file
$QEMU_IMG create -f raw disk.raw "$IMAGE_SIZE"
# For bootloader installation, we need to create a proper disk structure
# This requires sudo for partitioning and mounting
log_info "Setting up disk partitions for bootloader installation..."
log_info "Note: This step requires sudo for disk operations"
# Create partition table and filesystem
log_info "Creating partition table and filesystem..."
sudo $PARTED disk.raw mklabel msdos
sudo $PARTED disk.raw mkpart primary ext4 1MiB 100%
sudo $PARTED disk.raw set 1 boot on
# Setup loopback device
log_info "Setting up loopback device..."
LOOP_DEV=$(sudo $LOSETUP --find --show disk.raw)
log_info "Using loopback device: $LOOP_DEV"
# Force kernel to reread partition table
log_info "Rereading partition table..."
sudo partprobe "$LOOP_DEV" || true
# Get the partition device
PART_DEV="${LOOP_DEV}p1"
log_info "Partition device: $PART_DEV"
# Wait for partition to be available and verify it exists
log_info "Waiting for partition to be available..."
for i in {1..10}; do
if [[ -b "$PART_DEV" ]]; then
log_info "Partition device found: $PART_DEV"
break
fi
log_info "Waiting for partition device... (attempt $i/10)"
sleep 1
done
if [[ ! -b "$PART_DEV" ]]; then
log_error "Partition device not found: $PART_DEV"
log_error "Available devices:"
ls -la /dev/loop* || true
exit 1
fi
# Create filesystem
log_info "Creating ext4 filesystem..."
sudo mkfs.ext4 "$PART_DEV"
# Mount the partition
MOUNT_POINT="$WORK_DIR/mount-point"
mkdir -p "$MOUNT_POINT"
sudo $MOUNT "$PART_DEV" "$MOUNT_POINT"
# Copy container filesystem
log_info "Copying container filesystem to disk image..."
sudo rsync -a container-fs/ "$MOUNT_POINT/"
# Install bootloader manually since deb-bootupd doesn't support non-default sysroots
log_info "Installing bootloader manually..."
if [[ -x "$GRUB_INSTALL" ]]; then
log_info "Found grub-install, installing GRUB bootloader..."
# Install GRUB to the mounted filesystem
# Use --root-directory to specify the mount point
# Use --target=i386-pc for BIOS systems
# Use --boot-directory to specify where boot files are located
log_info "Installing GRUB to $MOUNT_POINT..."
sudo "$GRUB_INSTALL" \
--root-directory="$MOUNT_POINT" \
--target=i386-pc \
--boot-directory="$MOUNT_POINT/boot" \
--force \
"$LOOP_DEV" || {
log_warn "GRUB installation failed, continuing without bootloader"
}
# Generate GRUB configuration manually since grub-mkconfig needs host environment
log_info "Generating GRUB configuration manually..."
# Create a basic GRUB configuration for the container
cat > "$MOUNT_POINT/boot/grub/grub.cfg" << 'EOF'
# GRUB configuration for simple-cli container
set timeout=5
set default=0
menuentry "Simple CLI" {
set root=(hd0,msdos1)
linux /boot/vmlinuz-6.12.38+deb13-amd64 root=/dev/sda1 rw console=ttyS0
initrd /boot/initrd.img-6.12.38+deb13-amd64
}
menuentry "Simple CLI (Recovery)" {
set root=(hd0,msdos1)
linux /boot/vmlinuz-6.12.38+deb13-amd64 root=/dev/sda1 rw single console=ttyS0
initrd /boot/initrd.img-6.12.38+deb13-amd64
}
EOF
log_info "GRUB configuration created at $MOUNT_POINT/boot/grub/grub.cfg"
else
log_warn "grub-install not found at $GRUB_INSTALL, skipping bootloader installation"
log_warn "Image will not be bootable without bootloader"
fi
# Unmount
sudo $UMOUNT "$MOUNT_POINT"
# Detach loopback device
sudo $LOSETUP -d "$LOOP_DEV"
# Convert to requested format
log_info "Converting to $OUTPUT_FORMAT format..."
case "$OUTPUT_FORMAT" in
qcow2)
$QEMU_IMG convert -f raw -O qcow2 disk.raw "$OUTPUT_DIR/$(basename "$CONTAINER_IMAGE" | tr ':' '_').qcow2"
;;
raw)
cp disk.raw "$OUTPUT_DIR/$(basename "$CONTAINER_IMAGE" | tr ':' '_').raw"
;;
img)
cp disk.raw "$OUTPUT_DIR/$(basename "$CONTAINER_IMAGE" | tr ':' '_').img"
;;
esac
log_info "Image creation completed successfully!"
log_info "Output file: $OUTPUT_DIR/$(basename "$CONTAINER_IMAGE" | tr ':' '_').$OUTPUT_FORMAT"
# List output files
log_info "Output directory contents:"
ls -la "$OUTPUT_DIR"