feat: initial Phase 1 PoC scaffolding for KubeSolo OS

Complete Phase 1 implementation of KubeSolo OS — an immutable, bootable
Linux distribution built on Tiny Core Linux for running KubeSolo
single-node Kubernetes.

Build system:
- Makefile with fetch, rootfs, initramfs, iso, disk-image targets
- Dockerfile.builder for reproducible builds
- Scripts to download Tiny Core, extract rootfs, inject KubeSolo,
  pack initramfs, and create bootable ISO/disk images

Init system (10 POSIX sh stages):
- Early mount (proc/sys/dev/cgroup2), cmdline parsing, persistent
  mount with bind-mounts, kernel module loading, sysctl, DHCP
  networking, hostname, clock sync, containerd prep, KubeSolo exec

Shared libraries:
- functions.sh (device wait, IP lookup, config helpers)
- network.sh (static IP, config persistence, interface detection)
- health.sh (containerd, API server, node readiness checks)
- Emergency shell for boot failure debugging

Testing:
- QEMU boot test with serial log marker detection
- K8s readiness test with kubectl verification
- Persistence test (reboot + verify state survives)
- Workload deployment test (nginx pod)
- Local storage test (PVC + local-path provisioner)
- Network policy test
- Reusable run-vm.sh launcher

Developer tools:
- dev-vm.sh (interactive QEMU with port forwarding)
- rebuild-initramfs.sh (fast iteration)
- inject-ssh.sh (dropbear SSH for debugging)
- extract-kernel-config.sh + kernel-audit.sh

Documentation:
- Full design document with architecture research
- Boot flow documentation covering all 10 init stages
- Cloud-init examples (DHCP, static IP, Portainer Edge, air-gapped)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 10:18:42 -06:00
commit e372df578b
50 changed files with 4392 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
#!/bin/bash
# create-disk-image.sh — Create a raw disk image with boot + data partitions
# Phase 1: simple layout (boot + data). Phase 3 adds A/B system partitions.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_ROOT/output}"
VERSION="$(cat "$PROJECT_ROOT/VERSION")"
OS_NAME="kubesolo-os"
IMG_OUTPUT="$OUTPUT_DIR/${OS_NAME}-${VERSION}.img"
IMG_SIZE_MB="${IMG_SIZE_MB:-2048}" # 2 GB default
VMLINUZ="$ROOTFS_DIR/vmlinuz"
INITRAMFS="$ROOTFS_DIR/kubesolo-os.gz"
for f in "$VMLINUZ" "$INITRAMFS"; do
[ -f "$f" ] || { echo "ERROR: Missing $f — run 'make initramfs'"; exit 1; }
done
echo "==> Creating ${IMG_SIZE_MB}MB disk image..."
mkdir -p "$OUTPUT_DIR"
# Create sparse image
dd if=/dev/zero of="$IMG_OUTPUT" bs=1M count=0 seek="$IMG_SIZE_MB" 2>/dev/null
# Partition: 256MB boot (ext4) + rest data (ext4)
# Using sfdisk for scriptability
sfdisk "$IMG_OUTPUT" << EOF
label: dos
unit: sectors
# Boot partition: 256 MB, bootable
start=2048, size=524288, type=83, bootable
# Data partition: remaining space
start=526336, type=83
EOF
# Set up loop device
LOOP=$(losetup --show -fP "$IMG_OUTPUT")
echo "==> Loop device: $LOOP"
cleanup() {
umount "${LOOP}p1" 2>/dev/null || true
umount "${LOOP}p2" 2>/dev/null || true
losetup -d "$LOOP" 2>/dev/null || true
rm -rf "$MNT_BOOT" "$MNT_DATA" 2>/dev/null || true
}
trap cleanup EXIT
# Format partitions
mkfs.ext4 -q -L KSOLOBOOT "${LOOP}p1"
mkfs.ext4 -q -L KSOLODATA "${LOOP}p2"
# Mount and populate boot partition
MNT_BOOT=$(mktemp -d)
MNT_DATA=$(mktemp -d)
mount "${LOOP}p1" "$MNT_BOOT"
mount "${LOOP}p2" "$MNT_DATA"
# Install syslinux + kernel + initramfs to boot partition
mkdir -p "$MNT_BOOT/boot/syslinux"
cp "$VMLINUZ" "$MNT_BOOT/boot/vmlinuz"
cp "$INITRAMFS" "$MNT_BOOT/boot/kubesolo-os.gz"
# Syslinux config for disk boot (extlinux)
cat > "$MNT_BOOT/boot/syslinux/syslinux.cfg" << 'EOF'
DEFAULT kubesolo
TIMEOUT 30
PROMPT 0
LABEL kubesolo
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND quiet kubesolo.data=LABEL=KSOLODATA
LABEL kubesolo-debug
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND kubesolo.data=LABEL=KSOLODATA kubesolo.debug console=ttyS0,115200n8
LABEL kubesolo-shell
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND kubesolo.shell console=ttyS0,115200n8
EOF
# Install extlinux bootloader
if command -v extlinux >/dev/null 2>&1; then
extlinux --install "$MNT_BOOT/boot/syslinux" 2>/dev/null || {
echo "WARN: extlinux install failed — image may not be directly bootable"
echo " Use with QEMU -kernel/-initrd flags instead"
}
fi
# Prepare data partition structure
for dir in kubesolo containerd etc-kubesolo log usr-local network; do
mkdir -p "$MNT_DATA/$dir"
done
sync
echo ""
echo "==> Disk image created: $IMG_OUTPUT"
echo " Size: $(du -h "$IMG_OUTPUT" | cut -f1)"
echo " Boot partition (KSOLOBOOT): kernel + initramfs"
echo " Data partition (KSOLODATA): persistent K8s state"

