From d8f1487257b5c5a7e273cbb90729a801190673d6 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 18 Sep 2023 17:06:00 -0700 Subject: [PATCH] feat Route Timeouts Fixes: https://github.com/envoyproxy/gateway/issues/877 Signed-off-by: Arko Dasgupta --- internal/gatewayapi/route.go | 23 +++++++++++++++++++++++ internal/ir/xds.go | 5 +++++ internal/xds/translator/route.go | 11 +++++++++++ 3 files changed, 39 insertions(+) diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index e239b333cc46..9ba56d27c845 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -8,7 +8,9 @@ package gatewayapi import ( "fmt" "strings" + "time" + "google.golang.org/protobuf/types/known/durationpb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/apis/v1beta1" @@ -185,6 +187,22 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe return routeRoutes } +func processTimeout(irRoute *ir.HTTPRoute, rule v1beta1.HTTPRouteRule) { + if rule.Timeouts != nil { + if rule.Timeouts.Request != nil { + // TODO: handle parse errors + d, _ := time.ParseDuration(string(*rule.Timeouts.Request)) + irRoute.Timeout = durationpb.New(d) + } + + if rule.Timeouts.BackendRequest != nil { + // TODO: handle parse errors + d, _ := time.ParseDuration(string(*rule.Timeouts.BackendRequest)) + irRoute.PerTryTimeout = durationpb.New(d) + } + } +} + func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx int, httpFiltersContext *HTTPFiltersContext, rule v1beta1.HTTPRouteRule) []*ir.HTTPRoute { var ruleRoutes []*ir.HTTPRoute @@ -193,6 +211,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i irRoute := &ir.HTTPRoute{ Name: irRouteName(httpRoute, ruleIdx, -1), } + processTimeout(irRoute, rule) applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute) ruleRoutes = append(ruleRoutes, irRoute) } @@ -204,6 +223,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i irRoute := &ir.HTTPRoute{ Name: irRouteName(httpRoute, ruleIdx, matchIdx), } + processTimeout(irRoute, rule) if match.Path != nil { switch PathMatchTypeDerefOr(match.Path.Type, v1beta1.PathMatchPathPrefix) { @@ -295,6 +315,7 @@ func applyHTTPFiltersContextToIRRoute(httpFiltersContext *HTTPFiltersContext, ir if httpFiltersContext.RateLimit != nil { irRoute.RateLimit = httpFiltersContext.RateLimit } + if len(httpFiltersContext.ExtensionRefs) > 0 { irRoute.ExtensionRefs = httpFiltersContext.ExtensionRefs } @@ -536,6 +557,8 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route Mirrors: routeRoute.Mirrors, RequestAuthentication: routeRoute.RequestAuthentication, RateLimit: routeRoute.RateLimit, + Timeout: routeRoute.Timeout, + PerTryTimeout: routeRoute.PerTryTimeout, ExtensionRefs: routeRoute.ExtensionRefs, } // Don't bother copying over the weights unless the route has invalid backends. diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 21333e200723..e34a0fa5a476 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -13,6 +13,7 @@ import ( "github.com/tetratelabs/multierror" "golang.org/x/exp/slices" + "google.golang.org/protobuf/types/known/durationpb" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -273,6 +274,10 @@ type HTTPRoute struct { RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` // RequestAuthentication defines the schema for authenticating HTTP requests. RequestAuthentication *RequestAuthentication `json:"requestAuthentication,omitempty" yaml:"requestAuthentication,omitempty"` + // Timeout is the time until which entire response is received from the upstream. + Timeout *durationpb.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` + // PerTryTimeout is the timeout per retry attempt. Number of retries defaults to 1. + PerTryTimeout *durationpb.Duration `json:"perTryTimeout,omitempty" yaml:"perTryTimeout,omitempty"` // ExtensionRefs holds unstructured resources that were introduced by an extension and used on the HTTPRoute as extensionRef filters ExtensionRefs []*UnstructuredRef `json:"extensionRefs,omitempty" yaml:"extensionRefs,omitempty"` } diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go index bcbe33d7f17b..bee565f7ee9b 100644 --- a/internal/xds/translator/route.go +++ b/internal/xds/translator/route.go @@ -65,6 +65,17 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) *routev3.Route { } } + // Timeouts + if httpRoute.Timeout != nil { + router.GetRoute().Timeout = httpRoute.Timeout + } + + if httpRoute.PerTryTimeout != nil { + router.GetRoute().RetryPolicy = &routev3.RetryPolicy{ + PerTryTimeout: httpRoute.PerTryTimeout, + } + } + // TODO: Convert this into a generic interface for API Gateway features. // https://github.com/envoyproxy/gateway/issues/882 if err := patchRouteWithRateLimit(router.GetRoute(), httpRoute); err != nil {