- Complete Gemini CLI agent adapter with SSH execution - CLI agent factory with connection pooling - SSH executor with AsyncSSH for remote CLI execution - Backend integration with CLI agent manager - MCP server updates with CLI agent tools - Frontend UI updates for mixed agent types - Database migrations for CLI agent support - Docker deployment with CLI source integration - Comprehensive documentation and testing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
207 lines
6.1 KiB
Bash
Executable File
207 lines
6.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# CCLI SSH Connection Pooling Test
|
|
# Tests SSH connection reuse, limits, and error handling
|
|
|
|
set -e
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
function log() { echo -e "${BLUE}[$(date +'%H:%M:%S')]${NC} $1"; }
|
|
function success() { echo -e "${GREEN}✅ $1${NC}"; }
|
|
function warning() { echo -e "${YELLOW}⚠️ $1${NC}"; }
|
|
function error() { echo -e "${RED}❌ $1${NC}"; }
|
|
|
|
function test_connection_reuse() {
|
|
local host=$1
|
|
log "Testing SSH connection reuse on $host..."
|
|
|
|
# Use SSH ControlMaster for connection sharing
|
|
local control_path="/tmp/ssh_control_${host}_$$"
|
|
local ssh_opts="-o ControlMaster=auto -o ControlPath=$control_path -o ControlPersist=30"
|
|
|
|
# Start master connection
|
|
ssh $ssh_opts $host "echo 'Master connection established'" > /dev/null
|
|
|
|
# Test rapid connections (should reuse)
|
|
local start_time=$(date +%s.%N)
|
|
for i in {1..5}; do
|
|
ssh $ssh_opts $host "echo 'Reused connection $i'" > /dev/null &
|
|
done
|
|
wait
|
|
local end_time=$(date +%s.%N)
|
|
local duration=$(echo "$end_time - $start_time" | bc -l)
|
|
|
|
# Clean up
|
|
ssh $ssh_opts -O exit $host 2>/dev/null || true
|
|
rm -f "$control_path"
|
|
|
|
success "Connection reuse test completed in ${duration:0:5}s"
|
|
echo "$duration" > "/tmp/ssh_reuse_${host}.txt"
|
|
}
|
|
|
|
function test_connection_limits() {
|
|
local host=$1
|
|
log "Testing SSH connection limits on $host..."
|
|
|
|
local max_connections=10
|
|
local pids=()
|
|
local results_dir="/tmp/ssh_limits_${host}"
|
|
mkdir -p "$results_dir"
|
|
|
|
# Start multiple connections
|
|
for i in $(seq 1 $max_connections); do
|
|
{
|
|
ssh $host "sleep 5 && echo 'Connection $i completed'" > "$results_dir/conn_$i.out" 2>&1
|
|
echo $? > "$results_dir/conn_$i.exit"
|
|
} &
|
|
pids+=($!)
|
|
done
|
|
|
|
# Wait and count successful connections
|
|
wait
|
|
local successful=0
|
|
for i in $(seq 1 $max_connections); do
|
|
if [[ -f "$results_dir/conn_$i.exit" ]] && [[ $(cat "$results_dir/conn_$i.exit") -eq 0 ]]; then
|
|
((successful++))
|
|
fi
|
|
done
|
|
|
|
success "SSH connection limit test: $successful/$max_connections successful"
|
|
|
|
# Clean up
|
|
rm -rf "$results_dir"
|
|
}
|
|
|
|
function test_connection_recovery() {
|
|
local host=$1
|
|
log "Testing SSH connection recovery on $host..."
|
|
|
|
# Normal connection
|
|
if ssh $host "echo 'Normal connection'" > /dev/null 2>&1; then
|
|
success "Normal SSH connection working"
|
|
else
|
|
error "Normal SSH connection failed"
|
|
return 1
|
|
fi
|
|
|
|
# Test with short timeout
|
|
if timeout 5s ssh -o ConnectTimeout=2 $host "echo 'Quick connection'" > /dev/null 2>&1; then
|
|
success "Quick SSH connection working"
|
|
else
|
|
warning "Quick SSH connection timed out (may be normal under load)"
|
|
fi
|
|
|
|
# Test connection to invalid host (should fail gracefully)
|
|
if ssh -o ConnectTimeout=3 -o BatchMode=yes invalid-host-12345 "echo 'test'" > /dev/null 2>&1; then
|
|
warning "Connection to invalid host unexpectedly succeeded"
|
|
else
|
|
success "Connection to invalid host correctly failed"
|
|
fi
|
|
}
|
|
|
|
function test_gemini_via_ssh_multiplex() {
|
|
local host=$1
|
|
local node_version=$2
|
|
log "Testing Gemini CLI via SSH multiplexing on $host..."
|
|
|
|
local control_path="/tmp/ssh_gemini_${host}_$$"
|
|
local ssh_opts="-o ControlMaster=auto -o ControlPath=$control_path -o ControlPersist=60"
|
|
|
|
# Establish master connection
|
|
ssh $ssh_opts $host "echo 'Gemini multiplex ready'" > /dev/null
|
|
|
|
# Run multiple Gemini commands concurrently
|
|
local pids=()
|
|
local start_time=$(date +%s.%N)
|
|
|
|
for i in {1..3}; do
|
|
{
|
|
local cmd="source ~/.nvm/nvm.sh && nvm use $node_version && echo 'Task $i: Count to 3' | gemini --model gemini-2.5-pro"
|
|
ssh $ssh_opts $host "$cmd" > "/tmp/gemini_multiplex_${host}_$i.out" 2>&1
|
|
} &
|
|
pids+=($!)
|
|
done
|
|
|
|
wait
|
|
local end_time=$(date +%s.%N)
|
|
local duration=$(echo "$end_time - $start_time" | bc -l)
|
|
|
|
# Check results
|
|
local successful=0
|
|
for i in {1..3}; do
|
|
if [[ -s "/tmp/gemini_multiplex_${host}_$i.out" ]]; then
|
|
((successful++))
|
|
fi
|
|
done
|
|
|
|
# Clean up
|
|
ssh $ssh_opts -O exit $host 2>/dev/null || true
|
|
rm -f "$control_path" /tmp/gemini_multiplex_${host}_*.out
|
|
|
|
success "SSH multiplexed Gemini: $successful/3 tasks completed in ${duration:0:5}s"
|
|
}
|
|
|
|
function run_ssh_pooling_tests() {
|
|
local host=$1
|
|
local node_version=$2
|
|
|
|
echo ""
|
|
echo "🔗 SSH Connection Pooling Tests: $host"
|
|
echo "======================================="
|
|
|
|
test_connection_reuse "$host"
|
|
test_connection_limits "$host"
|
|
test_connection_recovery "$host"
|
|
test_gemini_via_ssh_multiplex "$host" "$node_version"
|
|
|
|
success "SSH pooling tests completed for $host"
|
|
}
|
|
|
|
# Main execution
|
|
echo "🚀 CCLI SSH Connection Pooling Test Suite"
|
|
echo ""
|
|
|
|
# Check dependencies
|
|
if ! command -v bc &> /dev/null; then
|
|
error "bc not found. Install with: sudo apt-get install bc"
|
|
exit 1
|
|
fi
|
|
|
|
# Test both machines
|
|
run_ssh_pooling_tests "walnut" "v22.14.0"
|
|
run_ssh_pooling_tests "ironwood" "v22.17.0"
|
|
|
|
# Performance comparison
|
|
echo ""
|
|
echo "📊 SSH Performance Analysis"
|
|
echo "=========================="
|
|
|
|
if [[ -f "/tmp/ssh_reuse_walnut.txt" ]] && [[ -f "/tmp/ssh_reuse_ironwood.txt" ]]; then
|
|
walnut_time=$(cat /tmp/ssh_reuse_walnut.txt)
|
|
ironwood_time=$(cat /tmp/ssh_reuse_ironwood.txt)
|
|
|
|
log "SSH connection reuse performance:"
|
|
log " WALNUT: ${walnut_time:0:5}s for 5 connections"
|
|
log " IRONWOOD: ${ironwood_time:0:5}s for 5 connections"
|
|
|
|
faster=$(echo "$walnut_time < $ironwood_time" | bc -l)
|
|
if [[ $faster -eq 1 ]]; then
|
|
success "WALNUT has faster SSH connection reuse"
|
|
else
|
|
success "IRONWOOD has faster SSH connection reuse"
|
|
fi
|
|
fi
|
|
|
|
success "🎉 SSH pooling tests completed successfully!"
|
|
echo ""
|
|
echo "📋 Key Findings:"
|
|
echo " ✅ SSH connection reuse working"
|
|
echo " ✅ Multiple concurrent connections supported"
|
|
echo " ✅ Connection recovery working"
|
|
echo " ✅ SSH multiplexing with Gemini CLI functional" |