140
build/scripts/create-iso.sh Executable file
View File

@@ -0,0 +1,140 @@
#!/bin/bash
# create-iso.sh — Create a bootable ISO from kernel + initramfs
# Uses isolinux (syslinux) for Phase 1 simplicity (GRUB in Phase 3)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_ROOT/output}"
VERSION="$(cat "$PROJECT_ROOT/VERSION")"
OS_NAME="kubesolo-os"
ISO_STAGING="$ROOTFS_DIR/iso-staging"
ISO_OUTPUT="$OUTPUT_DIR/${OS_NAME}-${VERSION}.iso"
VMLINUZ="$ROOTFS_DIR/vmlinuz"
INITRAMFS="$ROOTFS_DIR/kubesolo-os.gz"
# Validate inputs
for f in "$VMLINUZ" "$INITRAMFS"; do
if [ ! -f "$f" ]; then
echo "ERROR: Missing required file: $f"
echo "Run 'make initramfs' first."
exit 1
fi
done
# Check for required tools
for cmd in mkisofs xorriso genisoimage; do
if command -v "$cmd" >/dev/null 2>&1; then
MKISO_CMD="$cmd"
break
fi
done
if [ -z "${MKISO_CMD:-}" ]; then
echo "ERROR: Need mkisofs, genisoimage, or xorriso to create ISO"
exit 1
fi
# --- Stage ISO contents ---
rm -rf "$ISO_STAGING"
mkdir -p "$ISO_STAGING/boot/isolinux"
cp "$VMLINUZ" "$ISO_STAGING/boot/vmlinuz"
cp "$INITRAMFS" "$ISO_STAGING/boot/kubesolo-os.gz"
# Find isolinux.bin
ISOLINUX_BIN=""
for path in /usr/lib/ISOLINUX/isolinux.bin /usr/lib/syslinux/isolinux.bin \
/usr/share/syslinux/isolinux.bin /usr/lib/syslinux/bios/isolinux.bin; do
[ -f "$path" ] && ISOLINUX_BIN="$path" && break
done
if [ -z "$ISOLINUX_BIN" ]; then
echo "ERROR: Cannot find isolinux.bin. Install syslinux/isolinux package."
exit 1
fi
cp "$ISOLINUX_BIN" "$ISO_STAGING/boot/isolinux/"
# Copy ldlinux.c32 if it exists (needed by syslinux 6+)
LDLINUX_DIR="$(dirname "$ISOLINUX_BIN")"
for mod in ldlinux.c32 libcom32.c32 libutil.c32 mboot.c32; do
[ -f "$LDLINUX_DIR/$mod" ] && cp "$LDLINUX_DIR/$mod" "$ISO_STAGING/boot/isolinux/"
done
# Isolinux config
cat > "$ISO_STAGING/boot/isolinux/isolinux.cfg" << 'EOF'
DEFAULT kubesolo
TIMEOUT 30
PROMPT 0
LABEL kubesolo
MENU LABEL KubeSolo OS
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND quiet kubesolo.data=LABEL=KSOLODATA
LABEL kubesolo-debug
MENU LABEL KubeSolo OS (debug)
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND kubesolo.data=LABEL=KSOLODATA kubesolo.debug console=ttyS0,115200n8
LABEL kubesolo-shell
MENU LABEL KubeSolo OS (emergency shell)
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND kubesolo.shell console=ttyS0,115200n8
LABEL kubesolo-nopersist
MENU LABEL KubeSolo OS (RAM only, no persistence)
KERNEL /boot/vmlinuz
INITRD /boot/kubesolo-os.gz
APPEND kubesolo.nopersist
EOF
# --- Create ISO ---
mkdir -p "$OUTPUT_DIR"
case "$MKISO_CMD" in
xorriso)
xorriso -as mkisofs \
-o "$ISO_OUTPUT" \
-isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin 2>/dev/null || true \
-c boot/isolinux/boot.cat \
-b boot/isolinux/isolinux.bin \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
"$ISO_STAGING"
;;
*)
"$MKISO_CMD" \
-o "$ISO_OUTPUT" \
-b boot/isolinux/isolinux.bin \
-c boot/isolinux/boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
-J -R -V "KUBESOLOOS" \
"$ISO_STAGING"
;;
esac
# Make ISO hybrid-bootable (USB stick)
if command -v isohybrid >/dev/null 2>&1; then
isohybrid "$ISO_OUTPUT" 2>/dev/null || true
fi
# Clean staging
rm -rf "$ISO_STAGING"
echo ""
echo "==> ISO created: $ISO_OUTPUT"
echo " Size: $(du -h "$ISO_OUTPUT" | cut -f1)"
echo ""
echo " Boot in QEMU: make dev-vm"
echo " Write to USB: dd if=$ISO_OUTPUT of=/dev/sdX bs=4M status=progress"

