All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m28s
116 lines
3.6 KiB
Go
116 lines
3.6 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// NormalizeHostname returns the canonical lookup form for a DNS hostname.
|
|
func NormalizeHostname(hostname string) string {
|
|
return strings.TrimSuffix(strings.ToLower(strings.TrimSpace(hostname)), ".")
|
|
}
|
|
|
|
// Validate verifies configuration invariants that do not require opening files.
|
|
func (c Config) Validate() error {
|
|
var errs []error
|
|
if strings.TrimSpace(c.Listen) == "" {
|
|
errs = append(errs, errors.New("listen address is required"))
|
|
} else if err := validateListenAddress(c.Listen); err != nil {
|
|
errs = append(errs, fmt.Errorf("listen address %q: %w", c.Listen, err))
|
|
}
|
|
if strings.TrimSpace(c.TLS.CertFile) == "" {
|
|
errs = append(errs, errors.New("TLS certificate is required"))
|
|
}
|
|
if strings.TrimSpace(c.TLS.KeyFile) == "" {
|
|
errs = append(errs, errors.New("TLS private key is required"))
|
|
}
|
|
if len(c.Databases) == 0 {
|
|
errs = append(errs, errors.New("at least one database route is required"))
|
|
}
|
|
|
|
seenHostnames := make(map[string]string, len(c.Databases))
|
|
seenNames := make(map[string]struct{}, len(c.Databases))
|
|
for _, database := range c.Databases {
|
|
name := strings.TrimSpace(database.Name)
|
|
if name == "" {
|
|
errs = append(errs, errors.New("database name is required"))
|
|
} else if _, exists := seenNames[name]; exists {
|
|
errs = append(errs, fmt.Errorf("duplicate database name %q", name))
|
|
} else {
|
|
seenNames[name] = struct{}{}
|
|
}
|
|
hostname := NormalizeHostname(database.Hostname)
|
|
if hostname != "" {
|
|
if existing, ok := seenHostnames[hostname]; ok {
|
|
errs = append(errs, fmt.Errorf("duplicate hostname %q for databases %q and %q", hostname, existing, name))
|
|
} else {
|
|
seenHostnames[hostname] = name
|
|
}
|
|
}
|
|
if database.Postgres != nil {
|
|
if database.Upstream != "" {
|
|
errs = append(errs, fmt.Errorf("database %q: upstream and postgres cannot both be configured", name))
|
|
}
|
|
if err := validatePostgres(*database.Postgres); err != nil {
|
|
errs = append(errs, fmt.Errorf("database %q: postgres: %w", name, err))
|
|
}
|
|
} else if strings.TrimSpace(database.Upstream) == "" {
|
|
errs = append(errs, fmt.Errorf("database %q: upstream or postgres is required", name))
|
|
} else if err := validateAddress(database.Upstream); err != nil {
|
|
errs = append(errs, fmt.Errorf("database %q: upstream %q: %w", name, database.Upstream, err))
|
|
}
|
|
}
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
func validatePostgres(postgres PostgresConfig) error {
|
|
switch postgres.Image {
|
|
case "postgres:16", "postgres:17", "postgres:18":
|
|
default:
|
|
return fmt.Errorf("image %q must be postgres:16, postgres:17, or postgres:18", postgres.Image)
|
|
}
|
|
if strings.TrimSpace(postgres.Volume) == "" {
|
|
return errors.New("volume is required")
|
|
}
|
|
if strings.TrimSpace(postgres.PasswordEnv) == "" {
|
|
return errors.New("password_env is required")
|
|
}
|
|
if postgres.IdleTimeout < 0 {
|
|
return errors.New("idle_timeout cannot be negative")
|
|
}
|
|
if postgres.TrafficIdleTimeout < 0 {
|
|
return errors.New("traffic_idle_timeout cannot be negative")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateListenAddress(address string) error {
|
|
_, port, err := net.SplitHostPort(address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
portNumber, err := strconv.ParseUint(port, 10, 16)
|
|
if err != nil || portNumber == 0 {
|
|
return errors.New("port must be between 1 and 65535")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateAddress(address string) error {
|
|
host, port, err := net.SplitHostPort(address)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(host) == "" {
|
|
return errors.New("host is required")
|
|
}
|
|
portNumber, err := strconv.ParseUint(port, 10, 16)
|
|
if err != nil || portNumber == 0 {
|
|
return errors.New("port must be between 1 and 65535")
|
|
}
|
|
return nil
|
|
}
|