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:
422
scripts/build-images.sh
Executable file
422
scripts/build-images.sh
Executable 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
169
scripts/build-kernels.sh
Executable 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
251
scripts/install.sh
Executable 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
122
scripts/test-integration.sh
Executable 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}"
|
||||
Reference in New Issue
Block a user