83
build/scripts/extract-core.sh Executable file
View File

@@ -0,0 +1,83 @@
#!/bin/bash
# extract-core.sh — Extract Tiny Core Linux rootfs from ISO
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
# shellcheck source=../config/versions.env
. "$SCRIPT_DIR/../config/versions.env"
TC_ISO="$CACHE_DIR/$TINYCORE_ISO"
ISO_MNT="$ROOTFS_DIR/iso-mount"
if [ ! -f "$TC_ISO" ]; then
echo "ERROR: Tiny Core ISO not found: $TC_ISO"
echo "Run 'make fetch' first."
exit 1
fi
# Clean previous rootfs
rm -rf "$ROOTFS_DIR"
mkdir -p "$ROOTFS_DIR" "$ISO_MNT"
# --- Mount ISO and extract kernel + initramfs ---
echo "==> Mounting ISO: $TC_ISO"
mount -o loop,ro "$TC_ISO" "$ISO_MNT" 2>/dev/null || {
# Fallback for non-root: use 7z or bsdtar
echo " mount failed (need root?), trying bsdtar..."
mkdir -p "$ISO_MNT"
bsdtar xf "$TC_ISO" -C "$ISO_MNT" 2>/dev/null || {
echo " bsdtar failed, trying 7z..."
7z x -o"$ISO_MNT" "$TC_ISO" >/dev/null 2>&1
}
}
# Find vmlinuz and core.gz (path varies by Tiny Core version/arch)
VMLINUZ=""
COREGZ=""
for f in "$ISO_MNT"/boot/vmlinuz64 "$ISO_MNT"/boot/vmlinuz; do
[ -f "$f" ] && VMLINUZ="$f" && break
done
for f in "$ISO_MNT"/boot/corepure64.gz "$ISO_MNT"/boot/core.gz; do
[ -f "$f" ] && COREGZ="$f" && break
done
if [ -z "$VMLINUZ" ] || [ -z "$COREGZ" ]; then
echo "ERROR: Could not find vmlinuz/core.gz in ISO"
echo "ISO contents:"
find "$ISO_MNT" -type f
umount "$ISO_MNT" 2>/dev/null || true
exit 1
fi
echo "==> Found kernel: $VMLINUZ"
echo "==> Found initramfs: $COREGZ"
# Copy kernel
cp "$VMLINUZ" "$ROOTFS_DIR/vmlinuz"
# --- Extract initramfs (core.gz → rootfs) ---
echo "==> Extracting initramfs..."
mkdir -p "$ROOTFS_DIR/rootfs"
cd "$ROOTFS_DIR/rootfs"
zcat "$COREGZ" | cpio -idm 2>/dev/null
# Unmount ISO
cd "$PROJECT_ROOT"
umount "$ISO_MNT" 2>/dev/null || true
rm -rf "$ISO_MNT"
echo "==> Rootfs extracted: $ROOTFS_DIR/rootfs"
echo " Size: $(du -sh "$ROOTFS_DIR/rootfs" | cut -f1)"
echo " Kernel: $ROOTFS_DIR/vmlinuz ($(du -h "$ROOTFS_DIR/vmlinuz" | cut -f1))"
# --- Audit kernel config if available ---
if [ -f "$ROOTFS_DIR/rootfs/proc/config.gz" ]; then
echo "==> Kernel config found in rootfs, auditing..."
"$SCRIPT_DIR/../config/kernel-audit.sh" <(zcat "$ROOTFS_DIR/rootfs/proc/config.gz") || true
fi
echo "==> Extract complete."

