Volt CLI: source-available under AGPSL v5.0

Complete infrastructure platform CLI:
- Container runtime (systemd-nspawn)
- VoltVisor VMs (Neutron Stardust / QEMU)
- Stellarium CAS (content-addressed storage)
- ORAS Registry
- GitOps integration
- Landlock LSM security
- Compose orchestration
- Mesh networking

Copyright (c) Armored Gates LLC. All rights reserved.
Licensed under AGPSL v5.0
This commit is contained in:
Karl Clinger
2026-03-21 00:30:23 -05:00
commit 0ebe75b2ca
155 changed files with 63317 additions and 0 deletions

422
scripts/build-images.sh Executable file
View File

@@ -0,0 +1,422 @@
#!/bin/bash
#
# Volt Platform - Image Builder
# Creates TinyVol images from definitions
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_DIR="$PROJECT_DIR/configs/images"
OUTPUT_DIR="${OUTPUT_DIR:-/var/lib/volt/images}"
CACHE_DIR="${CACHE_DIR:-/var/cache/volt/packages}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${GREEN}[volt]${NC} $1"; }
info() { echo -e "${BLUE}[volt]${NC} $1"; }
warn() { echo -e "${YELLOW}[volt]${NC} $1"; }
error() { echo -e "${RED}[volt]${NC} $1" >&2; }
# Base packages for each userland type
declare -A USERLAND_PACKAGES=(
["musl-minimal"]="musl busybox"
["glibc-standard"]="glibc bash coreutils util-linux systemd"
["busybox-tiny"]="busybox-static"
)
# Image definitions
declare -A IMAGES=(
["volt/server"]="server.yaml"
["volt/server-db-postgres"]="server-db-postgres.yaml"
["volt/dev"]="dev.yaml"
["volt/desktop-minimal"]="desktop-minimal.yaml"
["volt/desktop-productivity"]="desktop-productivity.yaml"
["volt/edge"]="edge.yaml"
["volt/k8s-node"]="k8s-node.yaml"
)
build_rootfs() {
local image_name="$1"
local config_file="$2"
local rootfs_dir="$3"
log "Building rootfs for: $image_name"
# Create directory structure
mkdir -p "$rootfs_dir"/{bin,sbin,usr/{bin,sbin,lib},lib,lib64,etc,var,tmp,proc,sys,dev,run,home,root,app}
# Parse YAML config (simplified - in production use proper YAML parser)
local userland=$(grep "userland:" "$config_file" 2>/dev/null | awk '{print $2}' || echo "musl-minimal")
info " Userland: $userland"
# Install base userland
case "$userland" in
musl-minimal)
install_musl_minimal "$rootfs_dir"
;;
glibc-standard)
install_glibc_standard "$rootfs_dir"
;;
busybox-tiny)
install_busybox_tiny "$rootfs_dir"
;;
*)
warn "Unknown userland: $userland, using musl-minimal"
install_musl_minimal "$rootfs_dir"
;;
esac
# Create essential files
create_essential_files "$rootfs_dir"
# Set permissions
chmod 1777 "$rootfs_dir/tmp"
chmod 755 "$rootfs_dir"
}
install_musl_minimal() {
local rootfs="$1"
info " Installing musl-minimal userland..."
# Download and install BusyBox static binary
local busybox_url="https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox"
local busybox_path="$rootfs/bin/busybox"
if [[ ! -f "$CACHE_DIR/busybox" ]]; then
mkdir -p "$CACHE_DIR"
curl -fSL -o "$CACHE_DIR/busybox" "$busybox_url" || {
# Fallback: create minimal shell script
warn "Could not download busybox, creating minimal placeholder"
cat > "$busybox_path" << 'BUSYBOX'
#!/bin/sh
echo "Volt minimal shell"
exec /bin/sh "$@"
BUSYBOX
chmod +x "$busybox_path"
return 0
}
fi
cp "$CACHE_DIR/busybox" "$busybox_path"
chmod +x "$busybox_path"
# Create symlinks for common utilities
local utils="sh ash ls cat cp mv rm mkdir rmdir ln echo pwd env grep sed awk head tail sort uniq wc cut tr sleep date hostname uname id whoami ps kill"
for util in $utils; do
ln -sf busybox "$rootfs/bin/$util"
done
# Create sbin links
local sbin_utils="init halt reboot poweroff mount umount ifconfig route"
for util in $sbin_utils; do
ln -sf ../bin/busybox "$rootfs/sbin/$util"
done
}
install_glibc_standard() {
local rootfs="$1"
info " Installing glibc-standard userland..."
# For now, use Alpine as a base (it's actually musl but good enough for development)
# In production, this would pull from ArmoredGateHub registry
# Create minimal glibc-like structure
install_musl_minimal "$rootfs"
# Add bash if available
if command -v bash &>/dev/null; then
cp "$(command -v bash)" "$rootfs/bin/bash" 2>/dev/null || true
fi
# Copy essential libraries from host (for development only)
# In production, these come from TinyVol images
for lib in /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libm.so.6 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libpthread.so.0; do
if [[ -f "$lib" ]]; then
mkdir -p "$rootfs/lib/x86_64-linux-gnu"
cp "$lib" "$rootfs/lib/x86_64-linux-gnu/" 2>/dev/null || true
fi
done
# Copy ld-linux
if [[ -f /lib64/ld-linux-x86-64.so.2 ]]; then
mkdir -p "$rootfs/lib64"
cp /lib64/ld-linux-x86-64.so.2 "$rootfs/lib64/" 2>/dev/null || true
fi
}
install_busybox_tiny() {
local rootfs="$1"
info " Installing busybox-tiny userland..."
# Absolute minimal - just busybox
install_musl_minimal "$rootfs"
# Remove non-essential symlinks
rm -f "$rootfs/bin/awk" "$rootfs/bin/sed" "$rootfs/bin/grep"
}
create_essential_files() {
local rootfs="$1"
# /etc/passwd
cat > "$rootfs/etc/passwd" << 'EOF'
root:x:0:0:root:/root:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
volt:x:1000:1000:Volt User:/home/volt:/bin/sh
EOF
# /etc/group
cat > "$rootfs/etc/group" << 'EOF'
root:x:0:
nobody:x:65534:
volt:x:1000:
EOF
# /etc/shadow (empty passwords - VMs use keys)
cat > "$rootfs/etc/shadow" << 'EOF'
root:*:19000:0:99999:7:::
nobody:*:19000:0:99999:7:::
volt:*:19000:0:99999:7:::
EOF
chmod 640 "$rootfs/etc/shadow"
# /etc/hosts
cat > "$rootfs/etc/hosts" << 'EOF'
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
EOF
# /etc/hostname
echo "volt" > "$rootfs/etc/hostname"
# /etc/resolv.conf
cat > "$rootfs/etc/resolv.conf" << 'EOF'
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF
# /etc/nsswitch.conf
cat > "$rootfs/etc/nsswitch.conf" << 'EOF'
passwd: files
group: files
shadow: files
hosts: files dns
networks: files
protocols: files
services: files
EOF
# /etc/os-release
cat > "$rootfs/etc/os-release" << 'EOF'
NAME="Volt Platform"
VERSION="1.0"
ID=volt
ID_LIKE=alpine
VERSION_ID=1.0
PRETTY_NAME="Volt Platform VM"
HOME_URL="https://voltvisor.io"
EOF
# Init script
cat > "$rootfs/sbin/init" << 'INIT'
#!/bin/sh
# Volt Init
# Mount essential filesystems
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts /dev/shm
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm
mount -t tmpfs tmpfs /tmp
mount -t tmpfs tmpfs /run
# Set hostname
hostname -F /etc/hostname
# Network (if configured)
if [ -f /etc/network/interfaces ]; then
ifconfig lo up
fi
# Run init scripts
for script in /etc/init.d/S*; do
[ -x "$script" ] && "$script" start
done
# Start shell or configured service
if [ -f /etc/volt/service ]; then
exec $(cat /etc/volt/service)
else
exec /bin/sh
fi
INIT
chmod +x "$rootfs/sbin/init"
# Create init.d directory
mkdir -p "$rootfs/etc/init.d"
mkdir -p "$rootfs/etc/volt"
}
create_tinyvol() {
local image_name="$1"
local rootfs_dir="$2"
local output_path="$3"
log "Creating TinyVol: $output_path"
# Create squashfs image (TinyVol format)
# In production, this would use the actual TinyVol format
if command -v mksquashfs &>/dev/null; then
mksquashfs "$rootfs_dir" "$output_path" \
-comp zstd \
-Xcompression-level 19 \
-all-root \
-noappend \
-no-progress
else
# Fallback: create tar archive
warn "mksquashfs not found, creating tar archive"
tar -czf "$output_path" -C "$rootfs_dir" .
fi
local size=$(du -h "$output_path" | cut -f1)
info " Image size: $size"
}
generate_sbom() {
local image_name="$1"
local rootfs_dir="$2"
local output_path="$3"
log "Generating SBOM for: $image_name"
cat > "$output_path" << EOF
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "$(date -Iseconds)",
"component": {
"type": "operating-system",
"name": "$image_name",
"version": "1.0"
}
},
"components": [
{
"type": "application",
"name": "busybox",
"version": "1.35.0"
}
]
}
EOF
}
sign_image() {
local image_name="$1"
local image_path="$2"
log "Signing image: $image_name"
# Generate checksums
sha256sum "$image_path" > "${image_path}.sha256"
# TODO: Integrate with ArmoredForge signing
# armored-forge sign "$image_path" --key volt-image-key
}
build_image() {
local image_name="$1"
local config_file="$CONFIG_DIR/$2"
log "=========================================="
log "Building image: $image_name"
log "=========================================="
if [[ ! -f "$config_file" ]]; then
warn "Config file not found: $config_file"
# Create default config
config_file="$CONFIG_DIR/server.yaml"
fi
local safe_name=$(echo "$image_name" | tr '/' '_')
local work_dir="$OUTPUT_DIR/.build/$safe_name"
local rootfs_dir="$work_dir/rootfs"
local image_path="$OUTPUT_DIR/$safe_name.tinyvol"
# Clean and create work directory
rm -rf "$work_dir"
mkdir -p "$work_dir" "$rootfs_dir"
# Build rootfs
build_rootfs "$image_name" "$config_file" "$rootfs_dir"
# Create TinyVol image
create_tinyvol "$image_name" "$rootfs_dir" "$image_path"
# Generate SBOM
generate_sbom "$image_name" "$rootfs_dir" "${image_path}.sbom.json"
# Sign image
sign_image "$image_name" "$image_path"
# Create image metadata
cat > "${image_path}.json" << EOF
{
"name": "$image_name",
"version": "1.0",
"created": "$(date -Iseconds)",
"size": "$(du -h "$image_path" | cut -f1)",
"sha256": "$(sha256sum "$image_path" | cut -d' ' -f1)",
"sbom": "${image_path}.sbom.json"
}
EOF
# Cleanup work directory
rm -rf "$work_dir"
log "Image built: $image_path"
}
main() {
log "Volt Platform Image Builder"
log "=============================="
mkdir -p "$OUTPUT_DIR" "$CACHE_DIR"
# Build all defined images
for image_name in "${!IMAGES[@]}"; do
build_image "$image_name" "${IMAGES[$image_name]}"
done
# If no images defined, build defaults
if [[ ${#IMAGES[@]} -eq 0 ]]; then
build_image "volt/server" "server.yaml"
build_image "volt/desktop-productivity" "desktop-productivity.yaml"
fi
log ""
log "Build complete!"
log "Images installed to: $OUTPUT_DIR"
ls -la "$OUTPUT_DIR"/*.tinyvol 2>/dev/null || ls -la "$OUTPUT_DIR"
}
# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

169
scripts/build-kernels.sh Executable file
View File

@@ -0,0 +1,169 @@
#!/bin/bash
#
# Volt Platform - Kernel Build Script
# Builds all kernel profiles from configs
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG_DIR="$PROJECT_DIR/configs/kernels"
OUTPUT_DIR="${OUTPUT_DIR:-/var/lib/volt/kernels}"
KERNEL_VERSION="${KERNEL_VERSION:-6.6.15}"
KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz"
BUILD_DIR="/tmp/volt-kernel-build"
JOBS="${JOBS:-$(nproc)}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[volt]${NC} $1"; }
warn() { echo -e "${YELLOW}[volt]${NC} $1"; }
error() { echo -e "${RED}[volt]${NC} $1" >&2; }
# Kernel profiles to build
PROFILES=(
"server"
"desktop"
"minimal"
"rt"
"dev"
)
download_kernel() {
log "Downloading Linux kernel ${KERNEL_VERSION}..."
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
if [[ ! -f "linux-${KERNEL_VERSION}.tar.xz" ]]; then
curl -fSL -o "linux-${KERNEL_VERSION}.tar.xz" "$KERNEL_URL"
fi
if [[ ! -d "linux-${KERNEL_VERSION}" ]]; then
log "Extracting kernel source..."
tar xf "linux-${KERNEL_VERSION}.tar.xz"
fi
}
build_kernel() {
local profile="$1"
local config_file="$CONFIG_DIR/kernel-${profile}.config"
local output_name="kernel-${profile}"
log "Building kernel profile: ${profile}"
if [[ ! -f "$config_file" ]]; then
warn "Config file not found: $config_file, skipping"
return 0
fi
cd "$BUILD_DIR/linux-${KERNEL_VERSION}"
# Clean previous build
make mrproper
# Copy config
cp "$config_file" .config
# Update config with defaults
make olddefconfig
# Build kernel
log "Compiling kernel (this may take a while)..."
make -j"$JOBS" bzImage
# Build modules (if enabled)
if grep -q "CONFIG_MODULES=y" .config; then
make -j"$JOBS" modules
fi
# Install to output directory
mkdir -p "$OUTPUT_DIR/$output_name"
# Copy kernel image
cp arch/x86/boot/bzImage "$OUTPUT_DIR/$output_name/vmlinuz"
# Copy modules if built
if grep -q "CONFIG_MODULES=y" .config; then
make INSTALL_MOD_PATH="$OUTPUT_DIR/$output_name" modules_install
fi
# Copy config for reference
cp .config "$OUTPUT_DIR/$output_name/config"
# Generate kernel info
local size=$(du -h "$OUTPUT_DIR/$output_name/vmlinuz" | cut -f1)
cat > "$OUTPUT_DIR/$output_name/info.json" << EOF
{
"profile": "${profile}",
"version": "${KERNEL_VERSION}",
"localversion": "-volt-${profile}",
"size": "${size}",
"built": "$(date -Iseconds)",
"config_hash": "$(sha256sum "$config_file" | cut -d' ' -f1)"
}
EOF
log "Kernel ${profile} built: ${size}"
}
sign_kernel() {
local profile="$1"
local kernel_path="$OUTPUT_DIR/kernel-${profile}/vmlinuz"
log "Signing kernel: ${profile}"
# In production, this would use proper key management
# For now, generate signature placeholder
sha256sum "$kernel_path" > "$OUTPUT_DIR/kernel-${profile}/vmlinuz.sha256"
# TODO: Integrate with ArmoredForge signing
# armored-forge sign "$kernel_path" --key volt-kernel-key
}
main() {
log "Volt Platform Kernel Builder"
log "================================"
log "Kernel version: ${KERNEL_VERSION}"
log "Output directory: ${OUTPUT_DIR}"
log "Build jobs: ${JOBS}"
echo ""
# Check dependencies
for cmd in make gcc curl tar; do
if ! command -v "$cmd" &>/dev/null; then
error "Required command not found: $cmd"
exit 1
fi
done
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Download kernel source
download_kernel
# Build each profile
for profile in "${PROFILES[@]}"; do
if [[ -f "$CONFIG_DIR/kernel-${profile}.config" ]]; then
build_kernel "$profile"
sign_kernel "$profile"
else
warn "Skipping ${profile} (no config file)"
fi
done
log ""
log "Build complete!"
log "Kernels installed to: $OUTPUT_DIR"
ls -la "$OUTPUT_DIR"
}
# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

251
scripts/install.sh Executable file
View File

@@ -0,0 +1,251 @@
#!/bin/bash
#
# Volt Platform - Installation Script
#
set -euo pipefail
# Configuration
INSTALL_DIR="${INSTALL_DIR:-/usr/local}"
CONFIG_DIR="${CONFIG_DIR:-/etc/volt}"
DATA_DIR="${DATA_DIR:-/var/lib/volt}"
RUN_DIR="${RUN_DIR:-/var/run/volt}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${GREEN}[volt]${NC} $1"; }
info() { echo -e "${BLUE}[volt]${NC} $1"; }
warn() { echo -e "${YELLOW}[volt]${NC} $1"; }
error() { echo -e "${RED}[volt]${NC} $1" >&2; }
check_root() {
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root"
exit 1
fi
}
check_dependencies() {
log "Checking dependencies..."
local missing=()
# Required commands
for cmd in ip iptables mount; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
# Kernel features
if [[ ! -d /sys/fs/cgroup/unified ]] && [[ ! -d /sys/fs/cgroup/memory ]]; then
warn "Cgroups v2 recommended but not detected"
fi
# Landlock support
if [[ ! -f /sys/kernel/security/landlock/abi_version ]]; then
warn "Landlock not available (kernel >= 5.13 required for full functionality)"
fi
if [[ ${#missing[@]} -gt 0 ]]; then
error "Missing required commands: ${missing[*]}"
exit 1
fi
log "Dependencies OK"
}
create_directories() {
log "Creating directories..."
mkdir -p "$INSTALL_DIR/bin"
mkdir -p "$CONFIG_DIR"
mkdir -p "$DATA_DIR"/{vms,kernels,images,storage}
mkdir -p "$RUN_DIR"
# Set permissions
chmod 755 "$CONFIG_DIR"
chmod 755 "$DATA_DIR"
chmod 755 "$RUN_DIR"
}
install_binaries() {
log "Installing binaries..."
# Build if source available
if [[ -f "go.mod" ]]; then
info "Building from source..."
go build -o "$INSTALL_DIR/bin/volt" ./cmd/volt
else
# Download pre-built binary
local arch=$(uname -m)
case "$arch" in
x86_64) arch="amd64" ;;
aarch64) arch="arm64" ;;
esac
info "Downloading pre-built binary..."
curl -fsSL "https://get.voltvisor.io/volt-linux-${arch}" -o "$INSTALL_DIR/bin/volt"
fi
chmod +x "$INSTALL_DIR/bin/volt"
# Create volt-runtime symlink
ln -sf "$INSTALL_DIR/bin/volt" "$INSTALL_DIR/bin/volt-runtime"
}
install_configs() {
log "Installing configurations..."
# Copy kernel configs
if [[ -d "configs/kernels" ]]; then
cp -r configs/kernels "$CONFIG_DIR/"
fi
# Copy image definitions
if [[ -d "configs/images" ]]; then
cp -r configs/images "$CONFIG_DIR/"
fi
# Copy seccomp profiles
if [[ -d "configs/seccomp" ]]; then
cp -r configs/seccomp "$CONFIG_DIR/"
fi
# Copy systemd units
if [[ -d "configs/systemd" ]]; then
cp configs/systemd/*.service /etc/systemd/system/ 2>/dev/null || true
fi
# Main config file
if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then
cat > "$CONFIG_DIR/config.yaml" << 'EOF'
# Volt Platform Configuration
# Directories
data_dir: /var/lib/volt
run_dir: /var/run/volt
# Networking
network:
bridge: volt0
subnet: 10.100.0.0/16
enable_nat: true
# Defaults
defaults:
kernel: kernel-server
memory: 256M
cpus: 1
# Security
security:
verify_signatures: true
require_sbom: true
block_cve_severity: high
# Logging
logging:
level: info
format: json
EOF
fi
}
setup_networking() {
log "Setting up networking..."
# Create bridge if it doesn't exist
if ! ip link show volt0 &>/dev/null; then
ip link add volt0 type bridge
ip addr add 10.100.0.1/16 dev volt0
ip link set volt0 up
fi
# Enable IP forwarding
sysctl -w net.ipv4.ip_forward=1 > /dev/null
# Setup NAT
iptables -t nat -C POSTROUTING -s 10.100.0.0/16 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 10.100.0.0/16 -j MASQUERADE
# Allow forwarding
iptables -C FORWARD -i volt0 -j ACCEPT 2>/dev/null || \
iptables -A FORWARD -i volt0 -j ACCEPT
iptables -C FORWARD -o volt0 -j ACCEPT 2>/dev/null || \
iptables -A FORWARD -o volt0 -j ACCEPT
}
setup_systemd() {
log "Setting up systemd services..."
# Main service
cat > /etc/systemd/system/volt.service << 'EOF'
[Unit]
Description=Volt Platform Runtime
After=network.target
Wants=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/volt daemon
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd
systemctl daemon-reload
}
print_summary() {
echo ""
log "================================================"
log "Volt Platform installed successfully!"
log "================================================"
echo ""
info "Binary: $INSTALL_DIR/bin/volt"
info "Config: $CONFIG_DIR/config.yaml"
info "Data: $DATA_DIR"
echo ""
info "Quick start:"
echo " volt vm create my-server --image volt/server"
echo " volt vm start my-server"
echo " volt vm ssh my-server"
echo ""
info "Desktop VM:"
echo " volt desktop create my-desktop --image volt/desktop-productivity"
echo " volt desktop connect my-desktop"
echo ""
info "Kubernetes nodes:"
echo " volt k8s node add --count 100"
echo ""
}
main() {
echo ""
log "Volt Platform Installer"
log "=========================="
echo ""
check_root
check_dependencies
create_directories
install_binaries
install_configs
setup_networking
setup_systemd
print_summary
}
# Run if executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

122
scripts/test-integration.sh Executable file
View File

@@ -0,0 +1,122 @@
#!/bin/bash
#
# Volt Platform - Integration Tests
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
VOLT="$PROJECT_DIR/build/volt"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}${NC} $1"; }
fail() { echo -e "${RED}${NC} $1"; exit 1; }
skip() { echo -e "${YELLOW}${NC} $1 (skipped)"; }
# Test counter
TESTS_PASSED=0
TESTS_FAILED=0
run_test() {
local name="$1"
shift
if "$@" >/dev/null 2>&1; then
pass "$name"
((TESTS_PASSED++))
else
fail "$name"
((TESTS_FAILED++))
fi
}
# Check if binary exists
if [[ ! -x "$VOLT" ]]; then
echo "Building volt first..."
cd "$PROJECT_DIR"
make build
fi
echo "================================"
echo "Volt Platform Integration Tests"
echo "================================"
echo ""
# Test 1: Binary runs
echo "=== CLI Tests ==="
run_test "volt --help" $VOLT --help
run_test "volt vm --help" $VOLT vm --help
run_test "volt desktop --help" $VOLT desktop --help
run_test "volt k8s --help" $VOLT k8s --help
# Test 2: VM commands (need sudo for full test)
echo ""
echo "=== VM Tests (sudo required) ==="
if [[ $EUID -ne 0 ]]; then
skip "VM creation (requires root)"
skip "VM listing (requires root)"
skip "VM destruction (requires root)"
else
# Create test VM
TEST_VM="volt-test-$$"
run_test "Create VM" $VOLT vm create "$TEST_VM" --image volt/server
run_test "List VMs" $VOLT vm list
run_test "VM directory exists" test -d "/var/lib/volt/vms/$TEST_VM"
run_test "SystemD unit created" test -f "/etc/systemd/system/volt-vm@${TEST_VM}.service"
# Cleanup
run_test "Destroy VM" $VOLT vm destroy "$TEST_VM"
run_test "VM directory removed" test ! -d "/var/lib/volt/vms/$TEST_VM"
fi
# Test 3: Desktop commands
echo ""
echo "=== Desktop Tests ==="
run_test "ODE profiles defined" grep -q "office" "$PROJECT_DIR/pkg/ode/ode.go"
run_test "Desktop command exists" $VOLT desktop --help
# Test 4: K8s commands
echo ""
echo "=== Kubernetes Tests ==="
run_test "K8s node --help" $VOLT k8s node --help
run_test "K8s status" $VOLT k8s status
# Test 5: Configuration files
echo ""
echo "=== Config Tests ==="
run_test "Server image config" test -f "$PROJECT_DIR/configs/images/server.yaml"
run_test "Desktop image config" test -f "$PROJECT_DIR/configs/images/desktop-productivity.yaml"
run_test "Edge image config" test -f "$PROJECT_DIR/configs/images/edge.yaml"
run_test "K8s node config" test -f "$PROJECT_DIR/configs/images/k8s-node.yaml"
run_test "Server kernel config" test -f "$PROJECT_DIR/configs/kernels/kernel-server.config"
run_test "Desktop kernel config" test -f "$PROJECT_DIR/configs/kernels/kernel-desktop.config"
run_test "Seccomp profile" test -f "$PROJECT_DIR/configs/seccomp/server.json"
# Test 6: Build scripts
echo ""
echo "=== Build Script Tests ==="
run_test "build-kernels.sh exists" test -x "$PROJECT_DIR/scripts/build-kernels.sh"
run_test "build-images.sh exists" test -x "$PROJECT_DIR/scripts/build-images.sh"
run_test "install.sh exists" test -x "$PROJECT_DIR/scripts/install.sh"
# Summary
echo ""
echo "================================"
echo "Test Summary"
echo "================================"
echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
echo ""
if [[ $TESTS_FAILED -gt 0 ]]; then
exit 1
fi
echo -e "${GREEN}All tests passed!${NC}"