Replace Portainer container management service with Dockhand: - Update docker-compose.yml.template with Dockhand service definition - Replace portainer_data volume with dockhand_data - Update PORTAINER_PORT to DOCKHAND_PORT in demo.env - Update all script references (demo-stack.sh, demo-test.sh, validate-all.sh) - Update integration test from Portainer to Dockhand - Update documentation files (README.md, AGENTS.md, api-docs, service-guides, troubleshooting) Dockhand provides modern Docker management UI with: - Container lifecycle management - Compose stack orchestration - Git-based deployments - Multi-environment support - Terminal access and log streaming - File browser capabilities Maintains same port (4007) for consistency. 💘 Generated with Crush Assisted-by: GLM-4.7 via Crush <crush@charm.land>
291 lines
8.7 KiB
Bash
Executable File
291 lines
8.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# TSYS Developer Support Stack - Demo Deployment Script
|
|
# Version: 1.0
|
|
# Purpose: Dynamic deployment with user detection and validation
|
|
|
|
set -euo pipefail
|
|
|
|
# Script Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
DEMO_ENV_FILE="$PROJECT_ROOT/demo.env"
|
|
COMPOSE_FILE="$PROJECT_ROOT/docker-compose.yml"
|
|
|
|
# Color Codes for Output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging Functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Function to detect current user and group IDs
|
|
detect_user_ids() {
|
|
log_info "Detecting user and group IDs..."
|
|
|
|
local uid
|
|
local gid
|
|
local docker_gid
|
|
uid=$(id -u)
|
|
gid=$(id -g)
|
|
docker_gid=$(getent group docker | cut -d: -f3)
|
|
|
|
if [[ -z "$docker_gid" ]]; then
|
|
log_error "Docker group not found. Please ensure Docker is installed and user is in docker group."
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Detected UID: $uid, GID: $gid, Docker GID: $docker_gid"
|
|
|
|
# Update demo.env with detected values
|
|
sed -i "s/^DEMO_UID=$/DEMO_UID=$uid/" "$DEMO_ENV_FILE"
|
|
sed -i "s/^DEMO_GID=$/DEMO_GID=$gid/" "$DEMO_ENV_FILE"
|
|
sed -i "s/^DEMO_DOCKER_GID=$/DEMO_DOCKER_GID=$docker_gid/" "$DEMO_ENV_FILE"
|
|
|
|
log_success "User IDs detected and configured"
|
|
}
|
|
|
|
# Function to validate prerequisites
|
|
validate_prerequisites() {
|
|
log_info "Validating prerequisites..."
|
|
|
|
# Check if Docker is installed and running
|
|
if ! command -v docker &> /dev/null; then
|
|
log_error "Docker is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker info &> /dev/null; then
|
|
log_error "Docker daemon is not running"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if Docker Compose is available
|
|
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
|
log_error "Docker Compose is not installed"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if demo.env exists
|
|
if [[ ! -f "$DEMO_ENV_FILE" ]]; then
|
|
log_error "demo.env file not found at $DEMO_ENV_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Prerequisites validation passed"
|
|
}
|
|
|
|
# Function to generate docker-compose.yml from template
|
|
generate_compose_file() {
|
|
log_info "Generating docker-compose.yml..."
|
|
|
|
# Check if template exists (will be created in next phase)
|
|
local template_file="$PROJECT_ROOT/docker-compose.yml.template"
|
|
if [[ ! -f "$template_file" ]]; then
|
|
log_error "Docker Compose template not found at $template_file"
|
|
log_info "Please ensure the template file is created before running deployment"
|
|
exit 1
|
|
fi
|
|
|
|
# Source and export environment variables
|
|
# shellcheck disable=SC1090,SC1091
|
|
set -a
|
|
source "$DEMO_ENV_FILE"
|
|
set +a
|
|
|
|
# Generate docker-compose.yml from template
|
|
envsubst < "$template_file" > "$COMPOSE_FILE"
|
|
|
|
log_success "docker-compose.yml generated successfully"
|
|
}
|
|
|
|
# Function to deploy the stack
|
|
deploy_stack() {
|
|
log_info "Deploying TSYS Developer Support Stack..."
|
|
|
|
# Change to project directory
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# Deploy the stack
|
|
if command -v docker-compose &> /dev/null; then
|
|
docker-compose -f "$COMPOSE_FILE" up -d
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" up -d
|
|
fi
|
|
|
|
log_success "Stack deployment initiated"
|
|
}
|
|
|
|
# Function to wait for services to be healthy
|
|
wait_for_services() {
|
|
log_info "Waiting for services to become healthy..."
|
|
|
|
local max_wait=300 # 5 minutes
|
|
local wait_interval=10
|
|
local elapsed=0
|
|
|
|
while [[ $elapsed -lt $max_wait ]]; do
|
|
local unhealthy_services=0
|
|
|
|
# Check service health (will be implemented with actual service names)
|
|
if command -v docker-compose &> /dev/null; then
|
|
mapfile -t services < <(docker-compose -f "$COMPOSE_FILE" config --services)
|
|
else
|
|
mapfile -t services < <(docker compose -f "$COMPOSE_FILE" config --services)
|
|
fi
|
|
|
|
for service in "${services[@]}"; do
|
|
local health_status
|
|
if command -v docker-compose &> /dev/null; then
|
|
health_status=$(docker-compose -f "$COMPOSE_FILE" ps -q "$service" | xargs docker inspect --format='{{.State.Health.Status}}' 2>/dev/null || echo "none")
|
|
else
|
|
health_status=$(docker compose -f "$COMPOSE_FILE" ps -q "$service" | xargs docker inspect --format='{{.State.Health.Status}}' 2>/dev/null || echo "none")
|
|
fi
|
|
|
|
if [[ "$health_status" != "healthy" && "$health_status" != "none" ]]; then
|
|
((unhealthy_services++))
|
|
fi
|
|
done
|
|
|
|
if [[ $unhealthy_services -eq 0 ]]; then
|
|
log_success "All services are healthy"
|
|
return 0
|
|
fi
|
|
|
|
log_info "$unhealthy_services services still unhealthy... waiting ${wait_interval}s"
|
|
sleep $wait_interval
|
|
elapsed=$((elapsed + wait_interval))
|
|
done
|
|
|
|
log_warning "Timeout reached. Some services may not be fully healthy."
|
|
return 1
|
|
}
|
|
|
|
# Function to display deployment summary
|
|
display_summary() {
|
|
log_success "TSYS Developer Support Stack Deployment Summary"
|
|
echo "=================================================="
|
|
echo "📊 Homepage Dashboard: http://localhost:${HOMEPAGE_PORT:-4000}"
|
|
echo "🏗️ Infrastructure Services:"
|
|
echo " - Pi-hole (DNS): http://localhost:${PIHOLE_PORT:-4006}"
|
|
echo " - Dockhand (Containers): http://localhost:${DOCKHAND_PORT:-4007}"
|
|
echo "📊 Monitoring & Observability:"
|
|
echo " - InfluxDB (Database): http://localhost:${INFLUXDB_PORT:-4008}"
|
|
echo " - Grafana (Visualization): http://localhost:${GRAFANA_PORT:-4009}"
|
|
echo "📚 Documentation & Diagramming:"
|
|
echo " - Draw.io (Diagrams): http://localhost:${DRAWIO_PORT:-4010}"
|
|
echo " - Kroki (Diagrams as Service): http://localhost:${KROKI_PORT:-4011}"
|
|
echo "🛠️ Developer Tools:"
|
|
echo " - Atomic Tracker (Habits): http://localhost:${ATOMIC_TRACKER_PORT:-4012}"
|
|
echo " - ArchiveBox (Archiving): http://localhost:${ARCHIVEBOX_PORT:-4013}"
|
|
echo " - Tube Archivist (YouTube): http://localhost:${TUBE_ARCHIVIST_PORT:-4014}"
|
|
echo " - Wakapi (Time Tracking): http://localhost:${WAKAPI_PORT:-4015}"
|
|
echo " - MailHog (Email Testing): http://localhost:${MAILHOG_PORT:-4017}"
|
|
echo " - Atuin (Shell History): http://localhost:${ATUIN_PORT:-4018}"
|
|
echo "=================================================="
|
|
echo "🔐 Demo Credentials:"
|
|
echo " Username: ${DEMO_ADMIN_USER:-admin}"
|
|
echo " Password: ${DEMO_ADMIN_PASSWORD:-demo_password}"
|
|
echo "⚠️ FOR DEMONSTRATION PURPOSES ONLY - NOT FOR PRODUCTION"
|
|
}
|
|
|
|
# Function to stop the stack
|
|
stop_stack() {
|
|
log_info "Stopping TSYS Developer Support Stack..."
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
if command -v docker-compose &> /dev/null; then
|
|
docker-compose -f "$COMPOSE_FILE" down
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" down
|
|
fi
|
|
|
|
log_success "Stack stopped"
|
|
}
|
|
|
|
# Function to restart the stack
|
|
restart_stack() {
|
|
log_info "Restarting TSYS Developer Support Stack..."
|
|
stop_stack
|
|
sleep 5
|
|
deploy_stack
|
|
wait_for_services
|
|
display_summary
|
|
}
|
|
|
|
# Function to show usage
|
|
show_usage() {
|
|
echo "Usage: $0 {deploy|stop|restart|status|help}"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " deploy - Deploy the complete stack"
|
|
echo " stop - Stop all services"
|
|
echo " restart - Restart all services"
|
|
echo " status - Show service status"
|
|
echo " help - Show this help message"
|
|
}
|
|
|
|
# Function to show status
|
|
show_status() {
|
|
log_info "TSYS Developer Support Stack Status"
|
|
echo "===================================="
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
if command -v docker-compose &> /dev/null; then
|
|
docker-compose -f "$COMPOSE_FILE" ps
|
|
else
|
|
docker compose -f "$COMPOSE_FILE" ps
|
|
fi
|
|
}
|
|
|
|
# Main script execution
|
|
main() {
|
|
case "${1:-deploy}" in
|
|
deploy)
|
|
validate_prerequisites
|
|
detect_user_ids
|
|
generate_compose_file
|
|
deploy_stack
|
|
wait_for_services
|
|
display_summary
|
|
;;
|
|
stop)
|
|
stop_stack
|
|
;;
|
|
restart)
|
|
restart_stack
|
|
;;
|
|
status)
|
|
show_status
|
|
;;
|
|
help|--help|-h)
|
|
show_usage
|
|
;;
|
|
*)
|
|
log_error "Unknown command: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Execute main function with all arguments
|
|
main "$@" |