View File

@@ -0,0 +1,72 @@
#!/bin/bash
# fetch-components.sh — Download Tiny Core ISO and KubeSolo binary
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}"
# Load versions
# shellcheck source=../config/versions.env
. "$SCRIPT_DIR/../config/versions.env"
mkdir -p "$CACHE_DIR"
# --- Tiny Core Linux ISO ---
TC_ISO="$CACHE_DIR/$TINYCORE_ISO"
TC_URL="${TINYCORE_MIRROR}/${TINYCORE_VERSION%%.*}.x/${TINYCORE_ARCH}/release/${TINYCORE_ISO}"
if [ -f "$TC_ISO" ]; then
echo "==> Tiny Core ISO already cached: $TC_ISO"
else
echo "==> Downloading Tiny Core Linux ${TINYCORE_VERSION} (${TINYCORE_ARCH})..."
echo " URL: $TC_URL"
wget -q --show-progress -O "$TC_ISO" "$TC_URL" || {
# Fallback: try alternate mirror structure
TC_URL_ALT="${TINYCORE_MIRROR}/${TINYCORE_VERSION%%.*}.x/${TINYCORE_ARCH}/release/CorePure64-current.iso"
echo " Primary URL failed, trying: $TC_URL_ALT"
wget -q --show-progress -O "$TC_ISO" "$TC_URL_ALT"
}
echo "==> Downloaded: $TC_ISO ($(du -h "$TC_ISO" | cut -f1))"
fi
# --- KubeSolo ---
KUBESOLO_INSTALLER="$CACHE_DIR/install-kubesolo.sh"
KUBESOLO_BIN="$CACHE_DIR/kubesolo"
if [ -f "$KUBESOLO_BIN" ]; then
echo "==> KubeSolo binary already cached: $KUBESOLO_BIN"
else
echo "==> Downloading KubeSolo installer..."
curl -sfL "$KUBESOLO_INSTALL_URL" -o "$KUBESOLO_INSTALLER"
echo "==> Extracting KubeSolo binary..."
echo " NOTE: The installer normally runs 'install'. We extract the binary URL instead."
echo " For Phase 1 PoC, install KubeSolo on a host and copy the binary."
echo ""
echo " Manual step required:"
echo " 1. On a Linux x86_64 host: curl -sfL https://get.kubesolo.io | sudo sh -"
echo " 2. Copy /usr/local/bin/kubesolo to: $KUBESOLO_BIN"
echo " 3. Re-run: make rootfs"
echo ""
# Try to extract download URL from installer script
BINARY_URL=$(grep -oP 'https://[^ ]+kubesolo[^ ]+' "$KUBESOLO_INSTALLER" 2>/dev/null | head -1 || true)
if [ -n "$BINARY_URL" ]; then
echo " Attempting direct download from: $BINARY_URL"
curl -sfL "$BINARY_URL" -o "$KUBESOLO_BIN" && chmod +x "$KUBESOLO_BIN" || {
echo " Direct download failed. Use manual step above."
}
fi
if [ -f "$KUBESOLO_BIN" ]; then
echo "==> KubeSolo binary: $KUBESOLO_BIN ($(du -h "$KUBESOLO_BIN" | cut -f1))"
fi
fi
# --- Summary ---
echo ""
echo "==> Component cache:"
ls -lh "$CACHE_DIR"/ 2>/dev/null || true
echo ""
echo "==> Fetch complete."

