package main import ( "fmt" "os" "os/signal" "strconv" "strings" "syscall" "git.solsynth.dev/goatworks/turbine/pkg/shared/hash" "git.solsynth.dev/goatworks/turbine/pkg/shared/registrar" "github.com/gofiber/fiber/v3" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/viper" ) func init() { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) } func main() { log.Info().Msg("Starting Config Service...") viper.SetConfigName("settings") viper.AddConfigPath(".") viper.SetConfigType("toml") if err := viper.ReadInConfig(); err != nil { log.Fatal().Err(err).Msg("Failed to read config file") } log.Info().Msg("Configuration loaded.") // --- Service Registration --- etcdEndpoints := viper.GetStringSlice("etcd.endpoints") if len(etcdEndpoints) == 0 { log.Fatal().Msg("etcd.endpoints not configured") } if viper.GetBool("etcd.insecure") { for i, ep := range etcdEndpoints { if !strings.HasPrefix(ep, "http://") && !strings.HasPrefix(ep, "https://") { etcdEndpoints[i] = "http://" + ep } } } serviceReg, err := registrar.NewServiceRegistrar(etcdEndpoints) if err != nil { log.Fatal().Err(err).Msg("Failed to create service registrar") } listenAddr := viper.GetString("listen") host := viper.GetString("host") if host == "" { log.Fatal().Msg("host not configured") } portStr := strings.TrimPrefix(listenAddr, ":") port, err := strconv.Atoi(portStr) if err != nil { log.Fatal().Err(err).Msg("Invalid listen address") } serviceName := "config" instanceID := fmt.Sprint(hash.Hash(fmt.Sprintf("%s-%s-%d", serviceName, host, port)))[:8] err = serviceReg.Register(serviceName, "http", instanceID, host, port, 30) if err != nil { log.Fatal().Err(err).Msg("Failed to register service") } log.Info().Str("service", serviceName).Str("instanceID", instanceID).Msg("Service registered successfully") // --- Web Server --- app := fiber.New(fiber.Config{ ServerHeader: "Turbine Config", }) // This is the main endpoint that serves the configuration as JSON. app.Get("/api/", func(c fiber.Ctx) error { log.Info().Msg("Serving shared configuration as JSON") // Use a new Viper instance to read the shared config v := viper.New() v.SetConfigName("shared_config") v.AddConfigPath(".") // Look in the current directory (pkg/config) v.SetConfigType("toml") if err := v.ReadInConfig(); err != nil { log.Error().Err(err).Msg("Failed to read shared_config.toml") return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": "could not load configuration", }) } return c.JSON(v.AllSettings()) }) // Health check endpoint app.Get("/health", func(c fiber.Ctx) error { return c.SendString("Config service is running") }) // --- Graceful Shutdown --- go func() { err := app.Listen(listenAddr, fiber.ListenConfig{DisableStartupMessage: true}) if err != nil { log.Fatal().Err(err).Msg("Failed to start server") } }() log.Info().Msg("Server is listening on " + listenAddr) stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) <-stop log.Info().Msg("Shutting down service...") if err := serviceReg.Deregister(); err != nil { log.Error().Err(err).Msg("Failed to deregister service") } else { log.Info().Msg("Service deregistered successfully") } if err := app.Shutdown(); err != nil { log.Error().Err(err).Msg("Error during server shutdown") } }