All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m28s
104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"log/slog"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/barkstack/pawsql/internal/config"
|
|
"github.com/barkstack/pawsql/internal/postgres"
|
|
"github.com/barkstack/pawsql/internal/router"
|
|
"github.com/barkstack/pawsql/internal/server"
|
|
)
|
|
|
|
func main() {
|
|
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
|
if err := run(os.Args[1:], logger); err != nil {
|
|
logger.Error("pawsql failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func run(args []string, logger *slog.Logger) error {
|
|
validateOnly, configPath, err := parseArguments(args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfg, err := config.ParseFile(configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
return fmt.Errorf("invalid configuration: %w", err)
|
|
}
|
|
certificate, err := tls.LoadX509KeyPair(cfg.TLS.CertFile, cfg.TLS.KeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("load TLS certificate and key: %w", err)
|
|
}
|
|
if validateOnly {
|
|
logger.Info("configuration is valid", "config", configPath)
|
|
return nil
|
|
}
|
|
|
|
staticResolver, err := router.NewStaticResolver(cfg.Databases)
|
|
if err != nil {
|
|
return fmt.Errorf("build route resolver: %w", err)
|
|
}
|
|
resolver := postgres.NewResolver(staticResolver, cfg.Databases, postgres.NewProvisioner(logger))
|
|
routingServer, err := server.New(&tls.Config{Certificates: []tls.Certificate{certificate}}, resolver, logger)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
listener, err := net.Listen("tcp", cfg.Listen)
|
|
if err != nil {
|
|
return fmt.Errorf("listen on %s: %w", cfg.Listen, err)
|
|
}
|
|
logger.Info("pawsql listening", "address", listener.Addr().String())
|
|
|
|
signals := make(chan os.Signal, 1)
|
|
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
|
|
defer signal.Stop(signals)
|
|
serveErrors := make(chan error, 1)
|
|
go func() { serveErrors <- routingServer.Serve(listener) }()
|
|
|
|
select {
|
|
case received := <-signals:
|
|
logger.Info("shutdown signal received", "signal", received.String())
|
|
if err := routingServer.Shutdown(); err != nil && !errors.Is(err, net.ErrClosed) {
|
|
return fmt.Errorf("stop listener: %w", err)
|
|
}
|
|
if err := <-serveErrors; err != nil {
|
|
return err
|
|
}
|
|
routingServer.Wait()
|
|
logger.Info("pawsql shutdown complete")
|
|
return nil
|
|
case err := <-serveErrors:
|
|
return err
|
|
}
|
|
}
|
|
|
|
func parseArguments(args []string) (validateOnly bool, configPath string, err error) {
|
|
if len(args) > 0 && args[0] == "validate" {
|
|
validateOnly = true
|
|
args = args[1:]
|
|
}
|
|
flags := flag.NewFlagSet("pawsql", flag.ContinueOnError)
|
|
flags.SetOutput(os.Stderr)
|
|
configPath = "Barkfile"
|
|
flags.StringVar(&configPath, "config", configPath, "path to Barkfile")
|
|
if err := flags.Parse(args); err != nil {
|
|
return false, "", err
|
|
}
|
|
if flags.NArg() != 0 {
|
|
return false, "", fmt.Errorf("unexpected arguments: %v", flags.Args())
|
|
}
|
|
return validateOnly, configPath, nil
|
|
}
|