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
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:
parent
ab30e683d1
commit
9a810d3d27
7 changed files with 402 additions and 49 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
BIN
minimal-test.img
BIN
minimal-test.img
Binary file not shown.
|
|
@ -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
348
scripts/bootc-image-builder.sh
Executable 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"
|
||||
Loading…
Add table
Add a link
Reference in a new issue