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:
247
tests/hybrid/test_mode_toggle.sh
Executable file
247
tests/hybrid/test_mode_toggle.sh
Executable file
@@ -0,0 +1,247 @@
|
||||
#!/bin/bash
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
# Volt Hybrid Integration Tests — Mode Toggle (Container ↔ Hybrid-Native)
|
||||
#
|
||||
# Tests toggling a workload between container and hybrid-native mode:
|
||||
# 1. Create container workload
|
||||
# 2. Start and create a test file inside
|
||||
# 3. Toggle to hybrid-native mode
|
||||
# 4. Verify test file persists (filesystem state preserved)
|
||||
# 5. Verify now running with own kernel/init
|
||||
# 6. Toggle back to container mode
|
||||
# 7. Verify test file still exists
|
||||
# 8. Verify back to shared kernel behavior
|
||||
#
|
||||
# The toggle operation uses the workload abstraction layer. Currently a
|
||||
# placeholder (metadata-only), so we test the state transition and
|
||||
# filesystem preservation.
|
||||
#
|
||||
# Requires: root, systemd-nspawn, base image
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
set -uo pipefail
|
||||
source "$(dirname "$0")/test_helpers.sh"
|
||||
|
||||
# ── Prerequisites ─────────────────────────────────────────────────────────────
|
||||
|
||||
require_root
|
||||
require_volt
|
||||
require_nspawn
|
||||
|
||||
BASE_IMAGE="/var/lib/volt/images/ubuntu_24.04"
|
||||
if ! require_image "$BASE_IMAGE"; then
|
||||
echo "SKIP: No base image."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
trap cleanup_all EXIT
|
||||
|
||||
echo "⚡ Volt Hybrid Integration Tests — Mode Toggle"
|
||||
echo "════════════════════════════════════════════════════════════════"
|
||||
|
||||
TOGGLE_WL=$(test_name "toggle")
|
||||
|
||||
# ── 1. Create container workload ────────────────────────────────────────────
|
||||
|
||||
section "📦 1. Create Container Workload"
|
||||
|
||||
output=$(create_container "$TOGGLE_WL" "$BASE_IMAGE" 2>&1)
|
||||
assert_ok "Create container workload '$TOGGLE_WL'" test $? -eq 0
|
||||
assert_dir_exists "Rootfs exists" "/var/lib/volt/containers/$TOGGLE_WL"
|
||||
|
||||
# Register in workload state store as a container
|
||||
# The workload abstraction layer tracks type (container vs vm)
|
||||
sudo "$VOLT" workload list &>/dev/null || true # trigger discovery
|
||||
|
||||
# ── 2. Start and create a test file ─────────────────────────────────────────
|
||||
|
||||
section "🚀 2. Start and Create Test File"
|
||||
|
||||
output=$(start_workload "$TOGGLE_WL" 2>&1)
|
||||
assert_ok "Start workload" test $? -eq 0
|
||||
|
||||
if wait_running "$TOGGLE_WL" 30; then
|
||||
pass "Workload running"
|
||||
else
|
||||
fail "Workload running" "timed out"
|
||||
fi
|
||||
|
||||
LEADER_PID=$(get_leader_pid "$TOGGLE_WL")
|
||||
assert_nonempty "Leader PID available" "$LEADER_PID"
|
||||
|
||||
# Create a test file with unique content
|
||||
TEST_MARKER="volt-toggle-test-$(date +%s)-$$"
|
||||
exec_in "$TOGGLE_WL" sh -c "echo '$TEST_MARKER' > /tmp/toggle-test-file" 2>/dev/null || \
|
||||
sudo nsenter -t "$LEADER_PID" -p -m sh -c "echo '$TEST_MARKER' > /tmp/toggle-test-file" 2>/dev/null
|
||||
|
||||
# Verify the file was created
|
||||
if exec_in "$TOGGLE_WL" cat /tmp/toggle-test-file 2>/dev/null | grep -q "$TEST_MARKER"; then
|
||||
pass "Test file created inside workload"
|
||||
elif sudo nsenter -t "$LEADER_PID" -m cat /tmp/toggle-test-file 2>/dev/null | grep -q "$TEST_MARKER"; then
|
||||
pass "Test file created inside workload (via nsenter)"
|
||||
else
|
||||
fail "Test file created inside workload" "marker not found"
|
||||
fi
|
||||
|
||||
# Also create a file directly on the rootfs (this will definitely persist)
|
||||
sudo sh -c "echo '$TEST_MARKER' > /var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-rootfs-file"
|
||||
assert_file_exists "Rootfs test file created" "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-rootfs-file"
|
||||
|
||||
# Record the kernel version seen from inside (shared host kernel for containers)
|
||||
KERNEL_BEFORE=$(exec_in "$TOGGLE_WL" uname -r 2>/dev/null || \
|
||||
sudo nsenter -t "$LEADER_PID" -m -u uname -r 2>/dev/null || echo "unknown")
|
||||
HOST_KERNEL=$(uname -r)
|
||||
pass "Kernel before toggle: $KERNEL_BEFORE (host: $HOST_KERNEL)"
|
||||
|
||||
# ── 3. Toggle to hybrid-native mode ────────────────────────────────────────
|
||||
|
||||
section "🔄 3. Toggle to Hybrid-Native Mode"
|
||||
|
||||
# Stop the workload first (toggle currently requires stop → reconfigure → start)
|
||||
stop_workload "$TOGGLE_WL" &>/dev/null
|
||||
|
||||
# Use the workload toggle command
|
||||
toggle_output=$(sudo "$VOLT" workload toggle "$TOGGLE_WL" 2>&1) || true
|
||||
if echo "$toggle_output" | grep -qi "toggle\|vm\|hybrid"; then
|
||||
pass "Toggle command executed (output mentions toggle/vm/hybrid)"
|
||||
else
|
||||
# If workload toggle doesn't exist yet, simulate by checking what we can
|
||||
skip "Toggle command" "workload toggle may not be fully implemented"
|
||||
fi
|
||||
|
||||
# Check the workload state after toggle
|
||||
wl_status=$(sudo "$VOLT" workload status "$TOGGLE_WL" 2>&1) || true
|
||||
if echo "$wl_status" | grep -qi "vm\|hybrid"; then
|
||||
pass "Workload type changed after toggle"
|
||||
else
|
||||
skip "Workload type changed" "toggle may only update metadata"
|
||||
fi
|
||||
|
||||
# ── 4. Verify filesystem state preserved ────────────────────────────────────
|
||||
|
||||
section "📂 4. Verify Filesystem State Preserved"
|
||||
|
||||
# The rootfs file we created directly should still be there
|
||||
if [[ -f "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-rootfs-file" ]]; then
|
||||
content=$(cat "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-rootfs-file" 2>/dev/null)
|
||||
if [[ "$content" == "$TEST_MARKER" ]]; then
|
||||
pass "Rootfs test file preserved with correct content"
|
||||
else
|
||||
fail "Rootfs test file preserved" "content mismatch: expected '$TEST_MARKER', got '$content'"
|
||||
fi
|
||||
else
|
||||
fail "Rootfs test file preserved" "file not found after toggle"
|
||||
fi
|
||||
|
||||
# Check the in-container test file (was written to container's /tmp)
|
||||
if [[ -f "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-test-file" ]]; then
|
||||
content=$(cat "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-test-file" 2>/dev/null)
|
||||
if [[ "$content" == "$TEST_MARKER" ]]; then
|
||||
pass "In-container test file preserved with correct content"
|
||||
else
|
||||
fail "In-container test file preserved" "content mismatch"
|
||||
fi
|
||||
else
|
||||
skip "In-container test file preserved" "may have been on tmpfs (ephemeral)"
|
||||
fi
|
||||
|
||||
# ── 5. Verify hybrid-native mode properties ────────────────────────────────
|
||||
|
||||
section "🔒 5. Verify Hybrid-Native Mode (post-toggle)"
|
||||
|
||||
# Start the workload in its new mode
|
||||
start_output=$(start_workload "$TOGGLE_WL" 2>&1) || true
|
||||
|
||||
if wait_running "$TOGGLE_WL" 30; then
|
||||
pass "Workload starts after toggle"
|
||||
|
||||
NEW_LEADER_PID=$(get_leader_pid "$TOGGLE_WL")
|
||||
if [[ -n "$NEW_LEADER_PID" && "$NEW_LEADER_PID" != "0" ]]; then
|
||||
pass "New leader PID: $NEW_LEADER_PID"
|
||||
|
||||
# If we're truly in hybrid/boot mode, PID 1 inside should be init/systemd
|
||||
pid1_comm=$(sudo nsenter -t "$NEW_LEADER_PID" -p -m cat /proc/1/comm 2>/dev/null || echo "")
|
||||
if echo "$pid1_comm" | grep -qE "systemd|init"; then
|
||||
pass "PID 1 inside is systemd/init (hybrid mode confirmed)"
|
||||
else
|
||||
skip "PID 1 check after toggle" "PID 1 is: $pid1_comm (may not be in true hybrid mode)"
|
||||
fi
|
||||
|
||||
# Check kernel version — in hybrid mode with custom kernel it could differ
|
||||
KERNEL_AFTER=$(sudo nsenter -t "$NEW_LEADER_PID" -m -u uname -r 2>/dev/null || echo "unknown")
|
||||
pass "Kernel after toggle: $KERNEL_AFTER"
|
||||
else
|
||||
skip "Post-toggle leader PID" "PID not available"
|
||||
fi
|
||||
|
||||
# Stop for the next toggle
|
||||
stop_workload "$TOGGLE_WL" &>/dev/null
|
||||
else
|
||||
skip "Post-toggle start" "workload failed to start after toggle"
|
||||
fi
|
||||
|
||||
# ── 6. Toggle back to container mode ────────────────────────────────────────
|
||||
|
||||
section "🔄 6. Toggle Back to Container Mode"
|
||||
|
||||
toggle_back_output=$(sudo "$VOLT" workload toggle "$TOGGLE_WL" 2>&1) || true
|
||||
if echo "$toggle_back_output" | grep -qi "toggle\|container"; then
|
||||
pass "Toggle-back command executed"
|
||||
else
|
||||
skip "Toggle-back command" "may not be implemented"
|
||||
fi
|
||||
|
||||
# Check workload type reverted
|
||||
wl_status2=$(sudo "$VOLT" workload status "$TOGGLE_WL" 2>&1) || true
|
||||
if echo "$wl_status2" | grep -qi "container"; then
|
||||
pass "Workload type reverted to container"
|
||||
else
|
||||
skip "Workload type reverted" "status check inconclusive"
|
||||
fi
|
||||
|
||||
# ── 7. Verify test file still exists ────────────────────────────────────────
|
||||
|
||||
section "📂 7. Verify Test File After Round-Trip Toggle"
|
||||
|
||||
if [[ -f "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-rootfs-file" ]]; then
|
||||
content=$(cat "/var/lib/volt/containers/$TOGGLE_WL/tmp/toggle-rootfs-file" 2>/dev/null)
|
||||
assert_eq "Test file survives round-trip toggle" "$TEST_MARKER" "$content"
|
||||
else
|
||||
fail "Test file survives round-trip toggle" "file not found"
|
||||
fi
|
||||
|
||||
# ── 8. Verify back to shared kernel ────────────────────────────────────────
|
||||
|
||||
section "🔧 8. Verify Container Mode (shared kernel)"
|
||||
|
||||
start_workload "$TOGGLE_WL" &>/dev/null || true
|
||||
|
||||
if wait_running "$TOGGLE_WL" 30; then
|
||||
FINAL_LEADER=$(get_leader_pid "$TOGGLE_WL")
|
||||
if [[ -n "$FINAL_LEADER" && "$FINAL_LEADER" != "0" ]]; then
|
||||
KERNEL_FINAL=$(sudo nsenter -t "$FINAL_LEADER" -m -u uname -r 2>/dev/null || echo "unknown")
|
||||
if [[ "$KERNEL_FINAL" == "$HOST_KERNEL" ]]; then
|
||||
pass "Kernel matches host after toggle back ($KERNEL_FINAL)"
|
||||
else
|
||||
# In boot mode the kernel is always shared (nspawn doesn't boot a real kernel)
|
||||
# so this should always match unless a custom kernel-exec is used
|
||||
skip "Kernel match check" "kernel=$KERNEL_FINAL, host=$HOST_KERNEL"
|
||||
fi
|
||||
else
|
||||
skip "Post-toggle-back kernel check" "no leader PID"
|
||||
fi
|
||||
|
||||
stop_workload "$TOGGLE_WL" &>/dev/null
|
||||
else
|
||||
skip "Post-toggle-back start" "workload failed to start"
|
||||
fi
|
||||
|
||||
# ── Cleanup ──────────────────────────────────────────────────────────────────
|
||||
|
||||
destroy_workload "$TOGGLE_WL"
|
||||
CLEANUP_WORKLOADS=("${CLEANUP_WORKLOADS[@]/$TOGGLE_WL/}")
|
||||
|
||||
# ── Results ──────────────────────────────────────────────────────────────────
|
||||
|
||||
print_results "Mode Toggle (Container ↔ Hybrid-Native)"
|
||||
exit $?
|
||||
Reference in New Issue
Block a user