Replace AGENT_KERNEL_PATH, AGENT_IMAGES_PATH, AGENT_SANDBOXES_PATH, AGENT_SNAPSHOTS_PATH, and AGENT_TOKEN_FILE with a single AGENT_FILES_ROOTDIR (default /var/lib/wrenn) that derives all subdirectory paths automatically.
125 lines
3.9 KiB
Bash
Executable File
125 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# rootfs-from-container.sh — Create a bootable Wrenn rootfs from a Docker container.
|
|
#
|
|
# Exports a container's filesystem, writes it into an ext4 image, injects
|
|
# envd + wrenn-init, and shrinks the image to minimum size.
|
|
#
|
|
# Usage:
|
|
# bash scripts/rootfs-from-container.sh <container> <image_name>
|
|
#
|
|
# Arguments:
|
|
# container — Docker container name or ID to export
|
|
# image_name — Directory name under images dir (e.g. "waitlist")
|
|
#
|
|
# Output:
|
|
# ${AGENT_FILES_ROOTDIR}/images/<image_name>/rootfs.ext4
|
|
#
|
|
# Requires: docker, mkfs.ext4, resize2fs, e2fsck, make (for building envd)
|
|
# Sudo is used only for mount/umount/copy-into-image operations.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
AGENT_FILES_ROOTDIR="${AGENT_FILES_ROOTDIR:-/var/lib/wrenn}"
|
|
AGENT_IMAGES_PATH="${AGENT_FILES_ROOTDIR}/images"
|
|
|
|
if [ $# -lt 2 ]; then
|
|
echo "Usage: $0 <container> <image_name>"
|
|
exit 1
|
|
fi
|
|
|
|
CONTAINER="$1"
|
|
IMAGE_NAME="$2"
|
|
OUTPUT_DIR="${AGENT_IMAGES_PATH}/${IMAGE_NAME}"
|
|
OUTPUT_FILE="${OUTPUT_DIR}/rootfs.ext4"
|
|
MOUNT_DIR="/tmp/wrenn-rootfs-build"
|
|
TAR_FILE="/tmp/wrenn-rootfs-export-${IMAGE_NAME}.tar"
|
|
|
|
# Verify the container exists.
|
|
if ! docker inspect "${CONTAINER}" > /dev/null 2>&1; then
|
|
echo "ERROR: Container '${CONTAINER}' not found"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 1: Build envd.
|
|
echo "==> Building envd..."
|
|
cd "${PROJECT_ROOT}"
|
|
make build-envd
|
|
ENVD_BIN="${PROJECT_ROOT}/builds/envd"
|
|
|
|
if [ ! -f "${ENVD_BIN}" ]; then
|
|
echo "ERROR: envd binary not found at ${ENVD_BIN}"
|
|
exit 1
|
|
fi
|
|
|
|
if ! file "${ENVD_BIN}" | grep -q "statically linked"; then
|
|
echo "ERROR: envd is not statically linked!"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 2: Export container filesystem.
|
|
echo "==> Exporting container '${CONTAINER}'..."
|
|
docker export "${CONTAINER}" -o "${TAR_FILE}"
|
|
|
|
cleanup() {
|
|
echo "==> Cleaning up..."
|
|
sudo umount "${MOUNT_DIR}" 2>/dev/null || true
|
|
rmdir "${MOUNT_DIR}" 2>/dev/null || true
|
|
rm -f "${TAR_FILE}"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Step 3: Create an oversized ext4 image.
|
|
# Use 2x the tar size + 256MB headroom for filesystem overhead and injected binaries.
|
|
TAR_SIZE_BYTES="$(stat --format=%s "${TAR_FILE}")"
|
|
INITIAL_SIZE_MB=$(( (TAR_SIZE_BYTES / 1024 / 1024) * 2 + 256 ))
|
|
echo "==> Creating ${INITIAL_SIZE_MB}MB ext4 image (will shrink after populating)..."
|
|
sudo mkdir -p "${OUTPUT_DIR}"
|
|
sudo dd if=/dev/zero of="${OUTPUT_FILE}" bs=1M count="${INITIAL_SIZE_MB}" status=progress
|
|
sudo mkfs.ext4 -F "${OUTPUT_FILE}"
|
|
|
|
# Step 4: Mount and populate.
|
|
echo "==> Mounting image at ${MOUNT_DIR}..."
|
|
mkdir -p "${MOUNT_DIR}"
|
|
sudo mount -o loop "${OUTPUT_FILE}" "${MOUNT_DIR}"
|
|
|
|
echo "==> Extracting container filesystem..."
|
|
sudo tar xf "${TAR_FILE}" -C "${MOUNT_DIR}"
|
|
|
|
# Step 5: Inject wrenn guest binaries.
|
|
echo "==> Installing envd..."
|
|
sudo mkdir -p "${MOUNT_DIR}/usr/local/bin"
|
|
sudo cp "${ENVD_BIN}" "${MOUNT_DIR}/usr/local/bin/envd"
|
|
sudo chmod 755 "${MOUNT_DIR}/usr/local/bin/envd"
|
|
|
|
echo "==> Installing wrenn-init..."
|
|
sudo cp "${PROJECT_ROOT}/images/wrenn-init.sh" "${MOUNT_DIR}/usr/local/bin/wrenn-init"
|
|
sudo chmod 755 "${MOUNT_DIR}/usr/local/bin/wrenn-init"
|
|
|
|
# Step 6: Verify.
|
|
echo ""
|
|
echo "==> Installed guest binaries:"
|
|
ls -la "${MOUNT_DIR}/usr/local/bin/envd" "${MOUNT_DIR}/usr/local/bin/wrenn-init"
|
|
|
|
# Unmount before shrinking.
|
|
sudo umount "${MOUNT_DIR}"
|
|
rmdir "${MOUNT_DIR}" 2>/dev/null || true
|
|
|
|
# Step 7: Shrink the image to minimum size.
|
|
echo ""
|
|
echo "==> Shrinking image..."
|
|
sudo e2fsck -fy "${OUTPUT_FILE}"
|
|
sudo resize2fs -M "${OUTPUT_FILE}"
|
|
|
|
# Truncate the file to match the shrunk filesystem.
|
|
BLOCK_COUNT="$(sudo dumpe2fs -h "${OUTPUT_FILE}" 2>/dev/null | grep "Block count:" | awk '{print $3}')"
|
|
BLOCK_SIZE="$(sudo dumpe2fs -h "${OUTPUT_FILE}" 2>/dev/null | grep "Block size:" | awk '{print $3}')"
|
|
FS_SIZE_BYTES=$((BLOCK_COUNT * BLOCK_SIZE))
|
|
sudo truncate -s "${FS_SIZE_BYTES}" "${OUTPUT_FILE}"
|
|
|
|
FINAL_SIZE_MB=$((FS_SIZE_BYTES / 1024 / 1024))
|
|
echo ""
|
|
echo "==> Done. Rootfs created at: ${OUTPUT_FILE} (${FINAL_SIZE_MB}MB)"
|