#!/bin/bash # create-oci-image.sh — Package KubeSolo OS as an OCI container image # # Creates an OCI image containing the kernel and initramfs, suitable for # distribution via container registries (Docker Hub, GHCR, Quay, etc.). # # The OCI image is a minimal scratch-based image containing: # /vmlinuz — kernel # /kubesolo-os.gz — initramfs # /version — version string # /metadata.json — build metadata # # Usage: # build/scripts/create-oci-image.sh [--registry REGISTRY] [--push] # # Examples: # build/scripts/create-oci-image.sh # build/scripts/create-oci-image.sh --registry ghcr.io/portainer --push # build/scripts/create-oci-image.sh --registry docker.io/portainer --push set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" VERSION="$(cat "$PROJECT_ROOT/VERSION")" OUTPUT_DIR="$PROJECT_ROOT/output" # Defaults REGISTRY="" IMAGE_NAME="kubesolo-os" PUSH=false ARCH="${ARCH:-amd64}" # Parse args while [ $# -gt 0 ]; do case "$1" in --registry) REGISTRY="$2"; shift 2 ;; --push) PUSH=true; shift ;; --arch) ARCH="$2"; shift 2 ;; *) echo "Unknown option: $1" >&2; exit 1 ;; esac done # Build full image tag if [ -n "$REGISTRY" ]; then FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}:${VERSION}" LATEST_TAG="${REGISTRY}/${IMAGE_NAME}:latest" else FULL_IMAGE="${IMAGE_NAME}:${VERSION}" LATEST_TAG="${IMAGE_NAME}:latest" fi echo "==> Building OCI image: $FULL_IMAGE" # Check for required files VMLINUZ="$OUTPUT_DIR/vmlinuz" INITRAMFS="$OUTPUT_DIR/kubesolo-os.gz" # If individual files don't exist, try to extract from ISO if [ ! -f "$VMLINUZ" ] || [ ! -f "$INITRAMFS" ]; then ISO="$OUTPUT_DIR/kubesolo-os-${VERSION}.iso" if [ -f "$ISO" ]; then echo " Extracting from ISO..." TMPDIR=$(mktemp -d) trap "rm -rf $TMPDIR" EXIT # Extract kernel and initramfs from ISO xorriso -osirrox on -indev "$ISO" -extract /boot/vmlinuz "$TMPDIR/vmlinuz" 2>/dev/null || \ bsdtar -xf "$ISO" -C "$TMPDIR" boot/vmlinuz boot/kubesolo-os.gz 2>/dev/null || true # Try common paths for kpath in "$TMPDIR/boot/vmlinuz" "$TMPDIR/vmlinuz"; do [ -f "$kpath" ] && VMLINUZ="$kpath" && break done for ipath in "$TMPDIR/boot/kubesolo-os.gz" "$TMPDIR/kubesolo-os.gz"; do [ -f "$ipath" ] && INITRAMFS="$ipath" && break done fi fi if [ ! -f "$VMLINUZ" ] || [ ! -f "$INITRAMFS" ]; then echo "ERROR: Required files not found:" echo " vmlinuz: $VMLINUZ" echo " kubesolo-os.gz: $INITRAMFS" echo "" echo "Run 'make iso' or 'make initramfs' first." exit 1 fi # Create build context OCI_BUILD="$OUTPUT_DIR/oci-build" rm -rf "$OCI_BUILD" mkdir -p "$OCI_BUILD" cp "$VMLINUZ" "$OCI_BUILD/vmlinuz" cp "$INITRAMFS" "$OCI_BUILD/kubesolo-os.gz" echo "$VERSION" > "$OCI_BUILD/version" # Create metadata cat > "$OCI_BUILD/metadata.json" << EOF { "name": "KubeSolo OS", "version": "$VERSION", "arch": "$ARCH", "build_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "vmlinuz_sha256": "$(sha256sum "$OCI_BUILD/vmlinuz" | cut -d' ' -f1)", "initramfs_sha256": "$(sha256sum "$OCI_BUILD/kubesolo-os.gz" | cut -d' ' -f1)" } EOF # Create Dockerfile cat > "$OCI_BUILD/Dockerfile" << 'DOCKERFILE' FROM scratch LABEL org.opencontainers.image.title="KubeSolo OS" LABEL org.opencontainers.image.description="Immutable Kubernetes OS for edge/IoT" LABEL org.opencontainers.image.vendor="Portainer" LABEL org.opencontainers.image.source="https://github.com/portainer/kubesolo-os" COPY vmlinuz /vmlinuz COPY kubesolo-os.gz /kubesolo-os.gz COPY version /version COPY metadata.json /metadata.json DOCKERFILE # Build OCI image echo " Building..." docker build \ --platform "linux/${ARCH}" \ -t "$FULL_IMAGE" \ -t "$LATEST_TAG" \ -f "$OCI_BUILD/Dockerfile" \ "$OCI_BUILD" echo " Built: $FULL_IMAGE" echo " Size: $(docker image inspect "$FULL_IMAGE" --format='{{.Size}}' | awk '{printf "%.1f MB", $1/1024/1024}')" # Push if requested if [ "$PUSH" = true ]; then echo " Pushing to registry..." docker push "$FULL_IMAGE" docker push "$LATEST_TAG" echo " Pushed: $FULL_IMAGE" echo " Pushed: $LATEST_TAG" fi # Cleanup rm -rf "$OCI_BUILD" echo "" echo "==> OCI image ready: $FULL_IMAGE" echo "" echo "Usage:" echo " # Pull and extract on target machine:" echo " docker create --name kubesolo-extract $FULL_IMAGE" echo " docker cp kubesolo-extract:/vmlinuz ./vmlinuz" echo " docker cp kubesolo-extract:/kubesolo-os.gz ./kubesolo-os.gz" echo " docker rm kubesolo-extract"