Inject universal /slim/init instead of requiring container-specific setup
CI / build (pull_request) Successful in 12s
CI / build (pull_request) Successful in 12s
slim now works with any Containerfile by injecting distro-agnostic scripts at build time: - /slim/init: mounts special filesystems, configures networking (static QEMU slirp), sets up cgroup2, timezone, and rootless container prereqs. Parses slim.cmd=<base64> from the kernel cmdline for runtime overrides, otherwise execs /slim/exec. - /slim/exec: generated from the image's CMD/ENTRYPOINT (via podman image inspect), overridable via --cmd at build and run time. Changes: - New src/inject.rs: inspect image config, infer command, generate /slim/exec script, inject /slim/ into mounted rootfs - New src/scripts/slim-init.sh: the universal init script (include_str!) - build.rs: rename --init to --cmd, inject scripts before packing, drop Meta::save, restructure to ensure unmount always runs - qemu.rs: hardcode init=/slim/init, add --cmd (base64 on cmdline), drop Meta::load, add -no-reboot - Remove src/meta.rs and meta.toml (no longer needed) - Cargo.toml: add serde_json + base64, remove unused walkdir + cpio + toml - Example Containerfiles simplified to plain FROM + CMD - New example/test.sh for manual verification - README updated for new workflow
This commit is contained in:
@@ -1,9 +1,2 @@
|
||||
FROM alpine:latest
|
||||
RUN mkdir -p /lib/apk/db /run
|
||||
RUN apk add --no-cache --initdb linux-virt busybox util-linux
|
||||
RUN cp /boot/vmlinuz-virt /vmlinuz && echo "Welcome to slim!" > /etc/motd
|
||||
|
||||
# Use custom init script
|
||||
COPY init /init
|
||||
RUN chmod +x /init
|
||||
|
||||
CMD ["/bin/sh"]
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Create console device
|
||||
# TODO: is this necessary?
|
||||
[ -c /dev/console ] || mknod -m 600 /dev/console c 5 1
|
||||
|
||||
# Mount special filesystems
|
||||
mkdir -p /proc /sys
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sysfs /sys
|
||||
|
||||
# Networking
|
||||
echo slim > /proc/sys/kernel/hostname
|
||||
ip link set lo up 2>/dev/null
|
||||
ip link set eth0 up 2>/dev/null
|
||||
udhcpc -i eth0 -f -q # Get DHCP lease
|
||||
|
||||
echo
|
||||
echo 'Welcome to slim!'
|
||||
echo
|
||||
|
||||
# Run interactive shell
|
||||
/bin/sh </dev/console >/dev/console 2>&1
|
||||
|
||||
poweroff -f
|
||||
while true; do
|
||||
sleep 1
|
||||
poweroff -f
|
||||
done
|
||||
@@ -1,23 +1,2 @@
|
||||
# FROM archlinux:latest
|
||||
FROM marvin
|
||||
|
||||
|
||||
USER root
|
||||
|
||||
# Update, install kernel, initramfs tools, and utilities
|
||||
# 'linux' includes mkinitcpio hooks that auto-generate the initramfs
|
||||
RUN pacman -Syu --noconfirm linux busybox util-linux dhcpcd; \
|
||||
pacman -Sc --noconfirm
|
||||
|
||||
# Copy kernel and initramfs to root for VM extraction
|
||||
RUN cp /boot/vmlinuz-linux /vmlinuz && \
|
||||
cp /boot/initramfs-linux.img /initrd.img
|
||||
|
||||
# Allow root login without password
|
||||
RUN passwd -d root
|
||||
|
||||
# Set up timezone. Required to bypass archlinux's first-install setup.
|
||||
RUN ln -s /usr/share/zoneinfo/Europe/Stockholm /etc/localtime
|
||||
|
||||
COPY init /init
|
||||
RUN chmod +x /init
|
||||
FROM archlinux:latest
|
||||
CMD ["/bin/sh"]
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Mount special filesystems
|
||||
mkdir -p /dev /proc /sys
|
||||
[ -c /dev/console ] || mknod -m 600 /dev/console c 5 1
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sysfs /sys
|
||||
|
||||
# Networking
|
||||
echo slim > /proc/sys/kernel/hostname
|
||||
ip link set lo up 2>/dev/null
|
||||
ip link set eth0 up 2>/dev/null
|
||||
# udhcpc -i eth0 -f -q # Get DHCP lease
|
||||
|
||||
echo
|
||||
echo 'Welcome to slim!'
|
||||
echo
|
||||
|
||||
# Start systemd
|
||||
exec /sbin/init
|
||||
|
||||
# Or, just run a shell
|
||||
# /bin/sh </dev/console >/dev/console 2>&1
|
||||
# poweroff -f
|
||||
# while true; do
|
||||
# sleep 1
|
||||
# poweroff -f
|
||||
# done
|
||||
Executable
+177
@@ -0,0 +1,177 @@
|
||||
#!/bin/bash
|
||||
# Test script for slim - boots containers and verifies CMD inference/override.
|
||||
# Requires: podman, qemu-system-x86_64, KVM, curl, and a kernel at
|
||||
# $XDG_DATA_HOME/slim-rs/registry/vmlinuz.
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SLIM_BIN="$SCRIPT_DIR/../target/debug/slim"
|
||||
TIMEOUT=120
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
|
||||
report() {
|
||||
if [ "$1" = "pass" ]; then
|
||||
echo " PASS: $2"
|
||||
pass=$((pass + 1))
|
||||
else
|
||||
echo " FAIL: $2"
|
||||
fail=$((fail + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
check_output() {
|
||||
output="$1"
|
||||
marker="$2"
|
||||
label="$3"
|
||||
if echo "$output" | grep -q "$marker"; then
|
||||
report pass "$label"
|
||||
else
|
||||
report fail "$label (expected marker: $marker)"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
"$SLIM_BIN" image rm "$1" >/dev/null 2>&1 || true
|
||||
podman image rm "$2" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
test_distro() {
|
||||
distro="$1"
|
||||
from="$2"
|
||||
extra_setup="$3"
|
||||
|
||||
echo "=== Testing $distro ==="
|
||||
|
||||
work="$(mktemp -d)"
|
||||
|
||||
# === Test 1: CMD-inferred ===
|
||||
echo "-- Test 1: CMD-inferred"
|
||||
cat > "$work/Containerfile.infer" <<EOF
|
||||
FROM $from
|
||||
$extra_setup
|
||||
CMD ["/bin/sh", "-c", "echo CMD_INFERRED_OK; wget -q -O /dev/null http://1.1.1.1 2>/dev/null && echo CONN_OK; wget -q -O /dev/null http://example.org 2>/dev/null && echo DNS_OK; poweroff -f"]
|
||||
EOF
|
||||
img="slim-test-$distro-infer"
|
||||
if podman build -t "$img" -f "$work/Containerfile.infer" >/dev/null 2>&1; then
|
||||
"$SLIM_BIN" build qcow2 "$img" >/dev/null 2>&1
|
||||
output=$(timeout "$TIMEOUT" "$SLIM_BIN" run "$img" 2>&1 || true)
|
||||
check_output "$output" "CMD_INFERRED_OK" "$distro CMD-inferred"
|
||||
check_output "$output" "CONN_OK" "$distro connect to 1.1.1.1"
|
||||
check_output "$output" "DNS_OK" "$distro DNS lookup"
|
||||
else
|
||||
report fail "$distro CMD-inferred (podman build failed)"
|
||||
fi
|
||||
cleanup "$img" "$img"
|
||||
|
||||
# === Test 2: slim run --cmd override ===
|
||||
echo "-- Test 2: slim run --cmd override"
|
||||
cat > "$work/Containerfile.run" <<EOF
|
||||
FROM $from
|
||||
$extra_setup
|
||||
CMD ["/bin/sh", "-c", "echo SHOULD_NOT_APPEAR; poweroff -f"]
|
||||
EOF
|
||||
img="slim-test-$distro-run"
|
||||
if podman build -t "$img" -f "$work/Containerfile.run" >/dev/null 2>&1; then
|
||||
"$SLIM_BIN" build qcow2 "$img" >/dev/null 2>&1
|
||||
output=$(timeout "$TIMEOUT" "$SLIM_BIN" run "$img" --cmd 'echo RUN_OVERRIDE_OK; poweroff -f' 2>&1 || true)
|
||||
check_output "$output" "RUN_OVERRIDE_OK" "$distro run --cmd override"
|
||||
else
|
||||
report fail "$distro run --cmd override (podman build failed)"
|
||||
fi
|
||||
cleanup "$img" "$img"
|
||||
|
||||
# === Test 3: slim build --cmd override ===
|
||||
echo "-- Test 3: slim build --cmd override"
|
||||
cat > "$work/Containerfile.build" <<EOF
|
||||
FROM $from
|
||||
$extra_setup
|
||||
CMD ["/bin/sh", "-c", "echo SHOULD_NOT_APPEAR; poweroff -f"]
|
||||
EOF
|
||||
img="slim-test-$distro-build"
|
||||
if podman build -t "$img" -f "$work/Containerfile.build" >/dev/null 2>&1; then
|
||||
"$SLIM_BIN" build qcow2 "$img" --cmd 'echo BUILD_OVERRIDE_OK; poweroff -f' >/dev/null 2>&1
|
||||
output=$(timeout "$TIMEOUT" "$SLIM_BIN" run "$img" 2>&1 || true)
|
||||
check_output "$output" "BUILD_OVERRIDE_OK" "$distro build --cmd override"
|
||||
else
|
||||
report fail "$distro build --cmd override (podman build failed)"
|
||||
fi
|
||||
cleanup "$img" "$img"
|
||||
|
||||
rm -rf "$work"
|
||||
}
|
||||
|
||||
test_service() {
|
||||
echo "=== Testing nextcloud service ==="
|
||||
|
||||
img="slim-test-nextcloud"
|
||||
host_port=18080
|
||||
work="$(mktemp -d)"
|
||||
|
||||
cat > "$work/Containerfile" <<'EOF'
|
||||
FROM docker.io/library/nextcloud:32-apache
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends iproute2 && rm -rf /var/lib/apt/lists/*
|
||||
EOF
|
||||
|
||||
echo "-- Building nextcloud container image..."
|
||||
if ! podman build -t "$img" -f "$work/Containerfile" >/dev/null 2>&1; then
|
||||
report fail "nextcloud service (podman build failed)"
|
||||
rm -rf "$work"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "-- Building slim VM..."
|
||||
if ! "$SLIM_BIN" build qcow2 "$img" >/dev/null 2>&1; then
|
||||
report fail "nextcloud service (slim build failed)"
|
||||
cleanup "$img" "$img"
|
||||
rm -rf "$work"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "-- Booting VM with port forward (host :${host_port} -> guest :80)..."
|
||||
setsid timeout 300 "$SLIM_BIN" run "$img" \
|
||||
--forward "tcp:0.0.0.0:${host_port}-:80" \
|
||||
--memory 2048M >/dev/null 2>&1 &
|
||||
vm_pid=$!
|
||||
|
||||
echo "-- Waiting for Nextcloud to start..."
|
||||
up=0
|
||||
for _ in $(seq 1 60); do
|
||||
if curl -s -o /dev/null -m 2 "http://localhost:${host_port}/" 2>/dev/null; then
|
||||
up=1
|
||||
break
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
|
||||
if [ "$up" = "1" ]; then
|
||||
response=$(curl -s -L -m 10 "http://localhost:${host_port}/" 2>/dev/null || true)
|
||||
if echo "$response" | grep -qi "nextcloud"; then
|
||||
report pass "nextcloud service (port forward + content)"
|
||||
else
|
||||
report fail "nextcloud service (port reachable but no 'nextcloud' in response)"
|
||||
fi
|
||||
else
|
||||
report fail "nextcloud service (port not reachable within timeout)"
|
||||
fi
|
||||
|
||||
pkill -f "qemu-system-x86_64.*$img" 2>/dev/null || true
|
||||
kill "$vm_pid" 2>/dev/null || true
|
||||
wait "$vm_pid" 2>/dev/null || true
|
||||
|
||||
cleanup "$img" "$img"
|
||||
rm -rf "$work"
|
||||
}
|
||||
|
||||
echo "Building slim..."
|
||||
cargo build 2>&1
|
||||
|
||||
test_distro "alpine" "alpine:latest" ""
|
||||
test_distro "archlinux" "archlinux:latest" "RUN pacman -Sy --noconfirm iproute2 wget; pacman -Sc --noconfirm"
|
||||
|
||||
test_service
|
||||
|
||||
echo ""
|
||||
echo "=== Results: $pass passed, $fail failed ==="
|
||||
[ "$fail" -eq 0 ]
|
||||
Reference in New Issue
Block a user