// Package traefik erzeugt die dynamische Traefik-Konfiguration, die der // Sultan-Server über den HTTP-Provider (GET /v1/traefik-config) ausliefert. // // Die Typen bilden die Teilmenge von Traefiks "dynamic configuration" ab, // die Sultan aus Docker-Labels bzw. /etc/sultan.d-Dateien erzeugt. Die // Ausgabe ist JSON oder YAML; Traefiks HTTP-Provider akzeptiert beides. package traefik import ( "bytes" "encoding/json" "fmt" "strings" "gopkg.in/yaml.v3" ) // Configuration ist die Wurzel der dynamischen Traefik-Konfiguration. type Configuration struct { HTTP *HTTPConfiguration `json:"http,omitempty" yaml:"http,omitempty"` TCP *TCPConfiguration `json:"tcp,omitempty" yaml:"tcp,omitempty"` } // HTTPConfiguration enthält Router, Services und Middlewares der HTTP-Ebene. type HTTPConfiguration struct { Routers map[string]*Router `json:"routers,omitempty" yaml:"routers,omitempty"` Services map[string]*Service `json:"services,omitempty" yaml:"services,omitempty"` Middlewares map[string]Middleware `json:"middlewares,omitempty" yaml:"middlewares,omitempty"` } // Router entspricht einem Traefik-HTTP-Router. type Router struct { EntryPoints []string `json:"entryPoints,omitempty" yaml:"entryPoints,omitempty"` Middlewares []string `json:"middlewares,omitempty" yaml:"middlewares,omitempty"` Service string `json:"service" yaml:"service"` Rule string `json:"rule" yaml:"rule"` Priority int `json:"priority,omitempty" yaml:"priority,omitempty"` TLS *RouterTLS `json:"tls,omitempty" yaml:"tls,omitempty"` } // RouterTLS aktiviert TLS für einen Router. Ein leeres, nicht-nil RouterTLS // wird als "tls: {}" gerendert und schaltet TLS mit Standardwerten ein. type RouterTLS struct { Options string `json:"options,omitempty" yaml:"options,omitempty"` CertResolver string `json:"certResolver,omitempty" yaml:"certResolver,omitempty"` } // Service entspricht einem Traefik-HTTP-Service. type Service struct { LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` } // ServersLoadBalancer verteilt Anfragen auf die angegebenen Server. type ServersLoadBalancer struct { Servers []Server `json:"servers" yaml:"servers"` PassHostHeader *bool `json:"passHostHeader,omitempty" yaml:"passHostHeader,omitempty"` } // Server ist ein einzelnes Backend-Ziel. type Server struct { URL string `json:"url" yaml:"url"` } // TCPConfiguration enthält Router und Services der TCP-Ebene. Sultan nutzt // sie nur für TLS-Passthrough (Routing per SNI, TLS endet beim Backend). type TCPConfiguration struct { Routers map[string]*TCPRouter `json:"routers,omitempty" yaml:"routers,omitempty"` Services map[string]*TCPService `json:"services,omitempty" yaml:"services,omitempty"` } // TCPRouter entspricht einem Traefik-TCP-Router. type TCPRouter struct { EntryPoints []string `json:"entryPoints,omitempty" yaml:"entryPoints,omitempty"` Service string `json:"service" yaml:"service"` Rule string `json:"rule" yaml:"rule"` Priority int `json:"priority,omitempty" yaml:"priority,omitempty"` TLS *TCPRouterTLS `json:"tls,omitempty" yaml:"tls,omitempty"` } // TCPRouterTLS aktiviert TLS für einen TCP-Router; Passthrough reicht die // Verbindung unverändert an das Backend durch. type TCPRouterTLS struct { Passthrough bool `json:"passthrough,omitempty" yaml:"passthrough,omitempty"` } // TCPService entspricht einem Traefik-TCP-Service. type TCPService struct { LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` } // TCPServersLoadBalancer verteilt Verbindungen auf die angegebenen Server. type TCPServersLoadBalancer struct { Servers []TCPServer `json:"servers" yaml:"servers"` } // TCPServer ist ein einzelnes TCP-Backend (host:port). type TCPServer struct { Address string `json:"address" yaml:"address"` } // Middleware ist eine Traefik-Middleware als verschachtelte Map, z. B. // {"redirectRegex": {"regex": "...", "permanent": "true"}}. // // Die Blattwerte bleiben die unveränderten Label-Strings. Traefik wandelt sie // beim Einlesen genauso in Bool-, Zahl- und Listenwerte um wie Labels // (Listen kommagetrennt), deshalb braucht Sultan nicht jeden Middleware-Typ zu // kennen. type Middleware map[string]any // Format ist das Ausgabeformat der Konfiguration. type Format string const ( FormatJSON Format = "json" FormatYAML Format = "yaml" ) // ParseFormat wandelt z. B. einen Query-Parameter in ein Format um. // Ein leerer String ergibt FormatJSON. func ParseFormat(s string) (Format, error) { switch strings.ToLower(strings.TrimSpace(s)) { case "", "json": return FormatJSON, nil case "yaml", "yml": return FormatYAML, nil default: return "", fmt.Errorf("traefik: unbekanntes Format %q", s) } } // ContentType liefert den passenden HTTP-Content-Type. func (f Format) ContentType() string { if f == FormatYAML { return "application/yaml" } return "application/json" } // Marshal rendert die Konfiguration im angegebenen Format. Eine nil- // Konfiguration ergibt ein leeres Objekt, das Traefik als "keine Routen" // akzeptiert. func (c *Configuration) Marshal(f Format) ([]byte, error) { if c == nil { c = &Configuration{} } switch f { case FormatJSON: return json.Marshal(c) case FormatYAML: var buf bytes.Buffer enc := yaml.NewEncoder(&buf) enc.SetIndent(2) if err := enc.Encode(c); err != nil { return nil, err } if err := enc.Close(); err != nil { return nil, err } return buf.Bytes(), nil default: return nil, fmt.Errorf("traefik: unbekanntes Format %q", f) } }