Files
go-sip/cmd/saas-server/main.go
T

100 lines
2.8 KiB
Go

package main
import (
"context"
"crypto/tls"
"errors"
"flag"
"fmt"
"log/slog"
"net"
"os"
"os/signal"
"syscall"
"time"
"github.com/gofiber/fiber/v3"
)
func run(ctx context.Context, path string) error {
config, err := loadStartup(path)
if err != nil {
return err
}
data, err := loadSnapshots(config.Snapshots, config.DispatcherID)
if err != nil {
return err
}
slog.Info("test configurations loaded", "dispatcher_id", data.dispatcherID, "tasks", len(data.tasks))
if err = os.MkdirAll(config.Feedback, 0700); err != nil {
return fmt.Errorf("create private feedback directory: %w", err)
}
info, err := os.Stat(config.Feedback)
if err != nil {
return err
}
if !info.IsDir() || info.Mode().Perm()&0077 != 0 {
return errors.New("feedback directory must be private")
}
b, err := connectBroker(config.BrokerURL, data)
if err != nil {
return err
}
defer b.conn.Close()
slog.Info("isolated MQ topology ready", "dispatcher_id", data.dispatcherID)
listener, err := net.Listen("tcp", config.Listen)
if err != nil {
return fmt.Errorf("listen: %w", err)
}
defer listener.Close()
if config.TLSCert != "" {
pair, e := tls.LoadX509KeyPair(config.TLSCert, config.TLSKey)
if e != nil {
return fmt.Errorf("load configured TLS pair: %w", e)
}
listener = tls.NewListener(listener, &tls.Config{Certificates: []tls.Certificate{pair}, MinVersion: tls.VersionTLS12})
}
s := newService(data, config.DispatcherSecret)
s.feedbackDir = config.Feedback
s.publish = b.publish
child, cancel := context.WithCancel(ctx)
defer cancel()
errs := make(chan error, 2)
go func() { errs <- b.consume(child, s) }()
go func() { errs <- s.app.Listener(listener, fiber.ListenConfig{DisableStartupMessage: true}) }()
slog.Info("SaaS test server listening", "address", listener.Addr().String(), "tls", config.TLSCert != "", "automatic_commands", false)
select {
case <-ctx.Done():
err = nil
case err = <-errs:
if err == nil && ctx.Err() == nil {
err = errors.New("SaaS test server stopped unexpectedly")
}
}
cancel()
if shutdownErr := s.app.ShutdownWithTimeout(5 * time.Second); shutdownErr != nil {
err = errors.Join(err, fmt.Errorf("HTTP shutdown: %w", shutdownErr))
}
// Closing the listener/connection unblocks both workers; do not leave consumers behind.
listener.Close()
b.conn.Close()
slog.Info("SaaS test server stopped")
return err
}
func main() {
var path string
flag.StringVar(&path, "config", "", "private startup JSON (required)")
flag.Parse()
if path == "" || flag.NArg() != 0 {
fmt.Fprintln(os.Stderr, "usage: saas-server --config /path/server.json")
os.Exit(2)
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
if err := run(ctx, path); err != nil {
slog.Error("SaaS test server failed", "error", err)
os.Exit(1)
}
}