Add cluster management script for 3 server nodes and 1 HTTP proxy

Provides start/stop/status/restart/clean commands to manage a local
ToastyFS cluster. Nodes listen on 127.0.0.1:8001-8003 with the HTTP
proxy on 127.0.0.1:3000. Each node gets its own chunk storage directory
under cluster-data/. Logs and PID files are stored there as well.

https://claude.ai/code/session_01FAL4T35UGYqXexbaxvihRp
This commit is contained in:
Claude
2026-02-26 01:19:36 +01:00
committed by cozis
parent 02da6b0170
commit 7cd124e939
2 changed files with 179 additions and 1 deletions
+1
View File
@@ -23,3 +23,4 @@ vsr_boot_marker
*.pem
*.exe
.claude/
cluster-data/
Executable
+177
View File
@@ -0,0 +1,177 @@
#!/bin/bash
#
# Cluster management script for ToastyFS.
# Manages a 3-node server cluster with 1 HTTP proxy.
#
# Usage:
# ./cluster.sh start - Build (if needed) and start all nodes + proxy
# ./cluster.sh stop - Stop all running nodes and the proxy
# ./cluster.sh status - Show the status of each process
# ./cluster.sh restart - Stop then start
# ./cluster.sh clean - Stop everything and remove data/log directories
#
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DATA_DIR="$SCRIPT_DIR/cluster-data"
LOG_DIR="$DATA_DIR/logs"
PID_DIR="$DATA_DIR/pids"
NODE1_ADDR="127.0.0.1:8001"
NODE2_ADDR="127.0.0.1:8002"
NODE3_ADDR="127.0.0.1:8003"
build() {
echo "Building ToastyFS..."
(cd "$SCRIPT_DIR" && bash build.sh)
echo "Build complete."
}
ensure_dirs() {
mkdir -p "$LOG_DIR" "$PID_DIR"
mkdir -p "$DATA_DIR/chunks-node1"
mkdir -p "$DATA_DIR/chunks-node2"
mkdir -p "$DATA_DIR/chunks-node3"
}
start_node() {
local name="$1"
local addr="$2"
local peer1="$3"
local peer2="$4"
local chunks="$5"
local pidfile="$PID_DIR/$name.pid"
local logfile="$LOG_DIR/$name.log"
if [ -f "$pidfile" ] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
echo " $name is already running (pid $(cat "$pidfile"))"
return
fi
"$SCRIPT_DIR/toasty" \
--addr "$addr" \
--peer "$peer1" \
--peer "$peer2" \
--chunks "$chunks" \
> "$logfile" 2>&1 &
local pid=$!
echo "$pid" > "$pidfile"
echo " $name started (pid $pid) on $addr"
}
start_proxy() {
local pidfile="$PID_DIR/proxy.pid"
local logfile="$LOG_DIR/proxy.log"
if [ -f "$pidfile" ] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
echo " proxy is already running (pid $(cat "$pidfile"))"
return
fi
"$SCRIPT_DIR/toasty_proxy" \
--server "$NODE1_ADDR" \
--server "$NODE2_ADDR" \
--server "$NODE3_ADDR" \
> "$logfile" 2>&1 &
local pid=$!
echo "$pid" > "$pidfile"
echo " proxy started (pid $pid) on 127.0.0.1:3000"
}
stop_process() {
local name="$1"
local pidfile="$PID_DIR/$name.pid"
if [ ! -f "$pidfile" ]; then
echo " $name is not running (no pid file)"
return
fi
local pid
pid=$(cat "$pidfile")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
# Wait briefly for graceful shutdown
local i=0
while kill -0 "$pid" 2>/dev/null && [ $i -lt 10 ]; do
sleep 0.1
i=$((i + 1))
done
if kill -0 "$pid" 2>/dev/null; then
kill -9 "$pid" 2>/dev/null || true
fi
echo " $name stopped (was pid $pid)"
else
echo " $name was not running (stale pid $pid)"
fi
rm -f "$pidfile"
}
do_start() {
if [ ! -f "$SCRIPT_DIR/toasty" ] || [ ! -f "$SCRIPT_DIR/toasty_proxy" ]; then
build
fi
ensure_dirs
echo "Starting cluster..."
start_node "node1" "$NODE1_ADDR" "$NODE2_ADDR" "$NODE3_ADDR" "$DATA_DIR/chunks-node1"
start_node "node2" "$NODE2_ADDR" "$NODE1_ADDR" "$NODE3_ADDR" "$DATA_DIR/chunks-node2"
start_node "node3" "$NODE3_ADDR" "$NODE1_ADDR" "$NODE2_ADDR" "$DATA_DIR/chunks-node3"
# Give servers a moment to start listening before launching the proxy
sleep 1
start_proxy
echo "Cluster is running. HTTP proxy at http://127.0.0.1:3000"
}
do_stop() {
echo "Stopping cluster..."
stop_process "proxy"
stop_process "node1"
stop_process "node2"
stop_process "node3"
echo "Cluster stopped."
}
do_status() {
local all_stopped=true
for name in node1 node2 node3 proxy; do
local pidfile="$PID_DIR/$name.pid"
if [ -f "$pidfile" ] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
echo " $name: running (pid $(cat "$pidfile"))"
all_stopped=false
else
echo " $name: stopped"
fi
done
if $all_stopped; then
echo "Cluster is not running."
fi
}
do_clean() {
do_stop
echo "Removing cluster data..."
rm -rf "$DATA_DIR"
echo "Clean complete."
}
case "${1:-}" in
start) do_start ;;
stop) do_stop ;;
status) do_status ;;
restart) do_stop; do_start ;;
clean) do_clean ;;
*)
echo "Usage: $0 {start|stop|status|restart|clean}"
exit 1
;;
esac