diff --git a/internal/gatewayapi/listener_test.go b/internal/gatewayapi/listener_test.go index 40094dfa6b..8fa4073061 100644 --- a/internal/gatewayapi/listener_test.go +++ b/internal/gatewayapi/listener_test.go @@ -1379,7 +1379,7 @@ func TestProcessBackendRefsBackendTLSPolicy(t *testing.T) { }, } backendEndpoints := []*ir.DestinationEndpoint{{Host: "otel.example.com", Port: 443}} - backendMetadata := &ir.ResourceMetadata{Name: backendName, Namespace: ns} + backendMetadata := &ir.ResourceMetadata{Kind: resource.KindBackend, Name: backendName, Namespace: ns} backendPolicyTLS := &ir.TLSUpstreamConfig{ SNI: new("otel.example.com"), UseSystemTrustStore: true, CACertificate: &ir.TLSCACertificate{Name: "otel-tls/test-ns-ca"}, SubjectAltNames: []ir.SubjectAltName{}, @@ -1417,7 +1417,7 @@ func TestProcessBackendRefsBackendTLSPolicy(t *testing.T) { }, } serviceEndpoints := []*ir.DestinationEndpoint{{Host: "7.7.7.7", Port: 4317}} - serviceMetadata := &ir.ResourceMetadata{Name: serviceName, Namespace: ns, SectionName: "4317"} + serviceMetadata := &ir.ResourceMetadata{Kind: resource.KindService, Name: serviceName, Namespace: ns, SectionName: "4317"} servicePolicyTLS := &ir.TLSUpstreamConfig{ SNI: new("otel-svc.example.com"), UseSystemTrustStore: true, CACertificate: &ir.TLSCACertificate{Name: "otel-svc-tls/test-ns-ca"}, SubjectAltNames: []ir.SubjectAltName{}, diff --git a/internal/gatewayapi/overlap_test.go b/internal/gatewayapi/overlap_test.go new file mode 100644 index 0000000000..0a7a588507 --- /dev/null +++ b/internal/gatewayapi/overlap_test.go @@ -0,0 +1,427 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package gatewayapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" + "github.com/envoyproxy/gateway/internal/gatewayapi/status" + "github.com/envoyproxy/gateway/internal/ir" +) + +// routesOverlap is a test helper that returns true if two routes produce the +// same canonical overlap key (i.e., they match the same set of requests). +func routesOverlap(a, b *ir.HTTPRoute) bool { + return buildOverlapKey(a) == buildOverlapKey(b) +} + +func TestBuildOverlapKey(t *testing.T) { + tests := []struct { + name string + a *ir.HTTPRoute + b *ir.HTTPRoute + overlap bool + }{ + { + name: "identical exact path and hostname", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + overlap: true, + }, + { + name: "different exact paths", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/bar")}, + }, + overlap: false, + }, + { + name: "different hostnames", + a: &ir.HTTPRoute{ + Hostname: "a.example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + b: &ir.HTTPRoute{ + Hostname: "b.example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + overlap: false, + }, + { + name: "identical prefix paths are detected as overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Prefix: new("/api")}, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Prefix: new("/api")}, + }, + overlap: true, + }, + { + name: "nil path matches are detected as overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + }, + overlap: true, + }, + { + name: "nil path and root prefix path are detected as overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Prefix: new("/")}, + }, + overlap: true, + }, + { + name: "path prefixes with equivalent trailing slash are detected as overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Prefix: new("/api")}, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Prefix: new("/api/")}, + }, + overlap: true, + }, + { + name: "exact vs prefix same value not overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Prefix: new("/foo")}, + }, + overlap: false, + }, + { + name: "identical exact path with identical header matches", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-Custom", Exact: new("val1")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-Custom", Exact: new("val1")}, + }, + }, + overlap: true, + }, + { + name: "identical exact path with different header matches", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-Custom", Exact: new("val1")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-Custom", Exact: new("val2")}, + }, + }, + overlap: false, + }, + { + name: "identical exact path one has headers other does not", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-Custom", Exact: new("val1")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + }, + overlap: false, + }, + { + name: "header names are compared case-insensitively", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-Custom", Exact: new("val1")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "x-custom", Exact: new("val1")}, + }, + }, + overlap: true, + }, + { + name: "header matches in different order still overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-A", Exact: new("1")}, + {Name: "X-B", Exact: new("2")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + HeaderMatches: []*ir.StringMatch{ + {Name: "X-B", Exact: new("2")}, + {Name: "X-A", Exact: new("1")}, + }, + }, + overlap: true, + }, + { + name: "identical exact path with identical query param matches", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + QueryParamMatches: []*ir.StringMatch{ + {Name: "key", Exact: new("value")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{Exact: new("/foo")}, + QueryParamMatches: []*ir.StringMatch{ + {Name: "key", Exact: new("value")}, + }, + }, + overlap: true, + }, + { + name: "query param matches in different order still overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + QueryParamMatches: []*ir.StringMatch{ + {Name: "a", Exact: new("1")}, + {Name: "b", Exact: new("2")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + QueryParamMatches: []*ir.StringMatch{ + {Name: "b", Exact: new("2")}, + {Name: "a", Exact: new("1")}, + }, + }, + overlap: true, + }, + { + name: "cookie matches in different order still overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + CookieMatches: []*ir.StringMatch{ + {Name: "a", Exact: new("1")}, + {Name: "b", Exact: new("2")}, + }, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + CookieMatches: []*ir.StringMatch{ + {Name: "b", Exact: new("2")}, + {Name: "a", Exact: new("1")}, + }, + }, + overlap: true, + }, + { + name: "identical regex path matches are detected as overlap", + a: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{SafeRegex: new("/foo.*")}, + }, + b: &ir.HTTPRoute{ + Hostname: "example.com", + PathMatch: &ir.StringMatch{SafeRegex: new("/foo.*")}, + }, + overlap: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.overlap, routesOverlap(tt.a, tt.b)) + }) + } +} + +func TestBuildResourceMetadataWrappedRouteKinds(t *testing.T) { + tests := []struct { + name string + obj client.Object + want string + }{ + { + name: "http route context", + obj: &HTTPRouteContext{ + HTTPRoute: &gwapiv1.HTTPRoute{}, + }, + want: resource.KindHTTPRoute, + }, + { + name: "grpc route context", + obj: &GRPCRouteContext{ + GRPCRoute: &gwapiv1.GRPCRoute{}, + }, + want: resource.KindGRPCRoute, + }, + { + name: "tls route context", + obj: &TLSRouteContext{ + TLSRoute: &gwapiv1.TLSRoute{}, + }, + want: resource.KindTLSRoute, + }, + { + name: "tcp route context", + obj: &TCPRouteContext{ + TCPRoute: &gwapiv1a2.TCPRoute{}, + }, + want: resource.KindTCPRoute, + }, + { + name: "udp route context", + obj: &UDPRouteContext{ + UDPRoute: &gwapiv1a2.UDPRoute{}, + }, + want: resource.KindUDPRoute, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + metadata := buildResourceMetadata(tt.obj, nil) + require.Equal(t, tt.want, metadata.Kind) + }) + } +} + +func TestCheckRouteOverlapsRemovesStaleCondition(t *testing.T) { + parentRef := gwapiv1.ParentReference{Name: "gateway-1"} + httpRoute := &gwapiv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "route-1", + Namespace: "default", + }, + Status: gwapiv1.HTTPRouteStatus{ + RouteStatus: gwapiv1.RouteStatus{ + Parents: []gwapiv1.RouteParentStatus{ + { + ParentRef: parentRef, + Conditions: []metav1.Condition{ + { + Type: string(gwapiv1.RouteConditionAccepted), + Status: metav1.ConditionTrue, + Reason: string(gwapiv1.RouteReasonAccepted), + }, + { + Type: string(status.RouteConditionRouteRulesOverlap), + Status: metav1.ConditionTrue, + Reason: string(status.RouteReasonRouteRulesOverlap), + }, + }, + }, + }, + }, + }, + } + route := &HTTPRouteContext{ + HTTPRoute: httpRoute, + } + + gateway := &GatewayContext{ + Gateway: &gwapiv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-1", + Namespace: "default", + }, + }, + } + listener := &ListenerContext{ + Listener: &gwapiv1.Listener{Name: "http"}, + gateway: gateway, + } + route.ParentRefs = map[gwapiv1.ParentReference]*RouteParentContext{ + parentRef: { + ParentReference: &parentRef, + HTTPRoute: httpRoute, + routeParentStatusIdx: 0, + listeners: []*ListenerContext{listener}, + }, + } + + xdsIR := resource.XdsIRMap{ + "default/gateway-1": { + HTTP: []*ir.HTTPListener{ + { + CoreListenerDetails: ir.CoreListenerDetails{ + Name: irListenerName(listener), + }, + Routes: []*ir.HTTPRoute{ + { + Hostname: "example.com", + Metadata: &ir.ResourceMetadata{ + Kind: resource.KindHTTPRoute, + Namespace: "default", + Name: "route-1", + }, + }, + }, + }, + }, + }, + } + + translator := &Translator{} + translator.checkRouteOverlaps([]*HTTPRouteContext{route}, nil, xdsIR) + + conditions := route.Status.RouteStatus.Parents[0].Conditions + require.NotNil(t, meta.FindStatusCondition(conditions, string(gwapiv1.RouteConditionAccepted))) + assert.Nil(t, meta.FindStatusCondition(conditions, string(status.RouteConditionRouteRulesOverlap))) +} diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 4c86bcc38c..1b0aecfc61 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "net" + "sort" "strconv" "strings" "time" @@ -1260,6 +1261,7 @@ func (t *Translator) processGRPCRouteMethodRegularExpression(method *gwapiv1.GRP func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, routeRoutes []*ir.HTTPRoute, parentRef *RouteParentContext, xdsIR resource.XdsIRMap) bool { // need to check hostname intersection if there are listeners hasHostnameIntersection := len(parentRef.listeners) == 0 + for _, listener := range parentRef.listeners { hosts := computeHosts(GetHostnames(route), listener) if len(hosts) == 0 { @@ -1319,6 +1321,7 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route irListener.GRPC.EnableGRPCStats = new(true) } } + irListener.Routes = append(irListener.Routes, perHostRoutes...) } } @@ -1326,12 +1329,254 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route return hasHostnameIntersection } -func buildResourceMetadata(resource client.Object, sectionName *gwapiv1.SectionName) *ir.ResourceMetadata { +// routeKey returns a "kind/namespace/name" key for a route resource. +// Kind is included so HTTPRoute and GRPCRoute with the same namespace/name +// do not collide in the route lookup. +func routeKey(kind, namespace, name string) string { + return kind + "/" + namespace + "/" + name +} + +// routeDisplayNameFromKey converts a routeKey ("Kind/namespace/name") into a +// human-readable form ("Kind namespace/name") for user-facing status messages. +func routeDisplayNameFromKey(key string) string { + kind, nsName, ok := strings.Cut(key, "/") + if !ok { + return key + } + return kind + " " + nsName +} + +// overlapKey is a canonical representation of a route's match conditions. +// Routes sharing the same overlapKey within a listener match the exact same +// set of requests and are therefore considered overlapping. +type overlapKey struct { + hostname string + path string + headers string + query string + cookies string +} + +// checkRouteOverlaps detects overlapping route matches across all IR listeners +// and sets a warning Overlap condition on the affected HTTPRoutes and GRPCRoutes. +func (t *Translator) checkRouteOverlaps(httpRoutes []*HTTPRouteContext, grpcRoutes []*GRPCRouteContext, xdsIR resource.XdsIRMap) { + // Build a combined lookup from "kind/namespace/name" to RouteContext and its ParentRefs. + type routeInfo struct { + route RouteContext + parentRefs map[gwapiv1.ParentReference]*RouteParentContext + } + routeByKey := make(map[string]*routeInfo, len(httpRoutes)+len(grpcRoutes)) + for _, hr := range httpRoutes { + routeByKey[routeKey(string(hr.GetRouteType()), hr.GetNamespace(), hr.GetName())] = &routeInfo{route: hr, parentRefs: hr.ParentRefs} + } + for _, gr := range grpcRoutes { + routeByKey[routeKey(string(gr.GetRouteType()), gr.GetNamespace(), gr.GetName())] = &routeInfo{route: gr, parentRefs: gr.ParentRefs} + } + + // overlaps tracks per IR listener which routes overlap with which others. + // Key: IR listener name -> route key -> set of conflicting route keys. + type listenerOverlaps map[string]map[string]struct{} + overlaps := make(map[string]listenerOverlaps) + + for _, xds := range xdsIR { + for _, httpListener := range xds.HTTP { + // Bucket routes by their canonical overlap key. Any bucket with + // more than one distinct route contains overlapping routes. + buckets := make(map[overlapKey]map[string]struct{}) + for _, r := range httpListener.Routes { + if r.Metadata == nil { + continue + } + rKey := routeKey(r.Metadata.Kind, r.Metadata.Namespace, r.Metadata.Name) + k := buildOverlapKey(r) + if buckets[k] == nil { + buckets[k] = make(map[string]struct{}) + } + buckets[k][rKey] = struct{}{} + } + for _, routeKeys := range buckets { + if len(routeKeys) < 2 { + continue + } + if overlaps[httpListener.Name] == nil { + overlaps[httpListener.Name] = make(listenerOverlaps) + } + lo := overlaps[httpListener.Name] + for ki := range routeKeys { + for kj := range routeKeys { + if ki == kj { + continue + } + if lo[ki] == nil { + lo[ki] = make(map[string]struct{}) + } + lo[ki][kj] = struct{}{} + } + } + } + } + } + + // Pre-build listener name lookup to avoid repeated linear scans via GetHTTPListener. + listenerByName := make(map[string]*ir.HTTPListener) + for _, xds := range xdsIR { + for _, hl := range xds.HTTP { + listenerByName[hl.Name] = hl + } + } + + // Set the Overlap warning condition only on parentRefs whose listeners + // match an IR listener where the overlap was detected. + for rKey, info := range routeByKey { + routeStatus := GetRouteStatus(info.route) + // Collect all conflicts for this route across the parentRefs that have overlaps. + for _, parentRef := range info.parentRefs { + var conflicts map[string]struct{} + for _, listener := range parentRef.listeners { + irListener := listenerByName[irListenerName(listener)] + if irListener == nil { + continue + } + lo, ok := overlaps[irListener.Name] + if !ok { + continue + } + routeConflicts, ok := lo[rKey] + if !ok { + continue + } + if conflicts == nil { + conflicts = make(map[string]struct{}) + } + for c := range routeConflicts { + conflicts[c] = struct{}{} + } + } + if len(conflicts) == 0 { + status.RemoveRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, status.RouteConditionRouteRulesOverlap) + continue + } + + conflictNames := make([]string, 0, len(conflicts)) + for name := range conflicts { + conflictNames = append(conflictNames, routeDisplayNameFromKey(name)) + } + sort.Strings(conflictNames) + + msg := fmt.Sprintf("Overlapping match conditions with route(s): %s", strings.Join(conflictNames, ", ")) + + status.SetRouteStatusCondition(routeStatus, + parentRef.routeParentStatusIdx, + info.route.GetGeneration(), + status.RouteConditionRouteRulesOverlap, + metav1.ConditionTrue, + status.RouteReasonRouteRulesOverlap, + msg, + ) + } + } +} + +// buildOverlapKey returns a canonical key capturing a route's match conditions. +// Two routes with the same overlapKey match the exact same set of requests. +// Header names are normalized to lowercase since HTTP header names are +// case-insensitive, and slice-valued matches (headers, query params, cookies) +// are sorted so that ordering does not affect equality. +func buildOverlapKey(r *ir.HTTPRoute) overlapKey { + return overlapKey{ + hostname: r.Hostname, + path: pathMatchKey(r.PathMatch), + headers: stringMatchSliceKey(r.HeaderMatches, true), + query: stringMatchSliceKey(r.QueryParamMatches, false), + cookies: stringMatchSliceKey(r.CookieMatches, false), + } +} + +// pathMatchKey serializes route path matches the same way they are interpreted +// by the xDS translator: no path match is equivalent to prefix "/", and +// non-root prefixes have one trailing slash trimmed before translation. +func pathMatchKey(s *ir.StringMatch) string { + if s == nil { + return stringMatchKey(&ir.StringMatch{Prefix: new("/")}, false) + } + if s.Prefix == nil || *s.Prefix == "/" { + return stringMatchKey(s, false) + } + + normalized := s.DeepCopy() + normalized.Prefix = new(strings.TrimSuffix(*s.Prefix, "/")) + return stringMatchKey(normalized, false) +} + +// stringMatchKey serializes a StringMatch into a canonical string. +// When lowercaseName is true, the Name field is normalized to lowercase. +func stringMatchKey(s *ir.StringMatch, lowercaseName bool) string { + if s == nil { + return "" + } + name := s.Name + if lowercaseName { + name = strings.ToLower(name) + } + var b strings.Builder + b.WriteString(name) + b.WriteByte('\x00') + if s.Exact != nil { + b.WriteByte('e') + b.WriteString(*s.Exact) + } + b.WriteByte('\x00') + if s.Prefix != nil { + b.WriteByte('p') + b.WriteString(*s.Prefix) + } + b.WriteByte('\x00') + if s.Suffix != nil { + b.WriteByte('s') + b.WriteString(*s.Suffix) + } + b.WriteByte('\x00') + if s.SafeRegex != nil { + b.WriteByte('r') + b.WriteString(*s.SafeRegex) + } + b.WriteByte('\x00') + if s.Distinct { + b.WriteByte('d') + } + b.WriteByte('\x00') + if s.Invert != nil && *s.Invert { + b.WriteByte('i') + } + return b.String() +} + +// stringMatchSliceKey serializes a slice of StringMatch into a canonical string +// that is independent of element order. +func stringMatchSliceKey(s []*ir.StringMatch, lowercaseName bool) string { + if len(s) == 0 { + return "" + } + keys := make([]string, len(s)) + for i, m := range s { + keys[i] = stringMatchKey(m, lowercaseName) + } + sort.Strings(keys) + return strings.Join(keys, "\x01") +} + +func buildResourceMetadata(obj client.Object, sectionName *gwapiv1.SectionName) *ir.ResourceMetadata { + kind := obj.GetObjectKind().GroupVersionKind().Kind + if kind == "" { + // Typed objects fetched via controller-runtime clients have an empty + // TypeMeta; fall back to a type-based lookup so Kind stays reliable. + kind = kindForObject(obj) + } metadata := &ir.ResourceMetadata{ - Kind: resource.GetObjectKind().GroupVersionKind().Kind, - Name: resource.GetName(), - Namespace: resource.GetNamespace(), - Annotations: ir.MapToSlice(filterEGPrefix(resource.GetAnnotations())), + Kind: kind, + Name: obj.GetName(), + Namespace: obj.GetNamespace(), + Annotations: ir.MapToSlice(filterEGPrefix(obj.GetAnnotations())), } if sectionName != nil { metadata.SectionName = string(*sectionName) @@ -1339,6 +1584,28 @@ func buildResourceMetadata(resource client.Object, sectionName *gwapiv1.SectionN return metadata } +// kindForObject returns the Kind string for a known Gateway API or Kubernetes +// object type. Returns an empty string for unknown types. +func kindForObject(obj client.Object) string { + // Route wrapper types (HTTPRouteContext, GRPCRouteContext, etc.) report + // their Kind via the RouteContext interface; the switch below matches the + // raw types only. + if r, ok := obj.(RouteContext); ok { + return string(r.GetRouteType()) + } + switch obj.(type) { + case *gwapiv1.Gateway: + return resource.KindGateway + case *corev1.Service: + return resource.KindService + case *mcsapiv1a1.ServiceImport: + return resource.KindServiceImport + case *egv1a1.Backend: + return resource.KindBackend + } + return "" +} + func filterEGPrefix(in map[string]string) map[string]string { if len(in) == 0 { return nil diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 57b712fb8e..355e51bb17 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -31,6 +31,10 @@ const ( RouteReasonInvalidAddress gwapiv1.RouteConditionReason = "InvalidAddress" RouteReasonEndpointsNotFound gwapiv1.RouteConditionReason = "EndpointsNotFound" + // Route overlap reason and condition type + RouteReasonRouteRulesOverlap gwapiv1.RouteConditionReason = "RouteRulesOverlap" + RouteConditionRouteRulesOverlap gwapiv1.RouteConditionType = "RouteRulesOverlap" + // Network configuration related condition types RouteConditionBackendsAvailable gwapiv1.RouteConditionType = "BackendsAvailable" ) diff --git a/internal/gatewayapi/status/route.go b/internal/gatewayapi/status/route.go index 4b8f6bc743..96eedf6ff4 100644 --- a/internal/gatewayapi/status/route.go +++ b/internal/gatewayapi/status/route.go @@ -32,6 +32,17 @@ func SetRouteStatusCondition(route *gwapiv1.RouteStatus, routeParentStatusIdx in route.Parents[routeParentStatusIdx].Conditions = MergeConditions(route.Parents[routeParentStatusIdx].Conditions, cond) } +// RemoveRouteStatusCondition removes conditionType from the given parent's +// Conditions if present. The pre-check avoids meta.RemoveStatusCondition's +// unconditional slice reallocation when the condition is absent. +func RemoveRouteStatusCondition(route *gwapiv1.RouteStatus, routeParentStatusIdx int, conditionType gwapiv1.RouteConditionType) { + conds := &route.Parents[routeParentStatusIdx].Conditions + if meta.FindStatusCondition(*conds, string(conditionType)) == nil { + return + } + meta.RemoveStatusCondition(conds, string(conditionType)) +} + // TruncateRouteParents trims RouteStatus.Parents down to at most 32 entries. // The first 31 parents are shown as-is. // The last 32nd parent is shown as-is and gets the Aggregated condition. diff --git a/internal/gatewayapi/testdata/backend-tls-settings-invalid.out.yaml b/internal/gatewayapi/testdata/backend-tls-settings-invalid.out.yaml index ca0322916a..32fff4a7b7 100644 --- a/internal/gatewayapi/testdata/backend-tls-settings-invalid.out.yaml +++ b/internal/gatewayapi/testdata/backend-tls-settings-invalid.out.yaml @@ -150,6 +150,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -184,6 +189,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/backend-tls-settings.out.yaml b/internal/gatewayapi/testdata/backend-tls-settings.out.yaml index dee70c0bd8..45587f893d 100644 --- a/internal/gatewayapi/testdata/backend-tls-settings.out.yaml +++ b/internal/gatewayapi/testdata/backend-tls-settings.out.yaml @@ -431,6 +431,13 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2, + HTTPRoute default/httproute-3, HTTPRoute default/httproute-4, HTTPRoute + default/httproute-5, HTTPRoute default/httproute-6, HTTPRoute envoy-gateway/httproute-7-override-client-tls-settings' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -465,6 +472,13 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1, + HTTPRoute default/httproute-3, HTTPRoute default/httproute-4, HTTPRoute + default/httproute-5, HTTPRoute default/httproute-6, HTTPRoute envoy-gateway/httproute-7-override-client-tls-settings' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -499,6 +513,13 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1, + HTTPRoute default/httproute-2, HTTPRoute default/httproute-4, HTTPRoute + default/httproute-5, HTTPRoute default/httproute-6, HTTPRoute envoy-gateway/httproute-7-override-client-tls-settings' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -534,6 +555,13 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1, + HTTPRoute default/httproute-2, HTTPRoute default/httproute-3, HTTPRoute + default/httproute-5, HTTPRoute default/httproute-6, HTTPRoute envoy-gateway/httproute-7-override-client-tls-settings' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -570,6 +598,13 @@ httpRoutes: reason: InvalidBackendRef status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1, + HTTPRoute default/httproute-2, HTTPRoute default/httproute-3, HTTPRoute + default/httproute-4, HTTPRoute default/httproute-6, HTTPRoute envoy-gateway/httproute-7-override-client-tls-settings' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -607,6 +642,13 @@ httpRoutes: reason: InvalidBackendRef status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1, + HTTPRoute default/httproute-2, HTTPRoute default/httproute-3, HTTPRoute + default/httproute-4, HTTPRoute default/httproute-5, HTTPRoute envoy-gateway/httproute-7-override-client-tls-settings' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -642,6 +684,13 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1, + HTTPRoute default/httproute-2, HTTPRoute default/httproute-3, HTTPRoute + default/httproute-4, HTTPRoute default/httproute-5, HTTPRoute default/httproute-6' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/backendtlspolicy-status-conditions-truncated.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-status-conditions-truncated.out.yaml index cc46cac2e1..ffd1516a46 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-status-conditions-truncated.out.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-status-conditions-truncated.out.yaml @@ -1296,6 +1296,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -1311,6 +1316,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-2 @@ -1326,6 +1336,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-3 @@ -1341,6 +1356,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-4 @@ -1356,6 +1376,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-5 @@ -1371,6 +1396,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-6 @@ -1386,6 +1416,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-7 @@ -1401,6 +1436,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-8 @@ -1416,6 +1456,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-9 @@ -1431,6 +1476,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-10 @@ -1446,6 +1496,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-11 @@ -1461,6 +1516,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-12 @@ -1476,6 +1536,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-13 @@ -1491,6 +1556,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-14 @@ -1506,6 +1576,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-15 @@ -1521,6 +1596,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-16 @@ -1536,6 +1616,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-17 @@ -1551,6 +1636,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-18 @@ -1619,6 +1709,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -1635,6 +1730,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-2 @@ -1651,6 +1751,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-3 @@ -1667,6 +1772,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-4 @@ -1683,6 +1793,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-5 @@ -1699,6 +1814,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-6 @@ -1715,6 +1835,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-7 @@ -1731,6 +1856,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-8 @@ -1747,6 +1877,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-9 @@ -1763,6 +1898,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-10 @@ -1779,6 +1919,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-11 @@ -1795,6 +1940,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-12 @@ -1811,6 +1961,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-13 @@ -1827,6 +1982,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-14 @@ -1843,6 +2003,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-15 @@ -1859,6 +2024,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-16 @@ -1875,6 +2045,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-17 @@ -1891,6 +2066,11 @@ httpRoutes: reason: InvalidBackendTLS status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-18 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml index a1c8a4e287..295b453b74 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-multiple-targets-targetselector.out.yaml @@ -101,6 +101,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/httproute' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -141,6 +146,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-section.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-section.out.yaml index 57af373649..6632dbf155 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-section.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-section.out.yaml @@ -408,6 +408,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -442,6 +447,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck-overrides.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck-overrides.out.yaml index f6b26324dc..356465b0bb 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck-overrides.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck-overrides.out.yaml @@ -229,6 +229,11 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-with-no-overrides' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway @@ -261,6 +266,11 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-with-overrides' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml index a1480de5e0..335bb16280 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml @@ -505,6 +505,12 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-2, + GRPCRoute default/grpcroute-3' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -537,6 +543,12 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-1, + GRPCRoute default/grpcroute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -569,6 +581,12 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-1, + GRPCRoute default/grpcroute-3' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -644,6 +662,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-4' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-2 @@ -718,6 +741,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-2 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml index 0545d716c1..952b440541 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml @@ -147,6 +147,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -186,6 +191,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml index 429804c25b..caadc5c01c 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml @@ -379,6 +379,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-3' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-2 @@ -416,6 +421,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-2 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml index 7e1b42ffd4..99d036108e 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-same-prefix-httproutes.out.yaml @@ -106,6 +106,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -143,6 +148,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-stats.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-stats.out.yaml index 72bfc454a5..daa825e67c 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-stats.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-stats.out.yaml @@ -210,6 +210,11 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -242,6 +247,11 @@ grpcRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): GRPCRoute default/grpcroute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml index dcee0c7609..a0a1abbddd 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-invalid-settings.out.yaml @@ -717,6 +717,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -749,6 +754,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml index 2312844481..7bef534a60 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-section-name-override.out.yaml @@ -264,6 +264,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -301,6 +306,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-with-invalid-lua-validation-syntax.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-with-invalid-lua-validation-syntax.out.yaml index a8c02a1853..6fe81ab6a2 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-with-invalid-lua-validation-syntax.out.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-with-invalid-lua-validation-syntax.out.yaml @@ -164,6 +164,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -201,6 +206,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/gateway-with-attached-routes.out.yaml b/internal/gatewayapi/testdata/gateway-with-attached-routes.out.yaml index c2ac20067f..1ee7dbbece 100644 --- a/internal/gatewayapi/testdata/gateway-with-attached-routes.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-attached-routes.out.yaml @@ -178,6 +178,11 @@ httpRoutes: reason: BackendNotFound status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/http-route-3' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: kind: Gateway @@ -211,6 +216,11 @@ httpRoutes: reason: BackendNotFound status: "False" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute envoy-gateway/http-route-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: kind: Gateway diff --git a/internal/gatewayapi/testdata/httproute-dynamic-resolver.out.yaml b/internal/gatewayapi/testdata/httproute-dynamic-resolver.out.yaml index 117afe3269..e71694ca67 100644 --- a/internal/gatewayapi/testdata/httproute-dynamic-resolver.out.yaml +++ b/internal/gatewayapi/testdata/httproute-dynamic-resolver.out.yaml @@ -107,6 +107,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -142,6 +147,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/httproute-overlapping-matches.in.yaml b/internal/gatewayapi/testdata/httproute-overlapping-matches.in.yaml new file mode 100644 index 0000000000..9510e62777 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-overlapping-matches.in.yaml @@ -0,0 +1,50 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/testPath" + backendRefs: + - name: service-1 + port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/testPath" + backendRefs: + - name: service-2 + port: 8080 diff --git a/internal/gatewayapi/testdata/httproute-overlapping-matches.out.yaml b/internal/gatewayapi/testdata/httproute-overlapping-matches.out.yaml new file mode 100644 index 0000000000..cef8461152 --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-overlapping-matches.out.yaml @@ -0,0 +1,245 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + type: Exact + value: /testPath + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: httproute-2 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + type: Exact + value: /testPath + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-envoy-gateway-gateway-1-196ae069 + namespace: envoy-gateway-system + sectionName: "8080" + name: envoy-gateway/gateway-1 + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /testPath + name: "" + - destination: + metadata: + kind: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-2 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-2/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-2 + namespace: default + name: httproute/default/httproute-2/rule/0/match/0/* + pathMatch: + distinct: false + exact: /testPath + name: "" + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-app-protocols.out.yaml b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-app-protocols.out.yaml index e6d915ea41..21463c39aa 100644 --- a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-app-protocols.out.yaml +++ b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-app-protocols.out.yaml @@ -110,6 +110,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -144,6 +149,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml index f942a44d0e..540a898752 100644 --- a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml +++ b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-weights.out.yaml @@ -108,6 +108,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -142,6 +147,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/httproute-with-conflicting-filters.out.yaml b/internal/gatewayapi/testdata/httproute-with-conflicting-filters.out.yaml index ab7a2e2f57..1c853a0cad 100644 --- a/internal/gatewayapi/testdata/httproute-with-conflicting-filters.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-conflicting-filters.out.yaml @@ -98,6 +98,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/mirror-request-redirect' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -157,6 +162,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/mirror-direct-response' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/httproute-with-credential-injection.out.yaml b/internal/gatewayapi/testdata/httproute-with-credential-injection.out.yaml index 17ac89bf03..ca577509dc 100644 --- a/internal/gatewayapi/testdata/httproute-with-credential-injection.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-credential-injection.out.yaml @@ -90,6 +90,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -134,6 +139,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/httproute-with-header-match.out.yaml b/internal/gatewayapi/testdata/httproute-with-header-match.out.yaml index 85db3194db..8b90481574 100644 --- a/internal/gatewayapi/testdata/httproute-with-header-match.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-header-match.out.yaml @@ -74,6 +74,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-2' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -123,6 +128,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/httproute-1' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/listenerset-httproute.out.yaml b/internal/gatewayapi/testdata/listenerset-httproute.out.yaml index 69aa62ae91..ba5c8f1662 100644 --- a/internal/gatewayapi/testdata/listenerset-httproute.out.yaml +++ b/internal/gatewayapi/testdata/listenerset-httproute.out.yaml @@ -107,6 +107,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/route-entire-xls' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: group: gateway.networking.k8s.io @@ -143,6 +148,11 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/route-entire-xls' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: group: gateway.networking.k8s.io @@ -178,6 +188,12 @@ httpRoutes: reason: ResolvedRefs status: "True" type: ResolvedRefs + - lastTransitionTime: null + message: 'Overlapping match conditions with route(s): HTTPRoute default/route-extra-http-one, + HTTPRoute default/route-extra-http-two' + reason: RouteRulesOverlap + status: "True" + type: RouteRulesOverlap controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index 6163cc6410..c6a321c01c 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -297,6 +297,10 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult, // Process all relevant GRPCRoutes. grpcRoutes := t.ProcessGRPCRoutes(resources.GRPCRoutes, acceptedGateways, resources, xdsIR) + // Check for overlapping route matches across all listeners after all HTTP and + // GRPC routes have been processed. + t.checkRouteOverlaps(httpRoutes, grpcRoutes, xdsIR) + // Process all relevant TLSRoutes. tlsRoutes := t.ProcessTLSRoutes(resources.TLSRoutes, acceptedGateways, resources, xdsIR) diff --git a/release-notes/current.yaml b/release-notes/current.yaml index 871279210f..f9dcb28484 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -46,6 +46,7 @@ new features: | Added support for OpenTelemetry sampler configuration for tracing. Added support for default EnvoyProxy settings on EnvoyGatewaySpec that can be overridden by GatewayClass or Gateway-level EnvoyProxy configurations. A new MergeType field allows choosing between Replace (default), StrategicMerge, or JSONMerge strategies for combining configurations. Added support for sending Envoy Gateway route metadata to external authorization backends via `SecurityPolicy.spec.extAuth.includeRouteMetadata`. + Added a `RouteRulesOverlap` warning status condition for routes whose match conditions are identical to another route on the same listener, so users can identify silently-shadowed routes. bug fixes: | Fixed local rate limit rules with identical sourceCIDR client selectors producing conflicting descriptors.