124
build/scripts/inject-kubesolo.sh Executable file
View File

@@ -0,0 +1,124 @@
#!/bin/bash
# inject-kubesolo.sh — Add KubeSolo binary, init system, and configs to rootfs
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
ROOTFS="$ROOTFS_DIR/rootfs"
VERSION="$(cat "$PROJECT_ROOT/VERSION")"
if [ ! -d "$ROOTFS" ]; then
echo "ERROR: Rootfs not found: $ROOTFS"
echo "Run extract-core.sh first."
exit 1
fi
KUBESOLO_BIN="$CACHE_DIR/kubesolo"
if [ ! -f "$KUBESOLO_BIN" ]; then
echo "ERROR: KubeSolo binary not found: $KUBESOLO_BIN"
echo "See fetch-components.sh output for instructions."
exit 1
fi
echo "==> Injecting KubeSolo into rootfs..."
# --- 1. KubeSolo binary ---
mkdir -p "$ROOTFS/usr/local/bin"
cp "$KUBESOLO_BIN" "$ROOTFS/usr/local/bin/kubesolo"
chmod +x "$ROOTFS/usr/local/bin/kubesolo"
echo " Installed KubeSolo binary ($(du -h "$KUBESOLO_BIN" | cut -f1))"
# --- 2. Custom init system ---
echo " Installing init system..."
# Main init
cp "$PROJECT_ROOT/init/init.sh" "$ROOTFS/sbin/init"
chmod +x "$ROOTFS/sbin/init"
# Init stages
mkdir -p "$ROOTFS/usr/lib/kubesolo-os/init.d"
for stage in "$PROJECT_ROOT"/init/lib/*.sh; do
[ -f "$stage" ] || continue
cp "$stage" "$ROOTFS/usr/lib/kubesolo-os/init.d/"
chmod +x "$ROOTFS/usr/lib/kubesolo-os/init.d/$(basename "$stage")"
done
echo " Installed $(ls "$ROOTFS/usr/lib/kubesolo-os/init.d/" | wc -l) init stages"
# Shared functions
if [ -f "$PROJECT_ROOT/init/lib/functions.sh" ]; then
cp "$PROJECT_ROOT/init/lib/functions.sh" "$ROOTFS/usr/lib/kubesolo-os/functions.sh"
fi
# Emergency shell
if [ -f "$PROJECT_ROOT/init/emergency-shell.sh" ]; then
cp "$PROJECT_ROOT/init/emergency-shell.sh" "$ROOTFS/usr/lib/kubesolo-os/emergency-shell.sh"
chmod +x "$ROOTFS/usr/lib/kubesolo-os/emergency-shell.sh"
fi
# Shared library scripts (network, health)
for lib in network.sh health.sh; do
src="$PROJECT_ROOT/build/rootfs/usr/lib/kubesolo-os/$lib"
[ -f "$src" ] && cp "$src" "$ROOTFS/usr/lib/kubesolo-os/$lib"
done
# --- 3. Kernel modules list ---
cp "$PROJECT_ROOT/build/config/modules.list" "$ROOTFS/usr/lib/kubesolo-os/modules.list"
# --- 4. Sysctl config ---
mkdir -p "$ROOTFS/etc/sysctl.d"
cp "$PROJECT_ROOT/build/rootfs/etc/sysctl.d/k8s.conf" "$ROOTFS/etc/sysctl.d/k8s.conf"
# --- 5. OS metadata ---
echo "$VERSION" > "$ROOTFS/etc/kubesolo-os-version"
cat > "$ROOTFS/etc/os-release" << EOF
NAME="KubeSolo OS"
VERSION="$VERSION"
ID=kubesolo-os
VERSION_ID=$VERSION
PRETTY_NAME="KubeSolo OS $VERSION"
HOME_URL="https://github.com/portainer/kubesolo"
BUG_REPORT_URL="https://github.com/portainer/kubesolo/issues"
EOF
# --- 6. Default KubeSolo config ---
mkdir -p "$ROOTFS/etc/kubesolo"
if [ -f "$PROJECT_ROOT/build/rootfs/etc/kubesolo/defaults.yaml" ]; then
cp "$PROJECT_ROOT/build/rootfs/etc/kubesolo/defaults.yaml" "$ROOTFS/etc/kubesolo/defaults.yaml"
fi
# --- 7. Essential directories ---
mkdir -p "$ROOTFS/var/lib/kubesolo"
mkdir -p "$ROOTFS/var/lib/containerd"
mkdir -p "$ROOTFS/etc/kubesolo"
mkdir -p "$ROOTFS/etc/cni/net.d"
mkdir -p "$ROOTFS/opt/cni/bin"
mkdir -p "$ROOTFS/var/log"
mkdir -p "$ROOTFS/usr/local"
mkdir -p "$ROOTFS/mnt/data"
mkdir -p "$ROOTFS/run/containerd"
# --- 8. Ensure /etc/hosts and /etc/resolv.conf exist ---
if [ ! -f "$ROOTFS/etc/hosts" ]; then
cat > "$ROOTFS/etc/hosts" << EOF
127.0.0.1 localhost
::1 localhost
EOF
fi
if [ ! -f "$ROOTFS/etc/resolv.conf" ]; then
cat > "$ROOTFS/etc/resolv.conf" << EOF
nameserver 8.8.8.8
nameserver 1.1.1.1
EOF
fi
# --- Summary ---
echo ""
echo "==> Injection complete. Rootfs contents:"
echo " Total size: $(du -sh "$ROOTFS" | cut -f1)"
echo " KubeSolo: $(du -h "$ROOTFS/usr/local/bin/kubesolo" | cut -f1)"
echo " Init stages: $(ls "$ROOTFS/usr/lib/kubesolo-os/init.d/" | wc -l)"
echo ""

23
build/scripts/pack-initramfs.sh Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# pack-initramfs.sh — Repack modified rootfs into kubesolo-os.gz
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
ROOTFS="$ROOTFS_DIR/rootfs"
OUTPUT="$ROOTFS_DIR/kubesolo-os.gz"
if [ ! -d "$ROOTFS" ]; then
echo "ERROR: Rootfs not found: $ROOTFS"
exit 1
fi
echo "==> Packing initramfs..."
cd "$ROOTFS"
find . | cpio -o -H newc 2>/dev/null | gzip -9 > "$OUTPUT"
echo "==> Built: $OUTPUT"
echo " Size: $(du -h "$OUTPUT" | cut -f1)"
echo " (Original Tiny Core core.gz is ~11 MB for reference)"