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:
110
build/scripts/create-disk-image.sh
Executable file
110
build/scripts/create-disk-image.sh
Executable 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
140
build/scripts/create-iso.sh
Executable 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
83
build/scripts/extract-core.sh
Executable 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."
|
||||
72
build/scripts/fetch-components.sh
Executable file
72
build/scripts/fetch-components.sh
Executable 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
124
build/scripts/inject-kubesolo.sh
Executable 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
23
build/scripts/pack-initramfs.sh
Executable 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)"
|
||||
Reference in New Issue
Block a user