Files

164 lines
5.5 KiB
Go

// 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)
}
}