Files
sonr/cmd/snrd/etc/bootstrap.sh
T

336 lines
9.5 KiB
Bash
Raw Normal View History

2025-10-03 14:45:52 -04:00
#!/bin/bash
set -e
# ================================
# SONR TESTNET BOOTSTRAP SCRIPT
# ================================
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_header() { echo -e "${BLUE}==== $1 ====${NC}"; }
# Script directory and repository root
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT"
# Load environment variables if .env exists
if [ -f "$REPO_ROOT/.env" ]; then
export $(grep -v '^#' "$REPO_ROOT/.env" | xargs)
fi
# Default values
export CHAIN_ID=${CHAIN_ID:-"sonrtest_1-1"}
export DENOM=${DENOM:-"usnr"}
export DOCKER_IMAGE=${DOCKER_IMAGE:-"onsonr/snrd:latest"}
# Function to check prerequisites
check_prerequisites() {
log_header "Checking prerequisites"
local has_error=false
# Check Docker
if ! command -v docker &> /dev/null; then
log_error "Docker is not installed"
has_error=true
else
log_info "Docker: $(docker --version)"
fi
# Check Docker Compose
if ! docker compose version &> /dev/null; then
log_error "Docker Compose is not installed"
has_error=true
else
log_info "Docker Compose: $(docker compose version)"
fi
# Check jq
if ! command -v jq &> /dev/null; then
log_warn "jq is not installed - some commands may not work"
log_warn "Install with: apt-get install jq (Ubuntu) or brew install jq (macOS)"
else
log_info "jq: $(jq --version)"
fi
if [ "$has_error" = true ]; then
log_error "Prerequisites check failed"
exit 1
fi
log_info "All prerequisites met"
echo ""
}
# Function to initialize validators
init_validators() {
log_header "Initializing validators and sentries"
# Check for local snrd binary
if ! command -v snrd &> /dev/null; then
log_error "snrd binary not found in PATH"
log_error "Please install snrd locally or add it to your PATH"
log_error "The initialization requires a local snrd binary to avoid permission issues"
exit 1
fi
if [ -f "$SCRIPT_DIR/init-testnet.sh" ]; then
log_info "Using init-testnet.sh (local snrd binary initialization)"
log_info "snrd location: $(which snrd)"
"$SCRIPT_DIR/init-testnet.sh"
else
log_error "init-testnet.sh not found in $SCRIPT_DIR!"
exit 1
fi
}
# Function to start testnet
start_testnet() {
log_header "Starting testnet"
# Check if validators are initialized
if [ ! -d "val-alice" ] || [ ! -d "sentry-alice" ]; then
log_warn "Validators not initialized, running init first..."
init_validators
fi
# Pull latest image
log_info "Pulling Docker image: $DOCKER_IMAGE"
docker pull "$DOCKER_IMAGE"
# Start services
log_info "Starting services with docker compose..."
docker compose up -d
# Wait for services to be healthy
log_info "Waiting for services to start..."
sleep 5
# Check status
show_status
log_info "Testnet started successfully!"
echo ""
log_info "View logs: docker compose logs -f"
log_info "Stop testnet: ./bootstrap.sh stop"
}
# Function to stop testnet
stop_testnet() {
log_header "Stopping testnet"
docker compose down
log_info "Testnet stopped"
}
# Function to restart testnet
restart_testnet() {
log_header "Restarting testnet"
stop_testnet
sleep 2
start_testnet
}
# Function to clean all data
clean_all() {
log_header "Cleaning testnet data"
# Stop containers and remove volumes
log_info "Stopping containers and removing volumes..."
docker compose down -v 2>/dev/null || true
# Remove data directories using Docker to handle permission issues
log_info "Removing validator and sentry directories..."
if [ -d "val-alice" ] || [ -d "val-bob" ] || [ -d "val-carol" ] || [ -d "sentry-alice" ] || [ -d "sentry-bob" ] || [ -d "sentry-carol" ]; then
docker run --rm -v "$(pwd):/workspace" alpine:latest sh -c \
"rm -rf /workspace/val-alice /workspace/val-bob /workspace/val-carol /workspace/sentry-alice /workspace/sentry-bob /workspace/sentry-carol" \
2>/dev/null || true
fi
log_info "Clean complete"
}
# Function to show status
show_status() {
log_header "Testnet Status"
echo ""
echo "Container Status:"
docker ps --filter "name=val-" --filter "name=sentry-" --filter "name=ipfs" --format "table {{.Names}}\t{{.Status}}\t{{.State}}"
echo ""
echo "Network Endpoints (via Cloudflare Tunnel):"
echo " Alice RPC: https://alice-rpc.sonr.land"
echo " Alice REST: https://alice-rest.sonr.land"
echo " Alice gRPC: https://alice-grpc.sonr.land"
echo " Alice EVM: https://alice-evm.sonr.land"
echo " Bob RPC: https://bob-rpc.sonr.land"
echo " Carol RPC: https://carol-rpc.sonr.land"
echo " IPFS API: https://ipfs-api.sonr.land"
echo " IPFS Gateway: https://ipfs-gateway.sonr.land"
# Check sync status if services are running
if docker ps | grep -q "sentry-alice"; then
echo ""
echo "Sync Status:"
local block_height=$(docker exec sentry-alice sh -c 'curl -s http://localhost:26657/status 2>/dev/null | jq -r ".result.sync_info.latest_block_height" 2>/dev/null' || echo "0")
echo " Block Height: $block_height"
for container in sentry-alice sentry-bob sentry-carol; do
status=$(docker exec $container sh -c 'curl -s http://localhost:26657/status 2>/dev/null | jq -r ".result.sync_info.catching_up" 2>/dev/null' || echo "unavailable")
echo " $container: catching_up=$status"
done
fi
echo ""
}
# Function to view logs
view_logs() {
local service=$1
if [ -z "$service" ]; then
docker compose logs -f --tail=100
else
docker compose logs -f --tail=100 "$service"
fi
}
# Function to execute command in container
exec_in_container() {
local container=$1
shift
if [ -z "$container" ]; then
log_error "Container name required"
echo "Usage: ./bootstrap.sh exec <container> <command>"
echo "Example: ./bootstrap.sh exec val-alice status"
return 1
fi
docker exec -it "$container" snrd --home /root/.sonr "$@"
}
# Function to run tests
run_tests() {
log_header "Running testnet tests"
# Check if all services are running
log_info "Checking service health..."
local all_healthy=true
for service in val-alice val-bob val-carol sentry-alice sentry-bob sentry-carol; do
if ! docker ps | grep -q "$service"; then
log_error "$service is not running"
all_healthy=false
fi
done
if [ "$all_healthy" = false ]; then
log_error "Not all services are running"
return 1
fi
# Test RPC endpoints
log_info "Testing RPC endpoints..."
for container in sentry-alice sentry-bob sentry-carol; do
if docker exec $container sh -c 'curl -s http://localhost:26657/status' > /dev/null 2>&1; then
log_info "$container: OK"
else
log_error "$container: FAILED"
fi
done
# Test validator connectivity
log_info "Testing validator connectivity..."
local block_height=$(docker exec sentry-alice sh -c 'curl -s http://localhost:26657/status 2>/dev/null | jq -r ".result.sync_info.latest_block_height" 2>/dev/null' || echo "unavailable")
if [ "$block_height" != "unavailable" ] && [ "$block_height" != "null" ]; then
log_info "Current block height: $block_height"
else
log_error "Failed to get validator status"
fi
log_info "Tests complete"
}
# Function to show usage
show_usage() {
echo "Sonr Testnet Bootstrap Script"
echo ""
echo "Usage: ./bootstrap.sh [command]"
echo ""
echo "Commands:"
echo " init Initialize validators and sentries"
echo " start Start the testnet"
echo " stop Stop the testnet"
echo " restart Restart the testnet"
echo " status Show testnet status"
echo " logs View logs (optional: service name)"
echo " clean Clean all data and volumes"
echo " exec Execute command in container"
echo " test Run basic tests"
echo " help Show this help message"
echo ""
echo "Examples:"
echo " ./bootstrap.sh init"
echo " ./bootstrap.sh start"
echo " ./bootstrap.sh logs sentry-alice"
echo " ./bootstrap.sh exec val-alice status"
echo ""
}
# Main command handler
main() {
case "${1:-help}" in
init)
check_prerequisites
init_validators
;;
start)
check_prerequisites
start_testnet
;;
stop)
stop_testnet
;;
restart)
restart_testnet
;;
status)
show_status
;;
logs)
view_logs "${2:-}"
;;
clean)
clean_all
;;
exec)
shift
exec_in_container "$@"
;;
test)
run_tests
;;
help|--help|-h)
show_usage
;;
*)
log_error "Unknown command: $1"
show_usage
exit 1
;;
esac
}
# Run main function with all arguments
main "$@"