From 6587f05c4db571f09c8f2e7bdf00a4299bc880e1 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Mon, 14 Apr 2025 04:20:42 +0000 Subject: [PATCH 1/9] Improve the HTTPRoute status errors. Signed-off-by: Huabing (Robin) Zhao --- .../out/rejected-http-route.route.yaml | 3 +- internal/gatewayapi/error.go | 133 +++++++++++ internal/gatewayapi/filters.go | 10 +- internal/gatewayapi/listener.go | 4 +- internal/gatewayapi/route.go | 219 +++++++++++------- .../backend-invalid-feature-disabled.out.yaml | 3 +- .../backendtlspolicy-invalid-ca.out.yaml | 7 +- .../backendtlspolicy-multiple-targets.in.yaml | 22 ++ ...backendtlspolicy-multiple-targets.out.yaml | 16 +- ...kendtlspolicy-serviceimport-target.in.yaml | 2 +- ...endtlspolicy-serviceimport-target.out.yaml | 14 +- ...endtrafficpolicy-status-conditions.in.yaml | 62 +++++ ...ndtrafficpolicy-status-conditions.out.yaml | 42 ++-- ...fficpolicy-with-timeout-targetrefs.in.yaml | 34 ++- ...ficpolicy-with-timeout-targetrefs.out.yaml | 36 ++- .../clienttrafficpolicy-http10.in.yaml | 32 +++ .../clienttrafficpolicy-http10.out.yaml | 54 +++-- .../testdata/custom-filter-order.in.yaml | 33 +++ .../testdata/custom-filter-order.out.yaml | 18 +- ...yextensionpolicy-status-conditions.in.yaml | 61 +++++ ...extensionpolicy-status-conditions.out.yaml | 42 ++-- ...tensionpolicy-with-wasm-targetrefs.in.yaml | 35 ++- ...ensionpolicy-with-wasm-targetrefs.out.yaml | 36 ++- .../envoyproxy-accesslog-types.in.yaml | 32 ++- .../envoyproxy-accesslog-types.out.yaml | 18 +- ...nvoyproxy-tls-settings-invalid-ns.out.yaml | 8 +- .../envoyproxy-tls-settings-invalid.out.yaml | 8 +- ...istener-with-hostname-intersection.in.yaml | 32 +++ ...stener-with-hostname-intersection.out.yaml | 36 ++- ...route-with-mismatch-port-protocol.out.yaml | 3 +- ...route-with-mismatch-port-protocol.out.yaml | 3 +- ...end-backendref-mixed-address-type.out.yaml | 17 +- ...-listener-with-backend-backendref.out.yaml | 18 +- ...end-backendrefs-diff-address-type.out.yaml | 21 +- ...end-backendrefs-same-address-type.out.yaml | 6 +- ...ort-backendrefs-diff-address-type.out.yaml | 5 +- ...ort-backendref-mixed-address-type.out.yaml | 5 +- ...with-invalid-backend-ref-bad-port.out.yaml | 3 +- ...invalid-backend-ref-invalid-group.out.yaml | 7 +- ...-invalid-backend-ref-invalid-kind.out.yaml | 4 +- ...id-backend-ref-invalid-mixed-kind.out.yaml | 5 +- ...-with-invalid-backend-ref-no-port.out.yaml | 4 +- ...lid-backend-ref-no-service.import.out.yaml | 3 +- ...th-invalid-backend-ref-no-service.out.yaml | 3 +- ...id-backend-ref-unsupported-filter.out.yaml | 5 +- ...lid-backendref-in-other-namespace.out.yaml | 3 +- .../httproute-with-invalid-regex.out.yaml | 11 +- ...ith-mirror-filter-service-no-port.out.yaml | 11 +- ...h-mirror-filter-service-not-found.out.yaml | 10 +- ...ultiple-gateways-from-different-ns.in.yaml | 35 +++ ...ltiple-gateways-from-different-ns.out.yaml | 29 ++- ...proute-with-multiple-invalid-rules.in.yaml | 51 ++++ ...route-with-multiple-invalid-rules.out.yaml | 178 ++++++++++++++ ...h-single-rule-with-multiple-rules.out.yaml | 62 ++++- ...e-invalid-backend-refs-no-service.out.yaml | 4 +- .../securitypolicy-status-conditions.out.yaml | 3 +- ...curitypolicy-with-cors-targetrefs.out.yaml | 6 +- ...curitypolicy-with-extauth-backend.out.yaml | 3 +- internal/gatewayapi/validate.go | 124 ++++++---- 59 files changed, 1385 insertions(+), 309 deletions(-) create mode 100644 internal/gatewayapi/error.go create mode 100644 internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.in.yaml create mode 100644 internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.out.yaml diff --git a/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml b/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml index cff3a5f335..a292647721 100644 --- a/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml +++ b/internal/cmd/egctl/testdata/translate/out/rejected-http-route.route.yaml @@ -79,7 +79,8 @@ httpRoutes: status: "False" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway-system/backend not found + message: 'Failed to process route rule 0 backendRef 0: service envoy-gateway-system/backend + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/error.go b/internal/gatewayapi/error.go new file mode 100644 index 0000000000..fc35d85aa2 --- /dev/null +++ b/internal/gatewayapi/error.go @@ -0,0 +1,133 @@ +// 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 ( + "strings" + + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/envoyproxy/gateway/internal/gatewayapi/status" +) + +// Route condition reasons for various error scenarios +const ( + // Route configuration related reasons + RouteReasonUnsupportedSetting gwapiv1.RouteConditionReason = "UnsupportedValue" + RouteReasonUnsupportedRefValue gwapiv1.RouteConditionReason = "UnsupportedRefValue" + + // Backend reference related reasons + RouteReasonInvalidBackendRef gwapiv1.RouteConditionReason = "InvalidBackendRef" + RouteReasonInvalidBackendTLS gwapiv1.RouteConditionReason = "InvalidBackendTLS" + RouteReasonInvalidBackendFilters gwapiv1.RouteConditionReason = "InvalidBackendFilters" + + // Network configuration related reasons + RouteReasonInvalidPortNotFound gwapiv1.RouteConditionReason = "PortNotFound" + RouteReasonPortNotSpecified gwapiv1.RouteConditionReason = "PortNotSpecified" + RouteReasonInvalidAddressType gwapiv1.RouteConditionReason = "InvalidAddressType" +) + +// StatusError is an error interface that represents errors that need to be reflected +// in the status of a Kubernetes resource. It extends the standard error interface +// with a Reason method that returns the specific condition reason. +type StatusError interface { + error + Reason() gwapiv1.RouteConditionReason +} + +// RouteStatusError represents an error that needs to be reflected in the status of an xRoute. +// It wraps an underlying error and provides a specific route condition reason. +type RouteStatusError struct { + Wrapped error + RouteConditionReason gwapiv1.RouteConditionReason +} + +// Error returns the string representation of the error. +// If Wrapped is nil, it returns the string representation of the RouteConditionReason. +func (s *RouteStatusError) Error() string { + if s == nil { + return "" + } + if s.Wrapped != nil { + return s.Wrapped.Error() + } + return string(s.RouteConditionReason) +} + +// Reason returns the route condition reason associated with this error. +func (s *RouteStatusError) Reason() gwapiv1.RouteConditionReason { + if s == nil { + return "" + } + return s.RouteConditionReason +} + +// MultiStatusError represents a collection of status errors that occurred during processing. +// It implements the StatusError interface and provides methods to manage multiple errors. +type MultiStatusError struct { + errs []StatusError +} + +// Empty returns true if there are no errors in the collection. +func (m *MultiStatusError) Empty() bool { + return m == nil || len(m.errs) == 0 +} + +// Add appends a new status error to the collection. +// If the error is nil, it is ignored. +func (m *MultiStatusError) Add(err StatusError) { + if err == nil { + return + } + if m == nil { + m = &MultiStatusError{} + } + m.errs = append(m.errs, err) +} + +// Error returns a string representation of all the wrapped errors. +// If there are no errors, it returns an empty string. +func (m *MultiStatusError) Error() string { + if m == nil || len(m.errs) == 0 { + return "" + } + if len(m.errs) == 1 { + return m.errs[0].Error() + } + + var b strings.Builder + b.WriteString(status.Error2ConditionMsg(m.errs[0])) + for _, err := range m.errs[1:] { + b.WriteByte('\n') + b.WriteString(status.Error2ConditionMsg(err)) + } + return b.String() +} + +// Reason returns a string representation of all unique reasons from the wrapped errors. +// If there are no errors or no reasons, it returns an empty string. +func (m *MultiStatusError) Reason() gwapiv1.RouteConditionReason { + if m == nil || len(m.errs) == 0 { + return "" + } + + reasons := make(map[string]struct{}) + for _, err := range m.errs { + if reason := err.Reason(); reason != "" { + reasons[string(reason)] = struct{}{} + } + } + + if len(reasons) == 0 { + return "" + } + + reasonList := make([]string, 0, len(reasons)) + for reason := range reasons { + reasonList = append(reasonList, reason) + } + return gwapiv1.RouteConditionReason(strings.Join(reasonList, ", ")) +} diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index 73bf889ed5..2766dc4d53 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -31,7 +31,7 @@ type HTTPFiltersTranslator interface { processRedirectFilter(redirect *gwapiv1.HTTPRequestRedirectFilter, filterContext *HTTPFiltersContext) processRequestHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) processResponseHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) - processRequestMirrorFilter(filterIdx int, mirror *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *resource.Resources) error + processRequestMirrorFilter(filterIdx int, mirror *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *resource.Resources) StatusError processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) } @@ -69,14 +69,14 @@ func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, filters []gwapiv1.HTTPRouteFilter, ruleIdx int, resources *resource.Resources, -) (*HTTPFiltersContext, error) { +) (*HTTPFiltersContext, StatusError) { httpFiltersContext := &HTTPFiltersContext{ ParentRef: parentRef, Route: route, RuleIdx: ruleIdx, HTTPFilterIR: &HTTPFilterIR{}, } - var err error + var err StatusError for i := range filters { filter := filters[i] // If an invalid filter type has been configured then skip processing any more filters @@ -876,7 +876,7 @@ func (t *Translator) processRequestMirrorFilter( mirrorFilter *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *resource.Resources, -) error { +) (err StatusError) { // Make sure the config actually exists if mirrorFilter == nil { return nil @@ -896,7 +896,7 @@ func (t *Translator) processRequestMirrorFilter( // This sets the status on the HTTPRoute, should the usage be changed so that the status message reflects that the backendRef is from the filter? filterNs := filterContext.Route.GetNamespace() serviceNamespace := NamespaceDerefOr(mirrorBackend.Namespace, filterNs) - err := t.validateBackendRef(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, + err = t.validateBackendRef(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources, serviceNamespace, resource.KindHTTPRoute) if err != nil { return err diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 6633a0c44b..d14e61b52d 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -565,13 +565,13 @@ func (t *Translator) processBackendRefs(name string, backendCluster egv1a1.Backe kind := KindDerefOr(ref.Kind, resource.KindService) switch kind { case resource.KindService: - if _, err := validateBackendRefService(ref.BackendObjectReference, resources, ns, corev1.ProtocolTCP); err != nil { + if err := validateBackendRefService(ref.BackendObjectReference, resources, ns, corev1.ProtocolTCP); err != nil { return nil, nil, err } ds := t.processServiceDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, resources, envoyProxy) result = append(result, ds) case resource.KindBackend: - if _, err := t.validateBackendRefBackend(ref.BackendObjectReference, resources, ns, true); err != nil { + if err := t.validateBackendRefBackend(ref.BackendObjectReference, resources, ns, true); err != nil { return nil, nil, err } ds := t.processBackendDestinationSetting(name, ref.BackendObjectReference, ns, ir.TCP, resources) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index e784673969..b7f3bdfd76 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -120,17 +120,18 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res // any conditions that come out of it have to go on each RouteParentStatus, // not on the Route as a whole. routeRoutes, err := t.processHTTPRouteRules(httpRoute, parentRef, resources) + // TODO: zhaohuabing: according to the gateway api, the RouteConditionPartiallyInvalid condition should be set + // to true when an HTTPRoute contains a combination of both valid and invalid rules. if err != nil { routeStatus := GetRouteStatus(httpRoute) status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, httpRoute.GetGeneration(), - gwapiv1.RouteConditionAccepted, + gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - gwapiv1.RouteReasonUnsupportedValue, // TODO: better reason + err.Reason(), status.Error2ConditionMsg(err), ) - continue } // If no negative condition has been set for ResolvedRefs, set "ResolvedRefs=True" @@ -181,96 +182,115 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res } } -func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRef *RouteParentContext, resources *resource.Resources) ([]*ir.HTTPRoute, error) { - var routeRoutes []*ir.HTTPRoute - var envoyProxy *egv1a1.EnvoyProxy +func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRef *RouteParentContext, resources *resource.Resources) ([]*ir.HTTPRoute, StatusError) { + var ( + irRoutes []*ir.HTTPRoute + envoyProxy *egv1a1.EnvoyProxy + errs = &MultiStatusError{} + ) gatewayCtx := httpRoute.ParentRefs[*parentRef.ParentReference].GetGateway() if gatewayCtx != nil { envoyProxy = gatewayCtx.envoyProxy } - // compute matches, filters, backends + // process each HTTPRouteRule, generate a unique Xds IR HTTPRoute per match of the rule for ruleIdx, rule := range httpRoute.Spec.Rules { httpFiltersContext, err := t.ProcessHTTPFilters(parentRef, httpRoute, rule.Filters, ruleIdx, resources) if err != nil { - return nil, err + errs.Add(&RouteStatusError{ + Wrapped: fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), + RouteConditionReason: err.Reason(), + }) + continue } - // A rule is matched if any one of its matches - // is satisfied (i.e. a logical "OR"), so generate - // a unique Xds IR HTTPRoute per match. + + // The HTTPRouteRule matches are ORed, a rule is matched if any one of its matches is satisfied, + // so generate a unique Xds IR HTTPRoute per match. ruleRoutes, err := t.processHTTPRouteRule(httpRoute, ruleIdx, httpFiltersContext, rule) if err != nil { - return nil, err + errs.Add(&RouteStatusError{ + Wrapped: fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), + RouteConditionReason: err.Reason(), + }) + continue } dstAddrTypeSet := make(sets.Set[ir.DestinationAddressType]) destName := irRouteDestinationName(httpRoute, ruleIdx) + allDs := []*ir.DestinationSetting{} + failedProcessDestination := false + + // process each backendRef, and calculate the destination settings for this rule for i, backendRef := range rule.BackendRefs { settingName := irDestinationSettingName(destName, i) ds, err := t.processDestination(settingName, backendRef, parentRef, httpRoute, resources) - if !t.IsEnvoyServiceRouting(envoyProxy) && ds != nil && len(ds.Endpoints) > 0 && ds.AddressType != nil { + if err != nil { + errs.Add(&RouteStatusError{ + Wrapped: fmt.Errorf("failed to process route rule %d backendRef %d: %w", ruleIdx, i, err), + RouteConditionReason: err.Reason(), + }) + failedProcessDestination = true + continue + } + + // ds can be nil if the backendRef weight is 0 + if ds == nil { + continue + } + allDs = append(allDs, ds) + + if !t.IsEnvoyServiceRouting(envoyProxy) && len(ds.Endpoints) > 0 && ds.AddressType != nil { dstAddrTypeSet.Insert(*ds.AddressType) } + } - for _, route := range ruleRoutes { - // disable associated routes to a backend ref in case some of its config was invalid - if err != nil { - route.DirectResponse = &ir.CustomResponse{ - StatusCode: ptr.To(uint32(500)), - } - continue - } + // process each ir route + for _, irRoute := range ruleRoutes { + destination := &ir.RouteDestination{ + Settings: allDs, + } - if ds == nil { - continue - } - // If the route already has a direct response or redirect configured, then it was from a filter so skip - // processing any destinations for this route. - if route.DirectResponse != nil || route.Redirect != nil { - continue + switch { + // If the route already has a direct response or redirect configured, then it was from a filter so skip + // processing any destinations for this route. + case irRoute.DirectResponse != nil || irRoute.Redirect != nil: + // return 500 if any destination setting is invalid + // the error is already added to the error list when processing the destination + case failedProcessDestination: + irRoute.DirectResponse = &ir.CustomResponse{ + StatusCode: ptr.To(uint32(500)), } - - if route.Destination == nil { - route.Destination = &ir.RouteDestination{ - Name: destName, - } + // return 500 if the weight of all the valid destination settings(endpoints list is not empty) is 0 + case destination.ToBackendWeights().Valid == 0: + irRoute.DirectResponse = &ir.CustomResponse{ + StatusCode: ptr.To(uint32(500)), } - route.Destination.Settings = append(route.Destination.Settings, ds) + default: + destination.Name = destName + destination.Settings = allDs + irRoute.Destination = destination } } // TODO: support mixed endpointslice address type between backendRefs if !t.IsEnvoyServiceRouting(envoyProxy) && (dstAddrTypeSet.Len() > 1 || dstAddrTypeSet.Has(ir.MIXED)) { - routeStatus := GetRouteStatus(httpRoute) - status.SetRouteStatusCondition(routeStatus, - parentRef.routeParentStatusIdx, - httpRoute.GetGeneration(), - gwapiv1.RouteConditionResolvedRefs, - metav1.ConditionFalse, - gwapiv1.RouteReasonResolvedRefs, - "Mixed endpointslice address type between backendRefs is not supported") - } - - // If the route has no valid backends then just use a direct response and don't fuss with weighted responses - for _, ruleRoute := range ruleRoutes { - noValidBackends := ruleRoute.Destination == nil || ruleRoute.Destination.ToBackendWeights().Valid == 0 - if ruleRoute.DirectResponse == nil && noValidBackends && ruleRoute.Redirect == nil { - ruleRoute.DirectResponse = &ir.CustomResponse{ - StatusCode: ptr.To(uint32(500)), - } - } - ruleRoute.IsHTTP2 = false + errs.Add(&RouteStatusError{ + Wrapped: fmt.Errorf("failed to process route rule %d: mixed endpointslice address type between backendRefs is not supported", ruleIdx), + RouteConditionReason: RouteReasonInvalidBackendRef, + }) } // TODO handle: // - sum of weights for valid backend refs is 0 // - etc. - routeRoutes = append(routeRoutes, ruleRoutes...) + irRoutes = append(irRoutes, ruleRoutes...) } - - return routeRoutes, nil + if errs.Empty() { + return irRoutes, nil + } + return irRoutes, errs } func processRouteTrafficFeatures(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { @@ -339,7 +359,7 @@ func processRouteRetry(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { irRoute.Retry = res } -func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1.HTTPRouteRule) ([]*ir.HTTPRoute, error) { +func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1.HTTPRouteRule) ([]*ir.HTTPRoute, StatusError) { var ruleRoutes []*ir.HTTPRoute // If no matches are specified, the implementation MUST match every HTTP request. @@ -356,7 +376,11 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i var sessionPersistence *ir.SessionPersistence if rule.SessionPersistence != nil { if rule.SessionPersistence.IdleTimeout != nil { - return nil, fmt.Errorf("idle timeout is not supported in envoy gateway") + return nil, + &RouteStatusError{ + Wrapped: fmt.Errorf("idle timeout is not supported in envoy gateway"), + RouteConditionReason: RouteReasonUnsupportedSetting, + } } var sessionName string @@ -384,7 +408,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i *rule.SessionPersistence.CookieConfig.LifetimeType == gwapiv1.PermanentCookieLifetimeType { ttl, err := time.ParseDuration(string(*rule.SessionPersistence.AbsoluteTimeout)) if err != nil { - return nil, err + return nil, &RouteStatusError{ + Wrapped: err, + RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, + } } sessionPersistence.Cookie.TTL = &metav1.Duration{Duration: ttl} } @@ -396,7 +423,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i } default: // Unknown session persistence type is specified. - return nil, fmt.Errorf("unknown session persistence type %s", *rule.SessionPersistence.Type) + return nil, &RouteStatusError{ + Wrapped: fmt.Errorf("unknown session persistence type %s", *rule.SessionPersistence.Type), + RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, + } } } @@ -423,7 +453,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i } case gwapiv1.PathMatchRegularExpression: if err := regex.Validate(*match.Path.Value); err != nil { - return nil, err + return nil, &RouteStatusError{ + Wrapped: err, + RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, + } } irRoute.PathMatch = &ir.StringMatch{ SafeRegex: match.Path.Value, @@ -439,7 +472,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i }) case gwapiv1.HeaderMatchRegularExpression: if err := regex.Validate(headerMatch.Value); err != nil { - return nil, err + return nil, &RouteStatusError{ + Wrapped: err, + RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, + } } irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), @@ -456,7 +492,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i }) case gwapiv1.QueryParamMatchRegularExpression: if err := regex.Validate(queryParamMatch.Value); err != nil { - return nil, err + return nil, &RouteStatusError{ + Wrapped: err, + RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, + } } irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ Name: string(queryParamMatch.Name), @@ -1290,7 +1329,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour // This will result in a direct 500 response for HTTP-based requests. func (t *Translator) processDestination(name string, backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, -) (ds *ir.DestinationSetting, err error) { +) (ds *ir.DestinationSetting, err StatusError) { routeType := GetRouteType(route) weight := uint32(1) backendRef := GetBackendRef(backendRefContext) @@ -1334,7 +1373,8 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe ds = t.processBackendDestinationSetting(name, backendRef.BackendObjectReference, backendNamespace, protocol, resources) } - ds.TLS, err = t.applyBackendTLSSetting( + var tlsErr error + ds.TLS, tlsErr = t.applyBackendTLSSetting( backendRef.BackendObjectReference, backendNamespace, gwapiv1a2.ParentReference{ @@ -1348,13 +1388,20 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe resources, envoyProxy, ) - if err != nil { - return nil, err + if tlsErr != nil { + return nil, &RouteStatusError{ + Wrapped: tlsErr, + RouteConditionReason: RouteReasonInvalidBackendTLS, + } } - ds.Filters, err = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) - if err != nil { - return nil, err + var filtersErr error + ds.Filters, filtersErr = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) + if filtersErr != nil { + return nil, &RouteStatusError{ + Wrapped: filtersErr, + RouteConditionReason: RouteReasonInvalidBackendFilters, + } } if err := validateDestinationSettings(ds, t.IsEnvoyServiceRouting(envoyProxy), backendRef.Kind); err != nil { @@ -1373,16 +1420,22 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe return ds, nil } -func validateDestinationSettings(destinationSettings *ir.DestinationSetting, endpointRoutingDisabled bool, kind *gwapiv1.Kind) error { +func validateDestinationSettings(destinationSettings *ir.DestinationSetting, endpointRoutingDisabled bool, kind *gwapiv1.Kind) StatusError { // TODO: support mixed endpointslice address type for the same backendRef switch KindDerefOr(kind, resource.KindService) { case egv1a1.KindBackend: if destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { - return fmt.Errorf("mixed FQDN and IP or Unix address type for the same backendRef is not supported") + return &RouteStatusError{ + Wrapped: fmt.Errorf("mixed FQDN and IP or Unix address type for the same backendRef is not supported"), + RouteConditionReason: RouteReasonInvalidAddressType, + } } case resource.KindService, resource.KindServiceImport: if !endpointRoutingDisabled && destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { - return fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported") + return &RouteStatusError{ + Wrapped: fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported"), + RouteConditionReason: RouteReasonInvalidAddressType, + } } } @@ -1781,7 +1834,13 @@ func getTargetBackendReference(backendRef gwapiv1a2.BackendObjectReference, back return ref } -func (t *Translator) processBackendDestinationSetting(name string, backendRef gwapiv1.BackendObjectReference, backendNamespace string, protocol ir.AppProtocol, resources *resource.Resources) *ir.DestinationSetting { +func (t *Translator) processBackendDestinationSetting( + name string, + backendRef gwapiv1.BackendObjectReference, + backendNamespace string, + protocol ir.AppProtocol, + resources *resource.Resources, +) *ir.DestinationSetting { var ( dstEndpoints []*ir.DestinationEndpoint dstAddrType *ir.DestinationAddressType @@ -1790,6 +1849,8 @@ func (t *Translator) processBackendDestinationSetting(name string, backendRef gw addrTypeMap := make(map[ir.DestinationAddressType]int) backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) + ds := &ir.DestinationSetting{Name: name} + for _, bep := range backend.Spec.Endpoints { var irde *ir.DestinationEndpoint switch { @@ -1832,13 +1893,9 @@ func (t *Translator) processBackendDestinationSetting(name string, backendRef gw for _, ap := range backend.Spec.AppProtocols { protocol = backendAppProtocolToIRAppProtocol(ap, protocol) } - - ds := &ir.DestinationSetting{ - Name: name, - Protocol: protocol, - Endpoints: dstEndpoints, - AddressType: dstAddrType, - } + ds.Endpoints = dstEndpoints + ds.AddressType = dstAddrType + ds.Protocol = protocol if backend.Spec.Fallback != nil { // set only the secondary priority, the backend defaults to a primary priority if unset. diff --git a/internal/gatewayapi/testdata/backend-invalid-feature-disabled.out.yaml b/internal/gatewayapi/testdata/backend-invalid-feature-disabled.out.yaml index 6a82f655a8..c18041a2e7 100644 --- a/internal/gatewayapi/testdata/backend-invalid-feature-disabled.out.yaml +++ b/internal/gatewayapi/testdata/backend-invalid-feature-disabled.out.yaml @@ -121,7 +121,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Backend is disabled in Envoy Gateway configuration + message: 'Failed to process route rule 0 backendRef 0: Backend is disabled + in Envoy Gateway configuration.' reason: UnsupportedValue status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/backendtlspolicy-invalid-ca.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-invalid-ca.out.yaml index 50ae45b793..8584f51b3b 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-invalid-ca.out.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-invalid-ca.out.yaml @@ -101,9 +101,10 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" + message: 'Failed to process route rule 0 backendRef 0: no ca found in configmap + no-ca-cmap.' + reason: InvalidBackendTLS + status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: diff --git a/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.in.yaml b/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.in.yaml index 96a97fcb0c..ac044ec42c 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.in.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.in.yaml @@ -72,6 +72,28 @@ services: protocol: TCP targetPort: 8081 +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: envoy-gateway + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http1 + protocol: TCP + port: 8080 + - name: http2 + protocol: TCP + port: 8081 + endpoints: + - addresses: + - "10.244.0.11" + conditions: + ready: true + configMaps: - apiVersion: v1 kind: ConfigMap diff --git a/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.out.yaml index 268c7f71bf..b993b50455 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.out.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-multiple-targets.out.yaml @@ -192,7 +192,11 @@ xdsIR: - destination: name: httproute/envoy-gateway/httproute-btls-1/rule/0 settings: - - name: httproute/envoy-gateway/httproute-btls-1/rule/0/backend/0 + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + name: httproute/envoy-gateway/httproute-btls-1/rule/0/backend/0 protocol: HTTP tls: alpnProtocols: null @@ -201,8 +205,6 @@ xdsIR: name: policy-btls/envoy-gateway-ca sni: example.com weight: 1 - directResponse: - statusCode: 500 hostname: '*' isHTTP2: false metadata: @@ -217,7 +219,11 @@ xdsIR: - destination: name: httproute/envoy-gateway/httproute-btls-2/rule/0 settings: - - name: httproute/envoy-gateway/httproute-btls-2/rule/0/backend/0 + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8081 + name: httproute/envoy-gateway/httproute-btls-2/rule/0/backend/0 protocol: HTTP tls: alpnProtocols: null @@ -226,8 +232,6 @@ xdsIR: name: policy-btls/envoy-gateway-ca sni: example.com weight: 1 - directResponse: - statusCode: 500 hostname: '*' isHTTP2: false metadata: diff --git a/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.in.yaml b/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.in.yaml index 2db186d267..7ecbdb09f7 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.in.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.in.yaml @@ -82,7 +82,7 @@ endpointSlices: name: service-import-1 namespace: default labels: - kubernetes.io/service-name: service-import-1 + multicluster.kubernetes.io/service-name: service-import-1 addressType: IPv4 ports: - name: http diff --git a/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.out.yaml b/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.out.yaml index a3dfb20385..4659557c9f 100644 --- a/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.out.yaml +++ b/internal/gatewayapi/testdata/backendtlspolicy-serviceimport-target.out.yaml @@ -158,7 +158,11 @@ xdsIR: - destination: name: httproute/envoy-gateway/httproute-btls/rule/0 settings: - - name: httproute/envoy-gateway/httproute-btls/rule/0/backend/0 + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 8080 + name: httproute/envoy-gateway/httproute-btls/rule/0/backend/0 protocol: HTTP2 tls: alpnProtocols: null @@ -167,7 +171,11 @@ xdsIR: sni: example.com useSystemTrustStore: true weight: 1 - - name: httproute/envoy-gateway/httproute-btls/rule/0/backend/1 + - addressType: IP + endpoints: + - host: 10.244.0.11 + port: 9000 + name: httproute/envoy-gateway/httproute-btls/rule/0/backend/1 protocol: HTTP tls: alpnProtocols: null @@ -176,8 +184,6 @@ xdsIR: sni: example.com useSystemTrustStore: true weight: 1 - directResponse: - statusCode: 500 hostname: '*' isHTTP2: false metadata: diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml index a189199c52..863fde4c1d 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.in.yaml @@ -196,3 +196,65 @@ gateways: allowedRoutes: namespaces: from: Same + +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 +- apiVersion: v1 + kind: Service + metadata: + namespace: another-namespace + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: another-namespace + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml index 1f1259870a..db5f02fdc5 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml @@ -385,9 +385,9 @@ grpcRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -420,9 +420,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -454,9 +454,9 @@ httpRoutes: status: "False" type: Accepted - lastTransitionTime: null - message: Service another-namespace/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -560,8 +560,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 hostname: '*' isHTTP2: false metadata: @@ -600,8 +608,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: grpcroute/envoy-gateway/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/backend/0 + protocol: GRPC + weight: 1 headerMatches: - distinct: false exact: foo diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.in.yaml index 4da9fbe289..413102df5b 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.in.yaml @@ -62,6 +62,39 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true + backendTrafficPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy @@ -82,4 +115,3 @@ backendTrafficPolicies: http: connectionIdleTimeout: 16s maxConnectionDuration: 17s - diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml index 7c0b199a46..d3986837a5 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout-targetrefs.out.yaml @@ -152,9 +152,9 @@ grpcRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -191,9 +191,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -252,8 +252,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: grpcroute/envoy-gateway/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/backend/0 + protocol: GRPC + weight: 1 hostname: '*' isHTTP2: true metadata: @@ -294,8 +302,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 hostname: gateway.envoyproxy.io isHTTP2: false metadata: diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-http10.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-http10.in.yaml index c4f16d4a2a..9a95f4113f 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-http10.in.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-http10.in.yaml @@ -150,3 +150,35 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-http10.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-http10.out.yaml index e9a07af17f..4282fed573 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-http10.out.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-http10.out.yaml @@ -341,9 +341,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -357,9 +357,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -395,9 +395,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -520,8 +520,16 @@ xdsIR: mergeSlashes: true port: 8082 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 hostname: route.example.com isHTTP2: false metadata: @@ -549,7 +557,17 @@ xdsIR: mergeSlashes: true port: 8083 routes: - - directResponse: + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + directResponse: statusCode: 500 hostname: route.example.com isHTTP2: false @@ -562,7 +580,17 @@ xdsIR: distinct: false name: "" prefix: / - - directResponse: + - destination: + name: httproute/envoy-gateway/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-2/rule/0/backend/0 + protocol: HTTP + weight: 1 + directResponse: statusCode: 500 hostname: route2.example.com isHTTP2: false diff --git a/internal/gatewayapi/testdata/custom-filter-order.in.yaml b/internal/gatewayapi/testdata/custom-filter-order.in.yaml index 6f27637135..59c44d469a 100644 --- a/internal/gatewayapi/testdata/custom-filter-order.in.yaml +++ b/internal/gatewayapi/testdata/custom-filter-order.in.yaml @@ -53,6 +53,39 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true + securityPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: SecurityPolicy diff --git a/internal/gatewayapi/testdata/custom-filter-order.out.yaml b/internal/gatewayapi/testdata/custom-filter-order.out.yaml index 98a46d7ee0..f37742ea78 100644 --- a/internal/gatewayapi/testdata/custom-filter-order.out.yaml +++ b/internal/gatewayapi/testdata/custom-filter-order.out.yaml @@ -116,9 +116,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -240,8 +240,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 envoyExtensions: wasms: - config: diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.in.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.in.yaml index e0cf2363bc..2554c33cbe 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.in.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.in.yaml @@ -196,3 +196,64 @@ gateways: allowedRoutes: namespaces: from: Same +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 +- apiVersion: v1 + kind: Service + metadata: + namespace: another-namespace + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: another-namespace + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.out.yaml index 5418c20e84..66f30a694c 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.out.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.out.yaml @@ -385,9 +385,9 @@ grpcRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -420,9 +420,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -454,9 +454,9 @@ httpRoutes: status: "False" type: Accepted - lastTransitionTime: null - message: Service another-namespace/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -560,8 +560,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 envoyExtensions: {} hostname: '*' isHTTP2: false @@ -599,8 +607,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: grpcroute/envoy-gateway/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: grpcroute/envoy-gateway/grpcroute-1/rule/0/backend/0 + protocol: GRPC + weight: 1 envoyExtensions: {} headerMatches: - distinct: false diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.in.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.in.yaml index 17026ebbad..c93780dc74 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.in.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.in.yaml @@ -52,12 +52,45 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true + envoyextensionpolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: EnvoyExtensionPolicy metadata: namespace: envoy-gateway - name: policy-for-gateway # This policy should attach httproute-2 + name: policy-for-gateway # This policy should attach httproute-2 spec: targetRefs: - group: gateway.networking.k8s.io diff --git a/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.out.yaml b/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.out.yaml index 00b8255afa..0ea7be373d 100644 --- a/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.out.yaml +++ b/internal/gatewayapi/testdata/envoyextensionpolicy-with-wasm-targetrefs.out.yaml @@ -138,9 +138,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -176,9 +176,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -222,8 +222,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 envoyExtensions: wasms: - config: @@ -259,8 +267,16 @@ xdsIR: distinct: false name: "" prefix: /foo - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-2/rule/0/backend/0 + protocol: HTTP + weight: 1 envoyExtensions: wasms: - config: diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml index 03977e3307..0f54a65280 100644 --- a/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml @@ -215,7 +215,37 @@ services: appProtocol: grpc protocol: TCP targetPort: 9000 -endpointSlices: +- apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true - apiVersion: discovery.k8s.io/v1 kind: EndpointSlice metadata: diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml index ca1370e0f7..8b06e25768 100644 --- a/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml @@ -67,9 +67,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -451,8 +451,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 hostname: '*' isHTTP2: false metadata: diff --git a/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid-ns.out.yaml b/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid-ns.out.yaml index c37797f368..507259c65b 100644 --- a/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid-ns.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid-ns.out.yaml @@ -128,9 +128,11 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" + message: 'Failed to process route rule 0 backendRef 0: ClientCertificateRef + Secret is not located in the same namespace as Envoyproxy. Secret namespace: + envoy-gateway-user-ns does not match Envoyproxy namespace: envoy-gateway-system.' + reason: InvalidBackendTLS + status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: diff --git a/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid.out.yaml b/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid.out.yaml index 207ca8895d..259fa875a4 100644 --- a/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid.out.yaml +++ b/internal/gatewayapi/testdata/envoyproxy-tls-settings-invalid.out.yaml @@ -127,9 +127,11 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Resolved all the Object references for the Route - reason: ResolvedRefs - status: "True" + message: 'Failed to process route rule 0 backendRef 0: failed to locate TLS + secret for client auth: envoy-gateway-system/client-auth-not-found specified + in EnvoyProxy envoy-gateway-system/test.' + reason: InvalidBackendTLS + status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: diff --git a/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml b/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml index 8fba772492..83d7f940ae 100644 --- a/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml +++ b/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml @@ -63,3 +63,35 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.out.yaml b/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.out.yaml index 5ba73049e9..711c638280 100644 --- a/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.out.yaml +++ b/internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.out.yaml @@ -101,9 +101,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -141,9 +141,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found - reason: BackendNotFound - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -187,8 +187,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 hostname: bar.com isHTTP2: false metadata: @@ -215,8 +223,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/httproute-2/rule/0/backend/0 + protocol: HTTP + weight: 1 hostname: '*.example.com' isHTTP2: false metadata: diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml index fef198ced0..7977e60505 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-tcproute-with-mismatch-port-protocol.out.yaml @@ -72,8 +72,7 @@ tcpRoutes: parents: - conditions: - lastTransitionTime: null - message: 'backend Service validation failed: TCP Port 8081 not found on Service - default/service-1' + message: TCP Port 8081 not found on Service default/service-1 reason: Failed to process the settings associated with the TCP route. status: "False" type: Accepted diff --git a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml index 2bb2d0194e..77be96536e 100644 --- a/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml +++ b/internal/gatewayapi/testdata/gateway-with-listener-with-udproute-with-mismatch-port-protocol.out.yaml @@ -72,8 +72,7 @@ udpRoutes: parents: - conditions: - lastTransitionTime: null - message: 'backend Service validation failed: UDP Port 8080 not found on Service - default/service-1' + message: UDP Port 8080 not found on Service default/service-1 reason: Failed to process the settings associated with the UDP route. status: "False" type: Accepted diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml index fb128b0413..a1dc518961 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml @@ -151,8 +151,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Unix domain sockets are not supported in backend references - reason: UnsupportedRefAddressFound + message: 'Failed to process route rule 0 backendRef 0: unix domain sockets + are not supported in backend references.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -188,9 +189,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: mixed FQDN and IP or Unix address type for the same backendRef is - not supported - reason: ResolvedRefs + message: 'Failed to process route rule 0 backendRef 0: mixed FQDN and IP or + Unix address type for the same backendRef is not supported.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -226,9 +227,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: mixed FQDN and IP or Unix address type for the same backendRef is - not supported - reason: ResolvedRefs + message: 'Failed to process route rule 0 backendRef 0: mixed FQDN and IP or + Unix address type for the same backendRef is not supported.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml index b2e8aa5f37..e4abe69436 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml @@ -160,8 +160,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Unix domain sockets are not supported in backend references - reason: UnsupportedRefAddressFound + message: 'Failed to process route rule 0 backendRef 0: unix domain sockets + are not supported in backend references.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -271,9 +272,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: 'Invalid backend reference: IP address 127.0.0.1 in the loopback - range is not supported' - reason: UnsupportedRefAddressFound + message: 'Failed to process route rule 0 backendRef 0: invalid backend reference: + IP address 127.0.0.1 in the loopback range is not supported.' + reason: InvalidBackendRef status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -309,9 +310,10 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: 'Invalid backend reference: hostname localhost should be a domain - with at least two segments separated by dots' - reason: UnsupportedRefAddressFound + message: 'Failed to process route rule 0 backendRef 0: invalid backend reference: + hostname localhost should be a domain with at least two segments separated + by dots.' + reason: InvalidBackendRef status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml index 6163f16ded..ffadb0735d 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml @@ -128,8 +128,10 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Mixed endpointslice address type between backendRefs is not supported - reason: ResolvedRefs + message: |- + Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references. + Failed to process route rule 0: mixed endpointslice address type between backendRefs is not supported. + reason: InvalidAddressType, InvalidBackendRef status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -168,8 +170,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Unix domain sockets are not supported in backend references - reason: UnsupportedRefAddressFound + message: 'Failed to process route rule 0 backendRef 0: unix domain sockets + are not supported in backend references.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -208,8 +211,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Mixed endpointslice address type between backendRefs is not supported - reason: ResolvedRefs + message: 'Failed to process route rule 0: mixed endpointslice address type + between backendRefs is not supported.' + reason: InvalidBackendRef status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -248,8 +252,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Unix domain sockets are not supported in backend references - reason: UnsupportedRefAddressFound + message: 'Failed to process route rule 0 backendRef 0: unix domain sockets + are not supported in backend references.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml index eec8cb0e00..df408f6dc1 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml @@ -178,8 +178,10 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Unix domain sockets are not supported in backend references - reason: UnsupportedRefAddressFound + message: |- + Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references. + Failed to process route rule 0 backendRef 1: unix domain sockets are not supported in backend references. + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml index 696dc48cb6..84f357321f 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-serviceimport-backendrefs-diff-address-type.out.yaml @@ -73,8 +73,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Mixed endpointslice address type between backendRefs is not supported - reason: ResolvedRefs + message: 'Failed to process route rule 0: mixed endpointslice address type + between backendRefs is not supported.' + reason: InvalidBackendRef status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml index b379de8d18..53a87b329d 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml @@ -69,8 +69,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: mixed endpointslice address type for the same backendRef is not supported - reason: ResolvedRefs + message: 'Failed to process route rule 0 backendRef 0: mixed endpointslice + address type for the same backendRef is not supported.' + reason: InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml index 38843d504f..d772df320e 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-bad-port.out.yaml @@ -67,7 +67,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: TCP Port 8888 not found on Service default/service-1 + message: 'Failed to process route rule 0 backendRef 0: TCP Port 8888 not found + on Service default/service-1.' reason: PortNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml index 2a4d5bd09d..cff116987f 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-group.out.yaml @@ -69,9 +69,10 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Group is invalid, only the core API group (specified by omitting - the group field or setting it to an empty string), multicluster.x-k8s.io - and gateway.envoyproxy.io are supported + message: 'Failed to process route rule 0 backendRef 0: Group is invalid, only + the core API group (specified by omitting the group field or setting it + to an empty string), multicluster.x-k8s.io and gateway.envoyproxy.io are + supported.' reason: InvalidKind status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml index c4dc554c58..88ee413d29 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-kind.out.yaml @@ -68,8 +68,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Kind is invalid, only Service, MCS ServiceImport and Envoy Gateway - Backend are supported + message: 'Failed to process route rule 0 backendRef 0: Kind is invalid, only + Service, MCS ServiceImport and Envoy Gateway Backend are supported.' reason: InvalidKind status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-mixed-kind.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-mixed-kind.out.yaml index aca7b9e655..7753de766b 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-mixed-kind.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-invalid-mixed-kind.out.yaml @@ -91,8 +91,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Mixed endpointslice address type between backendRefs is not supported - reason: ResolvedRefs + message: 'Failed to process route rule 0: mixed endpointslice address type + between backendRefs is not supported.' + reason: InvalidBackendRef status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml index 34500886f2..928836b561 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml @@ -66,8 +66,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: A valid port number corresponding to a port on the Service must be - specified + message: 'Failed to process route rule 0 backendRef 0: A valid port number + corresponding to a port on the Service must be specified.' reason: PortNotSpecified status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml index 4eb1ff3739..419646b49b 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.import.out.yaml @@ -69,7 +69,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service import default/service-import-that-doesnt-exist not found + message: 'Failed to process route rule 0 backendRef 0: service import default/service-import-that-doesnt-exist + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml index 0cfcafe7ba..489fbd75c1 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-service.out.yaml @@ -67,7 +67,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service default/service-that-doesnt-exist not found + message: 'Failed to process route rule 0 backendRef 0: service default/service-that-doesnt-exist + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml index 4f031a0587..a688188f5b 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-unsupported-filter.out.yaml @@ -73,8 +73,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Specific filter is not supported within BackendRef, only RequestHeaderModifier - and ResponseHeaderModifier are supported + message: 'Failed to process route rule 0 backendRef 0: specific filter is + not supported within BackendRef, only RequestHeaderModifier and ResponseHeaderModifier + are supported.' reason: UnsupportedRefValue status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml index dcc76c54f4..043109b537 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backendref-in-other-namespace.out.yaml @@ -68,7 +68,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Backend ref to Service backends/service-1 not permitted by any ReferenceGrant. + message: 'Failed to process route rule 0 backendRef 0: Backend ref to Service + backends/service-1 not permitted by any ReferenceGrant.' reason: RefNotPermitted status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml index 0a4b1b8ace..7ee3d92582 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml @@ -102,11 +102,16 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: 'Regex "*.foo.bar.com" is invalid: error parsing regexp: missing - argument to repetition operator: `*`.' + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'Failed to process route rule 0: regex "*.foo.bar.com" is invalid: + error parsing regexp: missing argument to repetition operator: `*`.' reason: UnsupportedValue status: "False" - type: Accepted + type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml index 87c69760f6..233aad08d0 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -71,14 +71,13 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: 'Error validating backend port: port number not specified for backend - reference.' - reason: UnsupportedValue - status: "False" + message: Route is accepted + reason: Accepted + status: "True" type: Accepted - lastTransitionTime: null - message: A valid port number corresponding to a port on the Service must be - specified + message: 'Failed to process route rule 0: A valid port number corresponding + to a port on the Service must be specified.' reason: PortNotSpecified status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml index d12d21a45a..c8d89caa06 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml @@ -72,13 +72,13 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: 'Backend Service validation failed: service default/service-unknown - not found.' - reason: UnsupportedValue - status: "False" + message: Route is accepted + reason: Accepted + status: "True" type: Accepted - lastTransitionTime: null - message: Service default/service-unknown not found + message: 'Failed to process route rule 0: service default/service-unknown + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.in.yaml b/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.in.yaml index 12aa992ef4..6fe5033646 100644 --- a/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.in.yaml +++ b/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.in.yaml @@ -53,3 +53,38 @@ httpRoutes: path: type: PathPrefix value: /toy + backendRefs: + - name: service-1 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: service-1 + spec: + selector: + app: service-1 + ports: + - name: http + protocol: TCP + port: 8080 + +endpointslices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + namespace: envoy-gateway + name: service-1 + labels: + kubernetes.io/service-name: service-1 + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.out.yaml b/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.out.yaml index 36d163dc22..5187100b7e 100644 --- a/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-multiple-gateways-from-different-ns.out.yaml @@ -101,7 +101,10 @@ httpRoutes: kind: Gateway name: gateway-b rules: - - matches: + - backendRefs: + - name: service-1 + port: 8080 + matches: - method: GET path: type: PathPrefix @@ -193,8 +196,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/targeted-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/targeted-route/rule/0/backend/0 + protocol: HTTP + weight: 1 headerMatches: - distinct: false exact: GET @@ -235,8 +246,16 @@ xdsIR: mergeSlashes: true port: 10080 routes: - - directResponse: - statusCode: 500 + - destination: + name: httproute/envoy-gateway/targeted-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/envoy-gateway/targeted-route/rule/0/backend/0 + protocol: HTTP + weight: 1 headerMatches: - distinct: false exact: GET diff --git a/internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.in.yaml b/internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.in.yaml new file mode 100644 index 0000000000..c567b0982a --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.in.yaml @@ -0,0 +1,51 @@ +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: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-not-found + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-not-found-1 + port: 8080 + - name: service-not-found-2 + port: 8080 + - matches: + - path: + value: "/bar" + backendRefs: + - name: serviceimport-not-found-1 + kind: ServiceImport + port: 8080 + - name: serviceimport-not-found-2 + kind: ServiceImport + port: 8080 diff --git a/internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.out.yaml b/internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.out.yaml new file mode 100644 index 0000000000..f6f07b48ef --- /dev/null +++ b/internal/gatewayapi/testdata/httproute-with-multiple-invalid-rules.out.yaml @@ -0,0 +1,178 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + 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: 1 + 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: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-not-found + - backendRefs: + - name: service-not-found-1 + port: 8080 + - name: service-not-found-2 + port: 8080 + matches: + - path: + value: /foo + - backendRefs: + - kind: ServiceImport + name: serviceimport-not-found-1 + port: 8080 + - kind: ServiceImport + name: serviceimport-not-found-2 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: |- + Failed to process route rule 0 backendRef 0: Backend default/backend-not-found not found. + Failed to process route rule 1 backendRef 0: service default/service-not-found-1 not found. + Failed to process route rule 1 backendRef 1: service default/service-not-found-2 not found. + Failed to process route rule 2 backendRef 0: service import default/serviceimport-not-found-1 not found. + Failed to process route rule 2 backendRef 1: service import default/serviceimport-not-found-2 not found. + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + 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 + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + 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: + - directResponse: + statusCode: 500 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/1/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + - directResponse: + statusCode: 500 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/2/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /bar + - directResponse: + statusCode: 500 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/-1/gateway_envoyproxy_io + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml index f2fa88e8ab..6b8d3f89a3 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml @@ -92,11 +92,16 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: 'Regex "*regex*" is invalid: error parsing regexp: missing argument - to repetition operator: `*`.' + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'Failed to process route rule 2: regex "*regex*" is invalid: error + parsing regexp: missing argument to repetition operator: `*`.' reason: UnsupportedValue status: "False" - type: Accepted + type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: name: gateway-1 @@ -137,6 +142,57 @@ xdsIR: escapedSlashesAction: UnescapeAndRedirect mergeSlashes: true port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + headerMatches: + - distinct: false + exact: exact + name: Header-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: /exact + name: "" + queryParamMatches: + - distinct: false + exact: exact + name: QueryParam-1 + - destination: + name: httproute/default/httproute-1/rule/1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + name: httproute/default/httproute-1/rule/1/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/1/match/0/* + pathMatch: + distinct: false + name: "" + prefix: /prefix readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml b/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml index 5f5eb68a53..bde42466fb 100644 --- a/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-some-invalid-backend-refs-no-service.out.yaml @@ -71,7 +71,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service default/service-that-doesnt-exist-2 not found + message: |- + Failed to process route rule 0 backendRef 0: service default/service-that-doesnt-exist not found. + Failed to process route rule 0 backendRef 1: service default/service-that-doesnt-exist-2 not found. reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml index e72799b669..2380d0ae7f 100644 --- a/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-status-conditions.out.yaml @@ -194,7 +194,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found + message: 'Failed to process route rule 0 backendRef 0: service envoy-gateway/service-1 + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/securitypolicy-with-cors-targetrefs.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-cors-targetrefs.out.yaml index 79efe45f4a..708a622f92 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-cors-targetrefs.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-cors-targetrefs.out.yaml @@ -183,7 +183,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-1 not found + message: 'Failed to process route rule 0 backendRef 0: service envoy-gateway/service-1 + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs @@ -221,7 +222,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service envoy-gateway/service-2 not found + message: 'Failed to process route rule 0 backendRef 0: service envoy-gateway/service-2 + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-backend.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-backend.out.yaml index 3e48e76f89..947457d934 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-backend.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-backend.out.yaml @@ -225,7 +225,8 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Service default/service-5 not found + message: 'Failed to process route rule 0 backendRef 0: service default/service-5 + not found.' reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 15e7cc29d5..ee97101255 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -26,23 +26,23 @@ import ( func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, backendNamespace string, routeKind gwapiv1.Kind, -) error { +) StatusError { backendRef := GetBackendRef(backendRefContext) if err := t.validateBackendRefFilters(backendRefContext, parentRef, route, routeKind); err != nil { - return fmt.Errorf("error validating backend filters: %w", err) + return err } if err := t.validateBackendRefGroup(backendRef, parentRef, route); err != nil { - return fmt.Errorf("error validating backend group: %w", err) + return err } if err := t.validateBackendRefKind(backendRef, parentRef, route); err != nil { - return fmt.Errorf("error validating backend kind: %w", err) + return err } if err := t.validateBackendNamespace(backendRef, parentRef, route, resources, routeKind); err != nil { - return fmt.Errorf("error validating backend namespace: %w", err) + return err } if err := t.validateBackendPort(backendRef, parentRef, route); err != nil { - return fmt.Errorf("error validating backend port: %w", err) + return err } protocol := corev1.ProtocolTCP @@ -50,46 +50,47 @@ func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, par protocol = corev1.ProtocolUDP } + // TODO: zhaohuabing remove this and handle status in the out layer backendRefKind := KindDerefOr(backendRef.Kind, resource.KindService) switch backendRefKind { case resource.KindService: - if reason, err := validateBackendRefService(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { + if err := validateBackendRefService(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - reason, + err.Reason(), errorToMessage(err), ) - return fmt.Errorf("backend Service validation failed: %w", err) + return err } case resource.KindServiceImport: - if reason, err := t.validateBackendServiceImport(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { + if err := t.validateBackendServiceImport(backendRef.BackendObjectReference, resources, backendNamespace, protocol); err != nil { routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - reason, + err.Reason(), errorToMessage(err), ) - return fmt.Errorf("backend ServiceImport validation failed: %w", err) + return err } case egv1a1.KindBackend: - if reason, err := t.validateBackendRefBackend(backendRef.BackendObjectReference, resources, backendNamespace, false); err != nil { + if err := t.validateBackendRefBackend(backendRef.BackendObjectReference, resources, backendNamespace, false); err != nil { routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - reason, + err.Reason(), errorToMessage(err), ) - return fmt.Errorf("backend reference validation failed: %w", err) + return err } } return nil @@ -105,7 +106,7 @@ func errorToMessage(err error) string { return strings.ToUpper(msg[0:1]) + msg[1:] } -func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) error { +func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) StatusError { if backendRef.Group != nil && *backendRef.Group != "" && *backendRef.Group != GroupMultiClusterService && *backendRef.Group != egv1a1.GroupName { routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, @@ -116,12 +117,15 @@ func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, p gwapiv1.RouteReasonInvalidKind, fmt.Sprintf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string), %s and %s are supported", GroupMultiClusterService, egv1a1.GroupName), ) - return fmt.Errorf("unsupported backend reference group: %s", *backendRef.Group) + return &RouteStatusError{ + Wrapped: fmt.Errorf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string), %s and %s are supported", GroupMultiClusterService, egv1a1.GroupName), + RouteConditionReason: gwapiv1.RouteReasonInvalidKind, + } } return nil } -func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) error { +func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) StatusError { if backendRef.Kind != nil && *backendRef.Kind != resource.KindService && *backendRef.Kind != resource.KindServiceImport && *backendRef.Kind != egv1a1.KindBackend { routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, @@ -132,12 +136,15 @@ func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, pa gwapiv1.RouteReasonInvalidKind, "Kind is invalid, only Service, MCS ServiceImport and Envoy Gateway Backend are supported", ) - return fmt.Errorf("unsupported backend reference kind: %s", *backendRef.Kind) + return &RouteStatusError{ + Wrapped: fmt.Errorf("Kind is invalid, only Service, MCS ServiceImport and Envoy Gateway Backend are supported"), + RouteConditionReason: gwapiv1.RouteReasonInvalidKind, + } } return nil } -func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, parentRef *RouteParentContext, route RouteContext, routeKind gwapiv1.Kind) error { +func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, parentRef *RouteParentContext, route RouteContext, routeKind gwapiv1.Kind) StatusError { filters := GetFilters(backendRef) var unsupportedFilters bool @@ -158,6 +165,7 @@ func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, par return nil } + // TODO: zhaohuabing remove this and handle status in the out layer if unsupportedFilters { routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, @@ -165,10 +173,13 @@ func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, par route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - "UnsupportedRefValue", + RouteReasonUnsupportedRefValue, "Specific filter is not supported within BackendRef, only RequestHeaderModifier and ResponseHeaderModifier are supported", ) - return errors.New("unsupported filter type in backend reference") + return &RouteStatusError{ + Wrapped: errors.New("specific filter is not supported within BackendRef, only RequestHeaderModifier and ResponseHeaderModifier are supported"), + RouteConditionReason: RouteReasonUnsupportedRefValue, + } } return nil @@ -176,7 +187,7 @@ func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, par func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, routeKind gwapiv1.Kind, -) error { +) StatusError { if backendRef.Namespace != nil && string(*backendRef.Namespace) != "" && string(*backendRef.Namespace) != route.GetNamespace() { if !t.validateCrossNamespaceRef( crossNamespaceFrom{ @@ -201,13 +212,16 @@ func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, gwapiv1.RouteReasonRefNotPermitted, fmt.Sprintf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", KindDerefOr(backendRef.Kind, resource.KindService), *backendRef.Namespace, backendRef.Name), ) - return fmt.Errorf("cross-namespace reference not permitted for backend: %s", backendRef.Name) + return &RouteStatusError{ + Wrapped: fmt.Errorf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", KindDerefOr(backendRef.Kind, resource.KindService), *backendRef.Namespace, backendRef.Name), + RouteConditionReason: gwapiv1.RouteReasonRefNotPermitted, + } } } return nil } -func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) error { +func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) StatusError { if backendRef == nil { return nil } @@ -217,26 +231,33 @@ func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, paren } if backendRef.Port == nil { + // TODO: zhaohuabing remove this and handle status in the out layer routeStatus := GetRouteStatus(route) status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - "PortNotSpecified", + RouteReasonPortNotSpecified, "A valid port number corresponding to a port on the Service must be specified", ) - return errors.New("port number not specified for backend reference") + return &RouteStatusError{ + Wrapped: errors.New("A valid port number corresponding to a port on the Service must be specified"), + RouteConditionReason: RouteReasonPortNotSpecified, + } } return nil } func validateBackendRefService(backendRef gwapiv1a2.BackendObjectReference, resources *resource.Resources, serviceNamespace string, protocol corev1.Protocol, -) (gwapiv1.RouteConditionReason, error) { +) StatusError { service := resources.GetService(serviceNamespace, string(backendRef.Name)) if service == nil { - return gwapiv1.RouteReasonBackendNotFound, fmt.Errorf("service %s/%s not found", serviceNamespace, string(backendRef.Name)) + return &RouteStatusError{ + Wrapped: fmt.Errorf("service %s/%s not found", serviceNamespace, string(backendRef.Name)), + RouteConditionReason: gwapiv1.RouteReasonBackendNotFound, + } } var portFound bool for _, port := range service.Spec.Ports { @@ -251,17 +272,23 @@ func validateBackendRefService(backendRef gwapiv1a2.BackendObjectReference, reso } if !portFound { - return "PortNotFound", fmt.Errorf("%s Port %d not found on Service %s/%s", string(protocol), *backendRef.Port, serviceNamespace, string(backendRef.Name)) + return &RouteStatusError{ + Wrapped: fmt.Errorf("%s Port %d not found on Service %s/%s", string(protocol), *backendRef.Port, serviceNamespace, string(backendRef.Name)), + RouteConditionReason: RouteReasonInvalidPortNotFound, + } } - return "", nil + return nil } func (t *Translator) validateBackendServiceImport(backendRef gwapiv1a2.BackendObjectReference, resources *resource.Resources, serviceImportNamespace string, protocol corev1.Protocol, -) (gwapiv1.RouteConditionReason, error) { +) StatusError { serviceImport := resources.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) if serviceImport == nil { - return gwapiv1.RouteReasonBackendNotFound, fmt.Errorf("service import %s/%s not found", serviceImportNamespace, backendRef.Name) + return &RouteStatusError{ + Wrapped: fmt.Errorf("service import %s/%s not found", serviceImportNamespace, backendRef.Name), + RouteConditionReason: gwapiv1.RouteReasonBackendNotFound, + } } var portFound bool @@ -277,35 +304,50 @@ func (t *Translator) validateBackendServiceImport(backendRef gwapiv1a2.BackendOb } if !portFound { - return "PortNotFound", fmt.Errorf("%s port %d not found on service import %s/%s", string(protocol), *backendRef.Port, serviceImportNamespace, backendRef.Name) + return &RouteStatusError{ + Wrapped: fmt.Errorf("%s port %d not found on service import %s/%s", string(protocol), *backendRef.Port, serviceImportNamespace, backendRef.Name), + RouteConditionReason: RouteReasonInvalidPortNotFound, + } } - return "", nil + return nil } func (t *Translator) validateBackendRefBackend(backendRef gwapiv1a2.BackendObjectReference, resources *resource.Resources, backendNamespace string, allowUDS bool, -) (gwapiv1.RouteConditionReason, error) { +) StatusError { if !t.BackendEnabled { - return gwapiv1.RouteReasonUnsupportedValue, errors.New("backend is disabled in Envoy Gateway configuration") + return &RouteStatusError{ + Wrapped: errors.New("Backend is disabled in Envoy Gateway configuration"), + RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, + } } backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) if backend == nil { - return gwapiv1.RouteReasonBackendNotFound, fmt.Errorf("backend %s/%s not found", backendNamespace, backendRef.Name) + return &RouteStatusError{ + Wrapped: fmt.Errorf("Backend %s/%s not found", backendNamespace, backendRef.Name), + RouteConditionReason: gwapiv1.RouteReasonBackendNotFound, + } } if err := validateBackend(backend); err != nil { - return "UnsupportedRefAddressFound", fmt.Errorf("invalid backend reference: %w", err) + return &RouteStatusError{ + Wrapped: fmt.Errorf("invalid backend reference: %w", err), + RouteConditionReason: RouteReasonInvalidBackendRef, + } } for _, bep := range backend.Spec.Endpoints { if bep.Unix != nil && !allowUDS { - return "UnsupportedRefAddressFound", errors.New("unix domain sockets are not supported in backend references") + return &RouteStatusError{ + Wrapped: errors.New("unix domain sockets are not supported in backend references"), + RouteConditionReason: RouteReasonInvalidAddressType, + } } } - return "", nil + return nil } func (t *Translator) validateListenerConditions(listener *ListenerContext) (isReady bool) { From f37c41c4165f5d75c4b828313fd43cb897df2b48 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Thu, 17 Apr 2025 03:13:24 +0000 Subject: [PATCH 2/9] move status error to the status package Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/backend.go | 27 +++- internal/gatewayapi/filters.go | 8 +- internal/gatewayapi/route.go | 110 +++++++-------- internal/gatewayapi/{ => status}/error.go | 31 +++-- ...-listener-with-backend-backendref.out.yaml | 13 +- internal/gatewayapi/validate.go | 129 +++++++++--------- 6 files changed, 161 insertions(+), 157 deletions(-) rename internal/gatewayapi/{ => status}/error.go (79%) diff --git a/internal/gatewayapi/backend.go b/internal/gatewayapi/backend.go index 6b9bb09571..ae184ee70e 100644 --- a/internal/gatewayapi/backend.go +++ b/internal/gatewayapi/backend.go @@ -39,27 +39,42 @@ func (t *Translator) ProcessBackends(backends []*egv1a1.Backend) []*egv1a1.Backe return res } -func validateBackend(backend *egv1a1.Backend) error { +func validateBackend(backend *egv1a1.Backend) status.Error { for _, ep := range backend.Spec.Endpoints { if ep.FQDN != nil { hostname := ep.FQDN.Hostname // must be a valid hostname if errs := validation.IsDNS1123Subdomain(hostname); errs != nil { - return fmt.Errorf("hostname %s is not a valid FQDN", hostname) + return status.NewRouteStatusError( + fmt.Errorf("hostname %s is not a valid FQDN", hostname), + status.RouteReasonInvalidAddress, + ) } if len(strings.Split(hostname, ".")) < 2 { - return fmt.Errorf("hostname %s should be a domain with at least two segments separated by dots", hostname) + return status.NewRouteStatusError( + fmt.Errorf("hostname %s should be a domain with at least two segments separated by dots", hostname), + status.RouteReasonInvalidAddress, + ) } // IP addresses are not allowed so parsing the hostname as an address needs to fail if _, err := netip.ParseAddr(hostname); err == nil { - return fmt.Errorf("hostname %s is an IP address", hostname) + return status.NewRouteStatusError( + fmt.Errorf("hostname %s is an IP address", hostname), + status.RouteReasonInvalidAddress, + ) } } else if ep.IP != nil { ip, err := netip.ParseAddr(ep.IP.Address) if err != nil { - return fmt.Errorf("IP address %s is invalid", ep.IP.Address) + return status.NewRouteStatusError( + fmt.Errorf("IP address %s is invalid", ep.IP.Address), + status.RouteReasonInvalidAddress, + ) } else if ip.IsLoopback() { - return fmt.Errorf("IP address %s in the loopback range is not supported", ep.IP.Address) + return status.NewRouteStatusError( + fmt.Errorf("IP address %s in the loopback range is not supported", ep.IP.Address), + status.RouteReasonInvalidAddress, + ) } } } diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index 2766dc4d53..b53ae9bcc8 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -31,7 +31,7 @@ type HTTPFiltersTranslator interface { processRedirectFilter(redirect *gwapiv1.HTTPRequestRedirectFilter, filterContext *HTTPFiltersContext) processRequestHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) processResponseHeaderModifierFilter(headerModifier *gwapiv1.HTTPHeaderFilter, filterContext *HTTPFiltersContext) - processRequestMirrorFilter(filterIdx int, mirror *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *resource.Resources) StatusError + processRequestMirrorFilter(filterIdx int, mirror *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *resource.Resources) status.Error processUnsupportedHTTPFilter(filterType string, filterContext *HTTPFiltersContext) } @@ -69,14 +69,14 @@ func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext, filters []gwapiv1.HTTPRouteFilter, ruleIdx int, resources *resource.Resources, -) (*HTTPFiltersContext, StatusError) { +) (*HTTPFiltersContext, status.Error) { httpFiltersContext := &HTTPFiltersContext{ ParentRef: parentRef, Route: route, RuleIdx: ruleIdx, HTTPFilterIR: &HTTPFilterIR{}, } - var err StatusError + var err status.Error for i := range filters { filter := filters[i] // If an invalid filter type has been configured then skip processing any more filters @@ -876,7 +876,7 @@ func (t *Translator) processRequestMirrorFilter( mirrorFilter *gwapiv1.HTTPRequestMirrorFilter, filterContext *HTTPFiltersContext, resources *resource.Resources, -) (err StatusError) { +) (err status.Error) { // Make sure the config actually exists if mirrorFilter == nil { return nil diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index b7f3bdfd76..6d94e4a3c1 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -182,11 +182,11 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res } } -func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRef *RouteParentContext, resources *resource.Resources) ([]*ir.HTTPRoute, StatusError) { +func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRef *RouteParentContext, resources *resource.Resources) ([]*ir.HTTPRoute, status.Error) { var ( irRoutes []*ir.HTTPRoute envoyProxy *egv1a1.EnvoyProxy - errs = &MultiStatusError{} + errs = &status.MultiStatusError{} ) gatewayCtx := httpRoute.ParentRefs[*parentRef.ParentReference].GetGateway() @@ -198,10 +198,11 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe for ruleIdx, rule := range httpRoute.Spec.Rules { httpFiltersContext, err := t.ProcessHTTPFilters(parentRef, httpRoute, rule.Filters, ruleIdx, resources) if err != nil { - errs.Add(&RouteStatusError{ - Wrapped: fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), - RouteConditionReason: err.Reason(), - }) + // TODO: zhaohuabing should we set Accepted condition instead of Resolved for failed HTTPFilters? + errs.Add(status.NewRouteStatusError( + fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), + err.Reason(), + )) continue } @@ -209,10 +210,10 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe // so generate a unique Xds IR HTTPRoute per match. ruleRoutes, err := t.processHTTPRouteRule(httpRoute, ruleIdx, httpFiltersContext, rule) if err != nil { - errs.Add(&RouteStatusError{ - Wrapped: fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), - RouteConditionReason: err.Reason(), - }) + errs.Add(status.NewRouteStatusError( + fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), + err.Reason(), + )) continue } @@ -226,10 +227,10 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe settingName := irDestinationSettingName(destName, i) ds, err := t.processDestination(settingName, backendRef, parentRef, httpRoute, resources) if err != nil { - errs.Add(&RouteStatusError{ - Wrapped: fmt.Errorf("failed to process route rule %d backendRef %d: %w", ruleIdx, i, err), - RouteConditionReason: err.Reason(), - }) + errs.Add(status.NewRouteStatusError( + fmt.Errorf("failed to process route rule %d backendRef %d: %w", ruleIdx, i, err), + err.Reason(), + )) failedProcessDestination = true continue } @@ -275,10 +276,12 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe // TODO: support mixed endpointslice address type between backendRefs if !t.IsEnvoyServiceRouting(envoyProxy) && (dstAddrTypeSet.Len() > 1 || dstAddrTypeSet.Has(ir.MIXED)) { - errs.Add(&RouteStatusError{ - Wrapped: fmt.Errorf("failed to process route rule %d: mixed endpointslice address type between backendRefs is not supported", ruleIdx), - RouteConditionReason: RouteReasonInvalidBackendRef, - }) + errs.Add(status.NewRouteStatusError( + fmt.Errorf( + "failed to process route rule %d: mixed endpointslice address type between backendRefs is not supported", + ruleIdx), + status.RouteReasonInvalidBackendRef, + )) } // TODO handle: @@ -359,7 +362,11 @@ func processRouteRetry(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) { irRoute.Retry = res } -func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule gwapiv1.HTTPRouteRule) ([]*ir.HTTPRoute, StatusError) { +func (t *Translator) processHTTPRouteRule( + httpRoute *HTTPRouteContext, + ruleIdx int, + httpFiltersContext *HTTPFiltersContext, + rule gwapiv1.HTTPRouteRule) ([]*ir.HTTPRoute, status.Error) { var ruleRoutes []*ir.HTTPRoute // If no matches are specified, the implementation MUST match every HTTP request. @@ -376,11 +383,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i var sessionPersistence *ir.SessionPersistence if rule.SessionPersistence != nil { if rule.SessionPersistence.IdleTimeout != nil { - return nil, - &RouteStatusError{ - Wrapped: fmt.Errorf("idle timeout is not supported in envoy gateway"), - RouteConditionReason: RouteReasonUnsupportedSetting, - } + return nil, status.NewRouteStatusError( + fmt.Errorf("idle timeout is not supported in envoy gateway"), + status.RouteReasonUnsupportedSetting, + ) } var sessionName string @@ -408,10 +414,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i *rule.SessionPersistence.CookieConfig.LifetimeType == gwapiv1.PermanentCookieLifetimeType { ttl, err := time.ParseDuration(string(*rule.SessionPersistence.AbsoluteTimeout)) if err != nil { - return nil, &RouteStatusError{ - Wrapped: err, - RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, - } + return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) } sessionPersistence.Cookie.TTL = &metav1.Duration{Duration: ttl} } @@ -423,10 +426,10 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i } default: // Unknown session persistence type is specified. - return nil, &RouteStatusError{ - Wrapped: fmt.Errorf("unknown session persistence type %s", *rule.SessionPersistence.Type), - RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, - } + return nil, status.NewRouteStatusError( + fmt.Errorf("unknown session persistence type %s", *rule.SessionPersistence.Type), + gwapiv1.RouteReasonUnsupportedValue, + ) } } @@ -453,10 +456,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i } case gwapiv1.PathMatchRegularExpression: if err := regex.Validate(*match.Path.Value); err != nil { - return nil, &RouteStatusError{ - Wrapped: err, - RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, - } + return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) } irRoute.PathMatch = &ir.StringMatch{ SafeRegex: match.Path.Value, @@ -472,10 +472,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i }) case gwapiv1.HeaderMatchRegularExpression: if err := regex.Validate(headerMatch.Value); err != nil { - return nil, &RouteStatusError{ - Wrapped: err, - RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, - } + return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) } irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), @@ -492,10 +489,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i }) case gwapiv1.QueryParamMatchRegularExpression: if err := regex.Validate(queryParamMatch.Value); err != nil { - return nil, &RouteStatusError{ - Wrapped: err, - RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, - } + return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) } irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ Name: string(queryParamMatch.Name), @@ -1329,7 +1323,7 @@ func (t *Translator) processTCPRouteParentRefs(tcpRoute *TCPRouteContext, resour // This will result in a direct 500 response for HTTP-based requests. func (t *Translator) processDestination(name string, backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, -) (ds *ir.DestinationSetting, err StatusError) { +) (ds *ir.DestinationSetting, err status.Error) { routeType := GetRouteType(route) weight := uint32(1) backendRef := GetBackendRef(backendRefContext) @@ -1389,19 +1383,13 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe envoyProxy, ) if tlsErr != nil { - return nil, &RouteStatusError{ - Wrapped: tlsErr, - RouteConditionReason: RouteReasonInvalidBackendTLS, - } + return nil, status.NewRouteStatusError(tlsErr,status.RouteReasonInvalidBackendTLS) } var filtersErr error ds.Filters, filtersErr = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) if filtersErr != nil { - return nil, &RouteStatusError{ - Wrapped: filtersErr, - RouteConditionReason: RouteReasonInvalidBackendFilters, - } + return nil, status.NewRouteStatusError(filtersErr,status.RouteReasonInvalidBackendFilters) } if err := validateDestinationSettings(ds, t.IsEnvoyServiceRouting(envoyProxy), backendRef.Kind); err != nil { @@ -1420,22 +1408,20 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe return ds, nil } -func validateDestinationSettings(destinationSettings *ir.DestinationSetting, endpointRoutingDisabled bool, kind *gwapiv1.Kind) StatusError { +func validateDestinationSettings(destinationSettings *ir.DestinationSetting, endpointRoutingDisabled bool, kind *gwapiv1.Kind) status.Error { // TODO: support mixed endpointslice address type for the same backendRef switch KindDerefOr(kind, resource.KindService) { case egv1a1.KindBackend: if destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { - return &RouteStatusError{ - Wrapped: fmt.Errorf("mixed FQDN and IP or Unix address type for the same backendRef is not supported"), - RouteConditionReason: RouteReasonInvalidAddressType, - } + return status.NewRouteStatusError( + fmt.Errorf("mixed FQDN and IP or Unix address type for the same backendRef is not supported"), + status.RouteReasonInvalidAddressType) } case resource.KindService, resource.KindServiceImport: if !endpointRoutingDisabled && destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { - return &RouteStatusError{ - Wrapped: fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported"), - RouteConditionReason: RouteReasonInvalidAddressType, - } + return status.NewRouteStatusError( + fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported"), + status.RouteReasonInvalidAddressType) } } diff --git a/internal/gatewayapi/error.go b/internal/gatewayapi/status/error.go similarity index 79% rename from internal/gatewayapi/error.go rename to internal/gatewayapi/status/error.go index fc35d85aa2..475b3dd9a4 100644 --- a/internal/gatewayapi/error.go +++ b/internal/gatewayapi/status/error.go @@ -3,14 +3,12 @@ // The full text of the Apache license is available in the LICENSE file at // the root of the repo. -package gatewayapi +package status import ( "strings" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - "github.com/envoyproxy/gateway/internal/gatewayapi/status" ) // Route condition reasons for various error scenarios @@ -25,15 +23,16 @@ const ( RouteReasonInvalidBackendFilters gwapiv1.RouteConditionReason = "InvalidBackendFilters" // Network configuration related reasons - RouteReasonInvalidPortNotFound gwapiv1.RouteConditionReason = "PortNotFound" - RouteReasonPortNotSpecified gwapiv1.RouteConditionReason = "PortNotSpecified" - RouteReasonInvalidAddressType gwapiv1.RouteConditionReason = "InvalidAddressType" + RouteReasonPortNotFound gwapiv1.RouteConditionReason = "PortNotFound" + RouteReasonPortNotSpecified gwapiv1.RouteConditionReason = "PortNotSpecified" + RouteReasonInvalidAddressType gwapiv1.RouteConditionReason = "InvalidAddressType" + RouteReasonInvalidAddress gwapiv1.RouteConditionReason = "InvalidAddress" ) -// StatusError is an error interface that represents errors that need to be reflected +// Error is an error interface that represents errors that need to be reflected // in the status of a Kubernetes resource. It extends the standard error interface // with a Reason method that returns the specific condition reason. -type StatusError interface { +type Error interface { error Reason() gwapiv1.RouteConditionReason } @@ -45,6 +44,14 @@ type RouteStatusError struct { RouteConditionReason gwapiv1.RouteConditionReason } +// NewRouteStatusError creates a new RouteStatusError with the given wrapped error and route condition reason. +func NewRouteStatusError(wrapped error, reason gwapiv1.RouteConditionReason) *RouteStatusError { + return &RouteStatusError{ + Wrapped: wrapped, + RouteConditionReason: reason, + } +} + // Error returns the string representation of the error. // If Wrapped is nil, it returns the string representation of the RouteConditionReason. func (s *RouteStatusError) Error() string { @@ -68,7 +75,7 @@ func (s *RouteStatusError) Reason() gwapiv1.RouteConditionReason { // MultiStatusError represents a collection of status errors that occurred during processing. // It implements the StatusError interface and provides methods to manage multiple errors. type MultiStatusError struct { - errs []StatusError + errs []Error } // Empty returns true if there are no errors in the collection. @@ -78,7 +85,7 @@ func (m *MultiStatusError) Empty() bool { // Add appends a new status error to the collection. // If the error is nil, it is ignored. -func (m *MultiStatusError) Add(err StatusError) { +func (m *MultiStatusError) Add(err Error) { if err == nil { return } @@ -99,10 +106,10 @@ func (m *MultiStatusError) Error() string { } var b strings.Builder - b.WriteString(status.Error2ConditionMsg(m.errs[0])) + b.WriteString(Error2ConditionMsg(m.errs[0])) for _, err := range m.errs[1:] { b.WriteByte('\n') - b.WriteString(status.Error2ConditionMsg(err)) + b.WriteString(Error2ConditionMsg(err)) } return b.String() } diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml index e4abe69436..95dcf655e9 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml @@ -272,9 +272,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: 'Failed to process route rule 0 backendRef 0: invalid backend reference: - IP address 127.0.0.1 in the loopback range is not supported.' - reason: InvalidBackendRef + message: 'Failed to process route rule 0 backendRef 0: IP address 127.0.0.1 + in the loopback range is not supported.' + reason: InvalidAddress status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -310,10 +310,9 @@ httpRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: 'Failed to process route rule 0 backendRef 0: invalid backend reference: - hostname localhost should be a domain with at least two segments separated - by dots.' - reason: InvalidBackendRef + message: 'Failed to process route rule 0 backendRef 0: hostname localhost + should be a domain with at least two segments separated by dots.' + reason: InvalidAddress status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index ee97101255..430b18a205 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -26,7 +26,7 @@ import ( func (t *Translator) validateBackendRef(backendRefContext BackendRefContext, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, backendNamespace string, routeKind gwapiv1.Kind, -) StatusError { +) status.Error { backendRef := GetBackendRef(backendRefContext) if err := t.validateBackendRefFilters(backendRefContext, parentRef, route, routeKind); err != nil { @@ -106,10 +106,10 @@ func errorToMessage(err error) string { return strings.ToUpper(msg[0:1]) + msg[1:] } -func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) StatusError { +func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) status.Error { if backendRef.Group != nil && *backendRef.Group != "" && *backendRef.Group != GroupMultiClusterService && *backendRef.Group != egv1a1.GroupName { routeStatus := GetRouteStatus(route) - status.SetRouteStatusCondition(routeStatus, + status.SetRouteStatusCondition(routeStatus, // TODO: zhaohuabing remove this and handle status in the out layer parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, @@ -117,18 +117,19 @@ func (t *Translator) validateBackendRefGroup(backendRef *gwapiv1a2.BackendRef, p gwapiv1.RouteReasonInvalidKind, fmt.Sprintf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string), %s and %s are supported", GroupMultiClusterService, egv1a1.GroupName), ) - return &RouteStatusError{ - Wrapped: fmt.Errorf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string), %s and %s are supported", GroupMultiClusterService, egv1a1.GroupName), - RouteConditionReason: gwapiv1.RouteReasonInvalidKind, - } + return status.NewRouteStatusError( + fmt.Errorf("Group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string), %s and %s are supported", + GroupMultiClusterService, + egv1a1.GroupName), + gwapiv1.RouteReasonInvalidKind) } return nil } -func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) StatusError { +func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) status.Error { if backendRef.Kind != nil && *backendRef.Kind != resource.KindService && *backendRef.Kind != resource.KindServiceImport && *backendRef.Kind != egv1a1.KindBackend { routeStatus := GetRouteStatus(route) - status.SetRouteStatusCondition(routeStatus, + status.SetRouteStatusCondition(routeStatus, // TODO: zhaohuabing remove this and handle status in the out layer parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, @@ -136,15 +137,14 @@ func (t *Translator) validateBackendRefKind(backendRef *gwapiv1a2.BackendRef, pa gwapiv1.RouteReasonInvalidKind, "Kind is invalid, only Service, MCS ServiceImport and Envoy Gateway Backend are supported", ) - return &RouteStatusError{ - Wrapped: fmt.Errorf("Kind is invalid, only Service, MCS ServiceImport and Envoy Gateway Backend are supported"), - RouteConditionReason: gwapiv1.RouteReasonInvalidKind, - } + return status.NewRouteStatusError( + fmt.Errorf("Kind is invalid, only Service, MCS ServiceImport and Envoy Gateway Backend are supported"), + gwapiv1.RouteReasonInvalidKind) } return nil } -func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, parentRef *RouteParentContext, route RouteContext, routeKind gwapiv1.Kind) StatusError { +func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, parentRef *RouteParentContext, route RouteContext, routeKind gwapiv1.Kind) status.Error { filters := GetFilters(backendRef) var unsupportedFilters bool @@ -173,13 +173,12 @@ func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, par route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - RouteReasonUnsupportedRefValue, + status.RouteReasonUnsupportedRefValue, "Specific filter is not supported within BackendRef, only RequestHeaderModifier and ResponseHeaderModifier are supported", ) - return &RouteStatusError{ - Wrapped: errors.New("specific filter is not supported within BackendRef, only RequestHeaderModifier and ResponseHeaderModifier are supported"), - RouteConditionReason: RouteReasonUnsupportedRefValue, - } + return status.NewRouteStatusError( + errors.New("specific filter is not supported within BackendRef, only RequestHeaderModifier and ResponseHeaderModifier are supported"), + status.RouteReasonUnsupportedRefValue) } return nil @@ -187,7 +186,7 @@ func (t *Translator) validateBackendRefFilters(backendRef BackendRefContext, par func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext, resources *resource.Resources, routeKind gwapiv1.Kind, -) StatusError { +) status.Error { if backendRef.Namespace != nil && string(*backendRef.Namespace) != "" && string(*backendRef.Namespace) != route.GetNamespace() { if !t.validateCrossNamespaceRef( crossNamespaceFrom{ @@ -204,7 +203,7 @@ func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, resources.ReferenceGrants, ) { routeStatus := GetRouteStatus(route) - status.SetRouteStatusCondition(routeStatus, + status.SetRouteStatusCondition(routeStatus, // TODO: zhaohuabing remove this and handle status in the out layer parentRef.routeParentStatusIdx, route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, @@ -212,16 +211,18 @@ func (t *Translator) validateBackendNamespace(backendRef *gwapiv1a2.BackendRef, gwapiv1.RouteReasonRefNotPermitted, fmt.Sprintf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", KindDerefOr(backendRef.Kind, resource.KindService), *backendRef.Namespace, backendRef.Name), ) - return &RouteStatusError{ - Wrapped: fmt.Errorf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", KindDerefOr(backendRef.Kind, resource.KindService), *backendRef.Namespace, backendRef.Name), - RouteConditionReason: gwapiv1.RouteReasonRefNotPermitted, - } + return status.NewRouteStatusError( + fmt.Errorf("Backend ref to %s %s/%s not permitted by any ReferenceGrant.", + KindDerefOr(backendRef.Kind, resource.KindService), + *backendRef.Namespace, + backendRef.Name), + gwapiv1.RouteReasonRefNotPermitted) } } return nil } -func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) StatusError { +func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, parentRef *RouteParentContext, route RouteContext) status.Error { if backendRef == nil { return nil } @@ -238,26 +239,24 @@ func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, paren route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - RouteReasonPortNotSpecified, + status.RouteReasonPortNotSpecified, "A valid port number corresponding to a port on the Service must be specified", ) - return &RouteStatusError{ - Wrapped: errors.New("A valid port number corresponding to a port on the Service must be specified"), - RouteConditionReason: RouteReasonPortNotSpecified, - } + return status.NewRouteStatusError( + errors.New("A valid port number corresponding to a port on the Service must be specified"), + status.RouteReasonPortNotSpecified) } return nil } func validateBackendRefService(backendRef gwapiv1a2.BackendObjectReference, resources *resource.Resources, serviceNamespace string, protocol corev1.Protocol, -) StatusError { +) status.Error { service := resources.GetService(serviceNamespace, string(backendRef.Name)) if service == nil { - return &RouteStatusError{ - Wrapped: fmt.Errorf("service %s/%s not found", serviceNamespace, string(backendRef.Name)), - RouteConditionReason: gwapiv1.RouteReasonBackendNotFound, - } + return status.NewRouteStatusError( + fmt.Errorf("service %s/%s not found", serviceNamespace, string(backendRef.Name)), + gwapiv1.RouteReasonBackendNotFound) } var portFound bool for _, port := range service.Spec.Ports { @@ -272,23 +271,21 @@ func validateBackendRefService(backendRef gwapiv1a2.BackendObjectReference, reso } if !portFound { - return &RouteStatusError{ - Wrapped: fmt.Errorf("%s Port %d not found on Service %s/%s", string(protocol), *backendRef.Port, serviceNamespace, string(backendRef.Name)), - RouteConditionReason: RouteReasonInvalidPortNotFound, - } + return status.NewRouteStatusError( + fmt.Errorf("%s Port %d not found on Service %s/%s", string(protocol), *backendRef.Port, serviceNamespace, string(backendRef.Name)), + status.RouteReasonPortNotFound) } return nil } func (t *Translator) validateBackendServiceImport(backendRef gwapiv1a2.BackendObjectReference, resources *resource.Resources, serviceImportNamespace string, protocol corev1.Protocol, -) StatusError { +) status.Error { serviceImport := resources.GetServiceImport(serviceImportNamespace, string(backendRef.Name)) if serviceImport == nil { - return &RouteStatusError{ - Wrapped: fmt.Errorf("service import %s/%s not found", serviceImportNamespace, backendRef.Name), - RouteConditionReason: gwapiv1.RouteReasonBackendNotFound, - } + return status.NewRouteStatusError( + fmt.Errorf("service import %s/%s not found", serviceImportNamespace, backendRef.Name), + gwapiv1.RouteReasonBackendNotFound) } var portFound bool @@ -304,10 +301,9 @@ func (t *Translator) validateBackendServiceImport(backendRef gwapiv1a2.BackendOb } if !portFound { - return &RouteStatusError{ - Wrapped: fmt.Errorf("%s port %d not found on service import %s/%s", string(protocol), *backendRef.Port, serviceImportNamespace, backendRef.Name), - RouteConditionReason: RouteReasonInvalidPortNotFound, - } + return status.NewRouteStatusError( + fmt.Errorf("%s port %d not found on service import %s/%s", string(protocol), *backendRef.Port, serviceImportNamespace, backendRef.Name), + status.RouteReasonPortNotFound) } return nil @@ -315,35 +311,32 @@ func (t *Translator) validateBackendServiceImport(backendRef gwapiv1a2.BackendOb func (t *Translator) validateBackendRefBackend(backendRef gwapiv1a2.BackendObjectReference, resources *resource.Resources, backendNamespace string, allowUDS bool, -) StatusError { +) status.Error { if !t.BackendEnabled { - return &RouteStatusError{ - Wrapped: errors.New("Backend is disabled in Envoy Gateway configuration"), - RouteConditionReason: gwapiv1.RouteReasonUnsupportedValue, - } + return status.NewRouteStatusError( + errors.New("Backend is disabled in Envoy Gateway configuration"), + gwapiv1.RouteReasonUnsupportedValue, + ) } backend := resources.GetBackend(backendNamespace, string(backendRef.Name)) if backend == nil { - return &RouteStatusError{ - Wrapped: fmt.Errorf("Backend %s/%s not found", backendNamespace, backendRef.Name), - RouteConditionReason: gwapiv1.RouteReasonBackendNotFound, - } + return status.NewRouteStatusError( + fmt.Errorf("Backend %s/%s not found", backendNamespace, backendRef.Name), + gwapiv1.RouteReasonBackendNotFound, + ) } if err := validateBackend(backend); err != nil { - return &RouteStatusError{ - Wrapped: fmt.Errorf("invalid backend reference: %w", err), - RouteConditionReason: RouteReasonInvalidBackendRef, - } + return err } for _, bep := range backend.Spec.Endpoints { if bep.Unix != nil && !allowUDS { - return &RouteStatusError{ - Wrapped: errors.New("unix domain sockets are not supported in backend references"), - RouteConditionReason: RouteReasonInvalidAddressType, - } + return status.NewRouteStatusError( + errors.New("unix domain sockets are not supported in backend references"), + status.RouteReasonInvalidAddressType, + ) } } @@ -1081,6 +1074,10 @@ func (t *Translator) validateExtServiceBackendReference( if backend == nil { return fmt.Errorf("backend %s/%s not found", backendNamespace, backendRef.Name) } + // Dynamic resolver backend is not supported for EG policies + if backend.Spec.Type != nil && *backend.Spec.Type == egv1a1.BackendTypeDynamicResolver { + return fmt.Errorf("dynamic resolver backend %s/%s is not supported", backendNamespace, backendRef.Name) + } } // check if the cross-namespace reference is permitted From fdf6420c2276d82c6f6ee979c12347dd193388d3 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Thu, 17 Apr 2025 04:35:19 +0000 Subject: [PATCH 3/9] fix lint Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/route.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 6d94e4a3c1..741670188f 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -366,7 +366,8 @@ func (t *Translator) processHTTPRouteRule( httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, - rule gwapiv1.HTTPRouteRule) ([]*ir.HTTPRoute, status.Error) { + rule gwapiv1.HTTPRouteRule, +) ([]*ir.HTTPRoute, status.Error) { var ruleRoutes []*ir.HTTPRoute // If no matches are specified, the implementation MUST match every HTTP request. @@ -414,7 +415,7 @@ func (t *Translator) processHTTPRouteRule( *rule.SessionPersistence.CookieConfig.LifetimeType == gwapiv1.PermanentCookieLifetimeType { ttl, err := time.ParseDuration(string(*rule.SessionPersistence.AbsoluteTimeout)) if err != nil { - return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) + return nil, status.NewRouteStatusError(err, gwapiv1.RouteReasonUnsupportedValue) } sessionPersistence.Cookie.TTL = &metav1.Duration{Duration: ttl} } @@ -456,7 +457,7 @@ func (t *Translator) processHTTPRouteRule( } case gwapiv1.PathMatchRegularExpression: if err := regex.Validate(*match.Path.Value); err != nil { - return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) + return nil, status.NewRouteStatusError(err, gwapiv1.RouteReasonUnsupportedValue) } irRoute.PathMatch = &ir.StringMatch{ SafeRegex: match.Path.Value, @@ -472,7 +473,7 @@ func (t *Translator) processHTTPRouteRule( }) case gwapiv1.HeaderMatchRegularExpression: if err := regex.Validate(headerMatch.Value); err != nil { - return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) + return nil, status.NewRouteStatusError(err, gwapiv1.RouteReasonUnsupportedValue) } irRoute.HeaderMatches = append(irRoute.HeaderMatches, &ir.StringMatch{ Name: string(headerMatch.Name), @@ -489,7 +490,7 @@ func (t *Translator) processHTTPRouteRule( }) case gwapiv1.QueryParamMatchRegularExpression: if err := regex.Validate(queryParamMatch.Value); err != nil { - return nil, status.NewRouteStatusError(err,gwapiv1.RouteReasonUnsupportedValue) + return nil, status.NewRouteStatusError(err, gwapiv1.RouteReasonUnsupportedValue) } irRoute.QueryParamMatches = append(irRoute.QueryParamMatches, &ir.StringMatch{ Name: string(queryParamMatch.Name), @@ -1383,13 +1384,13 @@ func (t *Translator) processDestination(name string, backendRefContext BackendRe envoyProxy, ) if tlsErr != nil { - return nil, status.NewRouteStatusError(tlsErr,status.RouteReasonInvalidBackendTLS) + return nil, status.NewRouteStatusError(tlsErr, status.RouteReasonInvalidBackendTLS) } var filtersErr error ds.Filters, filtersErr = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) if filtersErr != nil { - return nil, status.NewRouteStatusError(filtersErr,status.RouteReasonInvalidBackendFilters) + return nil, status.NewRouteStatusError(filtersErr, status.RouteReasonInvalidBackendFilters) } if err := validateDestinationSettings(ds, t.IsEnvoyServiceRouting(envoyProxy), backendRef.Kind); err != nil { From 82b14ab2e4eec2005faaca72070312703496a49c Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Fri, 18 Apr 2025 01:27:35 +0000 Subject: [PATCH 4/9] fix accepted status Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/filters.go | 3 +- internal/gatewayapi/route.go | 7 +-- internal/gatewayapi/status/error.go | 28 +++++++++ ...end-backendrefs-diff-address-type.out.yaml | 2 +- .../httproute-with-invalid-regex.out.yaml | 10 +-- ...ith-mirror-filter-service-no-port.out.yaml | 12 ++-- ...h-mirror-filter-service-not-found.out.yaml | 10 +-- ...h-single-rule-with-multiple-rules.out.yaml | 61 ++----------------- 8 files changed, 56 insertions(+), 77 deletions(-) diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go index b53ae9bcc8..442beb1c88 100644 --- a/internal/gatewayapi/filters.go +++ b/internal/gatewayapi/filters.go @@ -899,7 +899,8 @@ func (t *Translator) processRequestMirrorFilter( err = t.validateBackendRef(mirrorBackendRef, filterContext.ParentRef, filterContext.Route, resources, serviceNamespace, resource.KindHTTPRoute) if err != nil { - return err + return status.NewRouteStatusError( + fmt.Errorf("failed to validate the RequestMirror filter: %w", err), err.Reason()) } destName := fmt.Sprintf("%s-mirror-%d", irRouteDestinationName(filterContext.Route, filterContext.RuleIdx), filterIdx) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 741670188f..03a407e443 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -127,7 +127,7 @@ func (t *Translator) processHTTPRouteParentRefs(httpRoute *HTTPRouteContext, res status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, httpRoute.GetGeneration(), - gwapiv1.RouteConditionResolvedRefs, + err.Type(), metav1.ConditionFalse, err.Reason(), status.Error2ConditionMsg(err), @@ -198,11 +198,10 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe for ruleIdx, rule := range httpRoute.Spec.Rules { httpFiltersContext, err := t.ProcessHTTPFilters(parentRef, httpRoute, rule.Filters, ruleIdx, resources) if err != nil { - // TODO: zhaohuabing should we set Accepted condition instead of Resolved for failed HTTPFilters? errs.Add(status.NewRouteStatusError( fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), err.Reason(), - )) + ).WithType(gwapiv1.RouteConditionAccepted)) continue } @@ -213,7 +212,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe errs.Add(status.NewRouteStatusError( fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), err.Reason(), - )) + ).WithType(gwapiv1.RouteConditionAccepted)) continue } diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 475b3dd9a4..750eea8c6a 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -35,6 +35,7 @@ const ( type Error interface { error Reason() gwapiv1.RouteConditionReason + Type() gwapiv1.RouteConditionType } // RouteStatusError represents an error that needs to be reflected in the status of an xRoute. @@ -42,6 +43,7 @@ type Error interface { type RouteStatusError struct { Wrapped error RouteConditionReason gwapiv1.RouteConditionReason + RouteConditionType gwapiv1.RouteConditionType } // NewRouteStatusError creates a new RouteStatusError with the given wrapped error and route condition reason. @@ -52,6 +54,11 @@ func NewRouteStatusError(wrapped error, reason gwapiv1.RouteConditionReason) *Ro } } +func (s *RouteStatusError) WithType(t gwapiv1.RouteConditionType) *RouteStatusError { + s.RouteConditionType = t + return s +} + // Error returns the string representation of the error. // If Wrapped is nil, it returns the string representation of the RouteConditionReason. func (s *RouteStatusError) Error() string { @@ -72,6 +79,15 @@ func (s *RouteStatusError) Reason() gwapiv1.RouteConditionReason { return s.RouteConditionReason } +// Type returns the route condition type associated with this error. +func (s *RouteStatusError) Type() gwapiv1.RouteConditionType { + // Default to ResolvedRefs because it's the most common type. + if s == nil { + return gwapiv1.RouteConditionResolvedRefs + } + return s.RouteConditionType +} + // MultiStatusError represents a collection of status errors that occurred during processing. // It implements the StatusError interface and provides methods to manage multiple errors. type MultiStatusError struct { @@ -138,3 +154,15 @@ func (m *MultiStatusError) Reason() gwapiv1.RouteConditionReason { } return gwapiv1.RouteConditionReason(strings.Join(reasonList, ", ")) } + +func (m *MultiStatusError) Type() gwapiv1.RouteConditionType { + if m == nil || len(m.errs) == 0 { + return gwapiv1.RouteConditionResolvedRefs + } + for _, err := range m.errs { + if err.Type() == gwapiv1.RouteConditionAccepted { + return gwapiv1.RouteConditionAccepted + } + } + return gwapiv1.RouteConditionResolvedRefs +} diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml index ffadb0735d..279126bed6 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml @@ -131,7 +131,7 @@ httpRoutes: message: |- Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references. Failed to process route rule 0: mixed endpointslice address type between backendRefs is not supported. - reason: InvalidAddressType, InvalidBackendRef + reason: InvalidBackendRef, InvalidAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml index 7ee3d92582..08214ca3b6 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-regex.out.yaml @@ -101,16 +101,16 @@ httpRoutes: status: parents: - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - lastTransitionTime: null message: 'Failed to process route rule 0: regex "*.foo.bar.com" is invalid: error parsing regexp: missing argument to repetition operator: `*`.' reason: UnsupportedValue status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml index 233aad08d0..08a5eab6a2 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -71,13 +71,15 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" + message: 'Failed to process route rule 0: failed to validate the RequestMirror + filter: A valid port number corresponding to a port on the Service must + be specified.' + reason: PortNotSpecified + status: "False" type: Accepted - lastTransitionTime: null - message: 'Failed to process route rule 0: A valid port number corresponding - to a port on the Service must be specified.' + message: A valid port number corresponding to a port on the Service must be + specified reason: PortNotSpecified status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml index c8d89caa06..21a5850fc2 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml @@ -72,13 +72,13 @@ httpRoutes: parents: - conditions: - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" + message: 'Failed to process route rule 0: failed to validate the RequestMirror + filter: service default/service-unknown not found.' + reason: BackendNotFound + status: "False" type: Accepted - lastTransitionTime: null - message: 'Failed to process route rule 0: service default/service-unknown - not found.' + message: Service default/service-unknown not found reason: BackendNotFound status: "False" type: ResolvedRefs diff --git a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml index 6b8d3f89a3..f766dc1a30 100644 --- a/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-single-rule-with-multiple-rules.out.yaml @@ -91,16 +91,16 @@ httpRoutes: status: parents: - conditions: - - lastTransitionTime: null - message: Route is accepted - reason: Accepted - status: "True" - type: Accepted - lastTransitionTime: null message: 'Failed to process route rule 2: regex "*regex*" is invalid: error parsing regexp: missing argument to repetition operator: `*`.' reason: UnsupportedValue status: "False" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -142,57 +142,6 @@ xdsIR: escapedSlashesAction: UnescapeAndRedirect mergeSlashes: true port: 10080 - routes: - - destination: - name: httproute/default/httproute-1/rule/0 - settings: - - addressType: IP - endpoints: - - host: 7.7.7.7 - port: 8080 - name: httproute/default/httproute-1/rule/0/backend/0 - protocol: HTTP - weight: 1 - headerMatches: - - distinct: false - exact: exact - name: Header-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: /exact - name: "" - queryParamMatches: - - distinct: false - exact: exact - name: QueryParam-1 - - destination: - name: httproute/default/httproute-1/rule/1 - settings: - - addressType: IP - endpoints: - - host: 7.7.7.7 - port: 8080 - name: httproute/default/httproute-1/rule/1/backend/0 - protocol: HTTP - weight: 1 - hostname: '*' - isHTTP2: false - metadata: - kind: HTTPRoute - name: httproute-1 - namespace: default - name: httproute/default/httproute-1/rule/1/match/0/* - pathMatch: - distinct: false - name: "" - prefix: /prefix readyListener: address: 0.0.0.0 ipFamily: IPv4 From 363fc474a233323c26b79edfde6b4575e4004318 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Fri, 18 Apr 2025 02:35:00 +0000 Subject: [PATCH 5/9] fix accepted reason Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/route.go | 8 +++---- internal/gatewayapi/status/error.go | 22 ++++++++++++++++++- ...end-backendref-mixed-address-type.out.yaml | 6 ++--- ...-listener-with-backend-backendref.out.yaml | 2 +- ...end-backendrefs-diff-address-type.out.yaml | 6 ++--- ...end-backendrefs-same-address-type.out.yaml | 2 +- ...ort-backendref-mixed-address-type.out.yaml | 2 +- ...-with-invalid-backend-ref-no-port.out.yaml | 2 +- ...ith-mirror-filter-service-no-port.out.yaml | 4 ++-- ...h-mirror-filter-service-not-found.out.yaml | 2 +- internal/gatewayapi/validate.go | 6 ++--- 11 files changed, 41 insertions(+), 21 deletions(-) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 03a407e443..9913322c73 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -200,7 +200,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe if err != nil { errs.Add(status.NewRouteStatusError( fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), - err.Reason(), + status.ConvertToAcceptedReason(err.Reason()), ).WithType(gwapiv1.RouteConditionAccepted)) continue } @@ -211,7 +211,7 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe if err != nil { errs.Add(status.NewRouteStatusError( fmt.Errorf("failed to process route rule %d: %w", ruleIdx, err), - err.Reason(), + status.ConvertToAcceptedReason(err.Reason()), ).WithType(gwapiv1.RouteConditionAccepted)) continue } @@ -1415,13 +1415,13 @@ func validateDestinationSettings(destinationSettings *ir.DestinationSetting, end if destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { return status.NewRouteStatusError( fmt.Errorf("mixed FQDN and IP or Unix address type for the same backendRef is not supported"), - status.RouteReasonInvalidAddressType) + status.RouteReasonUnsupportedAddressType) } case resource.KindService, resource.KindServiceImport: if !endpointRoutingDisabled && destinationSettings.AddressType != nil && *destinationSettings.AddressType == ir.MIXED { return status.NewRouteStatusError( fmt.Errorf("mixed endpointslice address type for the same backendRef is not supported"), - status.RouteReasonInvalidAddressType) + status.RouteReasonUnsupportedAddressType) } } diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 750eea8c6a..4ff274c1de 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -6,6 +6,7 @@ package status import ( + "sort" "strings" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -25,7 +26,7 @@ const ( // Network configuration related reasons RouteReasonPortNotFound gwapiv1.RouteConditionReason = "PortNotFound" RouteReasonPortNotSpecified gwapiv1.RouteConditionReason = "PortNotSpecified" - RouteReasonInvalidAddressType gwapiv1.RouteConditionReason = "InvalidAddressType" + RouteReasonUnsupportedAddressType gwapiv1.RouteConditionReason = "UnsupportedAddressType" RouteReasonInvalidAddress gwapiv1.RouteConditionReason = "InvalidAddress" ) @@ -152,6 +153,7 @@ func (m *MultiStatusError) Reason() gwapiv1.RouteConditionReason { for reason := range reasons { reasonList = append(reasonList, reason) } + sort.Strings(reasonList) return gwapiv1.RouteConditionReason(strings.Join(reasonList, ", ")) } @@ -166,3 +168,21 @@ func (m *MultiStatusError) Type() gwapiv1.RouteConditionType { } return gwapiv1.RouteConditionResolvedRefs } + +func isResolvedRefsReason(reason gwapiv1.RouteConditionReason) bool { + return reason == gwapiv1.RouteReasonRefNotPermitted || + reason == gwapiv1.RouteReasonInvalidKind || + reason == gwapiv1.RouteReasonBackendNotFound || + reason == gwapiv1.RouteReasonUnsupportedProtocol +} + +// ConvertToAcceptedReason converts ResolvedRefs reasons to Accepted condition reasons +// This is used to make the reasons compatible with the Gateway API spec. +// For example, the BackendRefs validation may return a InvalidBackendRef reason for a Mirror filter validation, +// but the Mirror filter failure is reflected in the Accepted condition as UnsupportedValue. +func ConvertToAcceptedReason(reason gwapiv1.RouteConditionReason) gwapiv1.RouteConditionReason { + if isResolvedRefsReason(reason) { + return gwapiv1.RouteReasonUnsupportedValue + } + return reason +} diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml index a1dc518961..495fe02e2e 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref-mixed-address-type.out.yaml @@ -153,7 +153,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -191,7 +191,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: mixed FQDN and IP or Unix address type for the same backendRef is not supported.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -229,7 +229,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: mixed FQDN and IP or Unix address type for the same backendRef is not supported.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml index 95dcf655e9..a6956ae1ca 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-backend-backendref.out.yaml @@ -162,7 +162,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml index 279126bed6..5921a8c47a 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-diff-address-type.out.yaml @@ -131,7 +131,7 @@ httpRoutes: message: |- Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references. Failed to process route rule 0: mixed endpointslice address type between backendRefs is not supported. - reason: InvalidBackendRef, InvalidAddressType + reason: InvalidBackendRef, UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -172,7 +172,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller @@ -254,7 +254,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml index df408f6dc1..965a2ef450 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-multiple-backend-backendrefs-same-address-type.out.yaml @@ -181,7 +181,7 @@ httpRoutes: message: |- Failed to process route rule 0 backendRef 0: unix domain sockets are not supported in backend references. Failed to process route rule 0 backendRef 1: unix domain sockets are not supported in backend references. - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml index 53a87b329d..5e1c6631e7 100644 --- a/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml +++ b/internal/gatewayapi/testdata/httproute-attaching-to-listener-with-serviceimport-backendref-mixed-address-type.out.yaml @@ -71,7 +71,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: mixed endpointslice address type for the same backendRef is not supported.' - reason: InvalidAddressType + reason: UnsupportedAddressType status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml index 928836b561..5ac0c961c8 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml @@ -68,7 +68,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: A valid port number corresponding to a port on the Service must be specified.' - reason: PortNotSpecified + reason: UnsupportedValue status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml index 08a5eab6a2..466200e972 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -74,13 +74,13 @@ httpRoutes: message: 'Failed to process route rule 0: failed to validate the RequestMirror filter: A valid port number corresponding to a port on the Service must be specified.' - reason: PortNotSpecified + reason: UnsupportedValue status: "False" type: Accepted - lastTransitionTime: null message: A valid port number corresponding to a port on the Service must be specified - reason: PortNotSpecified + reason: UnsupportedValue status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml index 21a5850fc2..664ffd08f5 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-not-found.out.yaml @@ -74,7 +74,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0: failed to validate the RequestMirror filter: service default/service-unknown not found.' - reason: BackendNotFound + reason: UnsupportedValue status: "False" type: Accepted - lastTransitionTime: null diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 430b18a205..0452942892 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -239,12 +239,12 @@ func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, paren route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - status.RouteReasonPortNotSpecified, + gwapiv1.RouteReasonUnsupportedValue, "A valid port number corresponding to a port on the Service must be specified", ) return status.NewRouteStatusError( errors.New("A valid port number corresponding to a port on the Service must be specified"), - status.RouteReasonPortNotSpecified) + gwapiv1.RouteReasonUnsupportedValue) } return nil } @@ -335,7 +335,7 @@ func (t *Translator) validateBackendRefBackend(backendRef gwapiv1a2.BackendObjec if bep.Unix != nil && !allowUDS { return status.NewRouteStatusError( errors.New("unix domain sockets are not supported in backend references"), - status.RouteReasonInvalidAddressType, + status.RouteReasonUnsupportedAddressType, ) } } From de36725cf0cc69b9a6bcf2142b10da8f3c719ef7 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Fri, 18 Apr 2025 02:35:33 +0000 Subject: [PATCH 6/9] fix accepted reason Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/status/error.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 4ff274c1de..8d4f34c315 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -24,10 +24,10 @@ const ( RouteReasonInvalidBackendFilters gwapiv1.RouteConditionReason = "InvalidBackendFilters" // Network configuration related reasons - RouteReasonPortNotFound gwapiv1.RouteConditionReason = "PortNotFound" - RouteReasonPortNotSpecified gwapiv1.RouteConditionReason = "PortNotSpecified" + RouteReasonPortNotFound gwapiv1.RouteConditionReason = "PortNotFound" + RouteReasonPortNotSpecified gwapiv1.RouteConditionReason = "PortNotSpecified" RouteReasonUnsupportedAddressType gwapiv1.RouteConditionReason = "UnsupportedAddressType" - RouteReasonInvalidAddress gwapiv1.RouteConditionReason = "InvalidAddress" + RouteReasonInvalidAddress gwapiv1.RouteConditionReason = "InvalidAddress" ) // Error is an error interface that represents errors that need to be reflected From 00e254c327efd8cb23da5b9f2156ed2be0c75deb Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Fri, 18 Apr 2025 03:41:27 +0000 Subject: [PATCH 7/9] fix accepted reason Signed-off-by: Huabing (Robin) Zhao --- .../httproute-with-invalid-backend-ref-no-port.out.yaml | 2 +- .../httproute-with-mirror-filter-service-no-port.out.yaml | 4 ++-- internal/gatewayapi/validate.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml index 5ac0c961c8..928836b561 100644 --- a/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-invalid-backend-ref-no-port.out.yaml @@ -68,7 +68,7 @@ httpRoutes: - lastTransitionTime: null message: 'Failed to process route rule 0 backendRef 0: A valid port number corresponding to a port on the Service must be specified.' - reason: UnsupportedValue + reason: PortNotSpecified status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml index 466200e972..08a5eab6a2 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -74,13 +74,13 @@ httpRoutes: message: 'Failed to process route rule 0: failed to validate the RequestMirror filter: A valid port number corresponding to a port on the Service must be specified.' - reason: UnsupportedValue + reason: PortNotSpecified status: "False" type: Accepted - lastTransitionTime: null message: A valid port number corresponding to a port on the Service must be specified - reason: UnsupportedValue + reason: PortNotSpecified status: "False" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 0452942892..5e1751b059 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -239,12 +239,12 @@ func (t *Translator) validateBackendPort(backendRef *gwapiv1a2.BackendRef, paren route.GetGeneration(), gwapiv1.RouteConditionResolvedRefs, metav1.ConditionFalse, - gwapiv1.RouteReasonUnsupportedValue, + status.RouteReasonPortNotSpecified, "A valid port number corresponding to a port on the Service must be specified", ) return status.NewRouteStatusError( errors.New("A valid port number corresponding to a port on the Service must be specified"), - gwapiv1.RouteReasonUnsupportedValue) + status.RouteReasonPortNotSpecified) } return nil } From 08f5a0ad5962a09e39af29c414c6c0918fbfa26d Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Fri, 18 Apr 2025 03:53:54 +0000 Subject: [PATCH 8/9] fix accepted reason Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/status/error.go | 20 ++++++++++--------- ...ith-mirror-filter-service-no-port.out.yaml | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 8d4f34c315..92c9ab62a3 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -169,20 +169,22 @@ func (m *MultiStatusError) Type() gwapiv1.RouteConditionType { return gwapiv1.RouteConditionResolvedRefs } -func isResolvedRefsReason(reason gwapiv1.RouteConditionReason) bool { - return reason == gwapiv1.RouteReasonRefNotPermitted || - reason == gwapiv1.RouteReasonInvalidKind || - reason == gwapiv1.RouteReasonBackendNotFound || - reason == gwapiv1.RouteReasonUnsupportedProtocol +func isAcceptedReason(reason gwapiv1.RouteConditionReason) bool { + return reason == gwapiv1.RouteReasonNotAllowedByListeners || + reason == gwapiv1.RouteReasonNoMatchingListenerHostname || + reason == gwapiv1.RouteReasonNoMatchingParent || + reason == gwapiv1.RouteReasonUnsupportedValue } // ConvertToAcceptedReason converts ResolvedRefs reasons to Accepted condition reasons // This is used to make the reasons compatible with the Gateway API spec. // For example, the BackendRefs validation may return a InvalidBackendRef reason for a Mirror filter validation, -// but the Mirror filter failure is reflected in the Accepted condition as UnsupportedValue. +// but this erros should be reflected in the Accepted condition as UnsupportedValue. func ConvertToAcceptedReason(reason gwapiv1.RouteConditionReason) gwapiv1.RouteConditionReason { - if isResolvedRefsReason(reason) { - return gwapiv1.RouteReasonUnsupportedValue + if isAcceptedReason(reason) { + return reason } - return reason + // Return UnsupportedValue as the default reason for ResolvedRefs reasons, which is kind of vague and can be used for + // any other reasons. + return gwapiv1.RouteReasonUnsupportedValue } diff --git a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml index 08a5eab6a2..d2605efc0a 100644 --- a/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-mirror-filter-service-no-port.out.yaml @@ -74,7 +74,7 @@ httpRoutes: message: 'Failed to process route rule 0: failed to validate the RequestMirror filter: A valid port number corresponding to a port on the Service must be specified.' - reason: PortNotSpecified + reason: UnsupportedValue status: "False" type: Accepted - lastTransitionTime: null From 39a29a679ff020440fa0b538dbc7c7f226527f99 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Fri, 18 Apr 2025 04:03:14 +0000 Subject: [PATCH 9/9] fix lint Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/status/error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go index 92c9ab62a3..bcad0e7510 100644 --- a/internal/gatewayapi/status/error.go +++ b/internal/gatewayapi/status/error.go @@ -179,7 +179,7 @@ func isAcceptedReason(reason gwapiv1.RouteConditionReason) bool { // ConvertToAcceptedReason converts ResolvedRefs reasons to Accepted condition reasons // This is used to make the reasons compatible with the Gateway API spec. // For example, the BackendRefs validation may return a InvalidBackendRef reason for a Mirror filter validation, -// but this erros should be reflected in the Accepted condition as UnsupportedValue. +// but this error should be reflected in the Accepted condition as UnsupportedValue. func ConvertToAcceptedReason(reason gwapiv1.RouteConditionReason) gwapiv1.RouteConditionReason { if isAcceptedReason(reason) { return reason