package traefik import ( "errors" "fmt" "reflect" ) // ErrConflict meldet zwei unterschiedliche Definitionen unter demselben Namen. var ErrConflict = errors.New("Namenskonflikt") // Merge führt die Konfigurationen mehrerer Knoten zu einer zusammen. // nil-Einträge werden übersprungen. Identische Definitionen unter demselben // Namen sind erlaubt, unterschiedliche ergeben ErrConflict; das Vergeben // eindeutiger Namen (z. B. pro Mandant/Knoten) ist Aufgabe des Aufrufers. // Die Eingaben werden nicht verändert, das Ergebnis teilt sich aber die // Router-, Service- und Middleware-Objekte mit ihnen. func Merge(configs ...*Configuration) (*Configuration, error) { out := &HTTPConfiguration{ Routers: map[string]*Router{}, Services: map[string]*Service{}, Middlewares: map[string]Middleware{}, } var errs []error for _, c := range configs { if c == nil || c.HTTP == nil { continue } errs = append(errs, mergeInto(out.Routers, c.HTTP.Routers, "router")...) errs = append(errs, mergeInto(out.Services, c.HTTP.Services, "service")...) errs = append(errs, mergeInto(out.Middlewares, c.HTTP.Middlewares, "middleware")...) } if len(errs) > 0 { return nil, errors.Join(errs...) } if len(out.Routers) == 0 && len(out.Services) == 0 && len(out.Middlewares) == 0 { return &Configuration{}, nil } return &Configuration{HTTP: out}, nil } func mergeInto[V any](dst, src map[string]V, kind string) []error { var errs []error for name, v := range src { existing, ok := dst[name] if !ok { dst[name] = v continue } if !reflect.DeepEqual(existing, v) { errs = append(errs, fmt.Errorf("%s %q: %w", kind, name, ErrConflict)) } } return errs }