 9aeaa433fc
			
		
	
	9aeaa433fc
	
	
	
		
			
			- Changed NetworkName from 'chorus_default' to 'chorus_net' - This matches the actual network 'CHORUS_chorus_net' (service prefix added automatically) - Fixes discovered_count:0 issue - now successfully discovering all 25 agents - Updated IMPLEMENTATION-SUMMARY with deployment status Result: All 25 CHORUS agents now discovered successfully via Docker Swarm API 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			226 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 	"os/signal"
 | |
| 	"strings"
 | |
| 	"syscall"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/chorus-services/whoosh/internal/config"
 | |
| 	"github.com/chorus-services/whoosh/internal/database"
 | |
| 	"github.com/chorus-services/whoosh/internal/server"
 | |
| 	"github.com/chorus-services/whoosh/internal/tracing"
 | |
| 	"github.com/kelseyhightower/envconfig"
 | |
| 	"github.com/rs/zerolog"
 | |
| 	"github.com/rs/zerolog/log"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	serviceName = "whoosh"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// Build-time variables (set via ldflags)
 | |
| 	version    = "0.1.5"
 | |
| 	commitHash = "unknown"
 | |
| 	buildDate  = "unknown"
 | |
| )
 | |
| 
 | |
| func main() {
 | |
| 	// Parse command line flags
 | |
| 	var (
 | |
| 		healthCheck = flag.Bool("health-check", false, "Run health check and exit")
 | |
| 		showVersion = flag.Bool("version", false, "Show version information and exit")
 | |
| 	)
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	// Handle version flag
 | |
| 	if *showVersion {
 | |
| 		fmt.Printf("WHOOSH %s\n", version)
 | |
| 		fmt.Printf("Commit: %s\n", commitHash)
 | |
| 		fmt.Printf("Built: %s\n", buildDate)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Handle health check flag
 | |
| 	if *healthCheck {
 | |
| 		if err := runHealthCheck(); err != nil {
 | |
| 			log.Fatal().Err(err).Msg("Health check failed")
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Configure structured logging
 | |
| 	setupLogging()
 | |
| 
 | |
| 	log.Info().
 | |
| 		Str("service", serviceName).
 | |
| 		Str("version", version).
 | |
| 		Str("commit", commitHash).
 | |
| 		Str("build_date", buildDate).
 | |
| 		Msg("🎭 Starting WHOOSH - Autonomous AI Development Teams")
 | |
| 
 | |
| 	// Load configuration
 | |
| 	var cfg config.Config
 | |
| 	
 | |
| 	// Debug: Print all environment variables starting with WHOOSH
 | |
| 	log.Debug().Msg("Environment variables:")
 | |
| 	for _, env := range os.Environ() {
 | |
| 		if strings.HasPrefix(env, "WHOOSH_") {
 | |
| 			// Don't log passwords in full, just indicate they exist
 | |
| 			if strings.Contains(env, "PASSWORD") {
 | |
| 				parts := strings.SplitN(env, "=", 2)
 | |
| 				if len(parts) == 2 && len(parts[1]) > 0 {
 | |
| 					log.Debug().Str("env", parts[0]+"=[REDACTED]").Msg("Found password env var")
 | |
| 				}
 | |
| 			} else {
 | |
| 				log.Debug().Str("env", env).Msg("Found env var")
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	if err := envconfig.Process("whoosh", &cfg); err != nil {
 | |
| 		log.Fatal().Err(err).Msg("Failed to load configuration")
 | |
| 	}
 | |
| 
 | |
| 	// Validate configuration
 | |
| 	if err := cfg.Validate(); err != nil {
 | |
| 		log.Fatal().Err(err).Msg("Invalid configuration")
 | |
| 	}
 | |
| 
 | |
| 	log.Info().
 | |
| 		Str("listen_addr", cfg.Server.ListenAddr).
 | |
| 		Str("database_host", cfg.Database.Host).
 | |
| 		Msg("📋 Configuration loaded")
 | |
| 
 | |
| 	// Initialize database
 | |
| 	db, err := database.NewPostgresDB(cfg.Database)
 | |
| 	if err != nil {
 | |
| 		log.Fatal().Err(err).Msg("Failed to initialize database")
 | |
| 	}
 | |
| 	defer db.Close()
 | |
| 
 | |
| 	log.Info().Msg("🗄️ Database connection established")
 | |
| 
 | |
| 	// Run migrations
 | |
| 	if cfg.Database.AutoMigrate {
 | |
| 		log.Info().Msg("🔄 Running database migrations...")
 | |
| 		if err := database.RunMigrations(cfg.Database.URL); err != nil {
 | |
| 			log.Fatal().Err(err).Msg("Database migration failed")
 | |
| 		}
 | |
| 		log.Info().Msg("✅ Database migrations completed")
 | |
| 	}
 | |
| 
 | |
| 	// Initialize tracing
 | |
| 	tracingCleanup, err := tracing.Initialize(cfg.OpenTelemetry)
 | |
| 	if err != nil {
 | |
| 		log.Fatal().Err(err).Msg("Failed to initialize tracing")
 | |
| 	}
 | |
| 	defer tracingCleanup()
 | |
| 	
 | |
| 	if cfg.OpenTelemetry.Enabled {
 | |
| 		log.Info().
 | |
| 			Str("jaeger_endpoint", cfg.OpenTelemetry.JaegerEndpoint).
 | |
| 			Msg("🔍 OpenTelemetry tracing enabled")
 | |
| 	} else {
 | |
| 		log.Info().Msg("🔍 OpenTelemetry tracing disabled (no-op tracer)")
 | |
| 	}
 | |
| 
 | |
| 	// Set version for server
 | |
| 	server.SetVersion(version)
 | |
| 	
 | |
| 	// Initialize server
 | |
| 	srv, err := server.NewServer(&cfg, db)
 | |
| 	if err != nil {
 | |
| 		log.Fatal().Err(err).Msg("Failed to create server")
 | |
| 	}
 | |
| 
 | |
| 	// Start server
 | |
| 	ctx, cancel := context.WithCancel(context.Background())
 | |
| 	defer cancel()
 | |
| 
 | |
| 	go func() {
 | |
| 		log.Info().
 | |
| 			Str("addr", cfg.Server.ListenAddr).
 | |
| 			Msg("🌐 Starting HTTP server")
 | |
| 		
 | |
| 		if err := srv.Start(ctx); err != nil {
 | |
| 			log.Error().Err(err).Msg("Server startup failed")
 | |
| 			cancel()
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	// Wait for shutdown signal
 | |
| 	sigChan := make(chan os.Signal, 1)
 | |
| 	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
 | |
| 
 | |
| 	select {
 | |
| 	case sig := <-sigChan:
 | |
| 		log.Info().Str("signal", sig.String()).Msg("🛑 Shutdown signal received")
 | |
| 	case <-ctx.Done():
 | |
| 		log.Info().Msg("🛑 Context cancelled")
 | |
| 	}
 | |
| 
 | |
| 	// Graceful shutdown
 | |
| 	log.Info().Msg("🔄 Starting graceful shutdown...")
 | |
| 	
 | |
| 	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
 | |
| 	defer shutdownCancel()
 | |
| 
 | |
| 	if err := srv.Shutdown(shutdownCtx); err != nil {
 | |
| 		log.Error().Err(err).Msg("Server shutdown failed")
 | |
| 	}
 | |
| 
 | |
| 	log.Info().Msg("✅ WHOOSH shutdown complete")
 | |
| }
 | |
| 
 | |
| func runHealthCheck() error {
 | |
| 	// Simple health check - try to connect to health endpoint
 | |
| 	client := &http.Client{Timeout: 5 * time.Second}
 | |
| 	
 | |
| 	// Use localhost for health check
 | |
| 	healthURL := "http://localhost:8080/health"
 | |
| 	
 | |
| 	resp, err := client.Get(healthURL)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("health check request failed: %w", err)
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	if resp.StatusCode != http.StatusOK {
 | |
| 		return fmt.Errorf("health check returned status %d", resp.StatusCode)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func setupLogging() {
 | |
| 	// Configure zerolog for structured logging
 | |
| 	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
 | |
| 	
 | |
| 	// Set log level from environment
 | |
| 	level := os.Getenv("LOG_LEVEL")
 | |
| 	switch level {
 | |
| 	case "debug":
 | |
| 		zerolog.SetGlobalLevel(zerolog.DebugLevel)
 | |
| 	case "info":
 | |
| 		zerolog.SetGlobalLevel(zerolog.InfoLevel)
 | |
| 	case "warn":
 | |
| 		zerolog.SetGlobalLevel(zerolog.WarnLevel)
 | |
| 	case "error":
 | |
| 		zerolog.SetGlobalLevel(zerolog.ErrorLevel)
 | |
| 	default:
 | |
| 		zerolog.SetGlobalLevel(zerolog.InfoLevel)
 | |
| 	}
 | |
| 
 | |
| 	// Pretty logging for development
 | |
| 	if os.Getenv("ENVIRONMENT") == "development" {
 | |
| 		log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | |
| 	}
 | |
| }
 |