Skip to content

Commit

Permalink
[release/v1.1] fix: don't lose timeout settings that originate from t…
Browse files Browse the repository at this point in the history
…he route when t… (#4450)

fix: don't lose timeout settings that originate from the route when translating the backend traffic policy (#4095) (#85)

* Don't lose timeout settings that originate from the route when
translating the backend traffic policy



* Improve the readability of the timeout processing code.



* Removed some logically unreachable code.



---------


(cherry picked from commit 262e046)

Signed-off-by: Lior Okman <lior.okman@sap.com>
Signed-off-by: Huabing Zhao <zhaohuabing@gmail.com>
Co-authored-by: Lior Okman <lior.okman@sap.com>
  • Loading branch information
zhaohuabing and liorokman authored Oct 16, 2024
1 parent 70b1697 commit 7391c1c
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 64 deletions.
105 changes: 52 additions & 53 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
rt = t.buildRetry(policy)
}
if policy.Spec.Timeout != nil {
if to, err = t.buildTimeout(policy, nil); err != nil {
if to, err = t.buildTimeout(*policy, nil); err != nil {
err = perr.WithMessage(err, "Timeout")
errs = errors.Join(errs, err)
}
Expand Down Expand Up @@ -411,6 +411,11 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen

for _, http := range x.HTTP {
for _, r := range http.Routes {
// Some timeout setting originate from the route.
if localTo, err := t.buildTimeout(*policy, r.Traffic); err == nil {
to = localTo
}

// Apply if there is a match
if strings.HasPrefix(r.Name, prefix) {
r.Traffic = &ir.TrafficFeatures{
Expand All @@ -423,18 +428,12 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
TCPKeepalive: ka,
Retry: rt,
BackendConnection: bc,
Timeout: to,
}

// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)

// Some timeout setting originate from the route.
if policy.Spec.Timeout != nil {
if to, err = t.buildTimeout(policy, r); err == nil {
r.Traffic.Timeout = to
}
}

if policy.Spec.UseClientProtocol != nil {
r.UseClientProtocol = policy.Spec.UseClientProtocol
}
Expand Down Expand Up @@ -498,7 +497,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
rt = t.buildRetry(policy)
}
if policy.Spec.Timeout != nil {
if ct, err = t.buildTimeout(policy, nil); err != nil {
if ct, err = t.buildTimeout(*policy, nil); err != nil {
err = perr.WithMessage(err, "Timeout")
errs = errors.Join(errs, err)
}
Expand Down Expand Up @@ -596,10 +595,8 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)

if policy.Spec.Timeout != nil {
if ct, err = t.buildTimeout(policy, r); err == nil {
r.Traffic.Timeout = ct
}
if ct, err = t.buildTimeout(*policy, r.Traffic); err == nil {
r.Traffic.Timeout = ct
}

if policy.Spec.UseClientProtocol != nil {
Expand Down Expand Up @@ -1069,23 +1066,26 @@ func (t *Translator) buildCircuitBreaker(policy *egv1a1.BackendTrafficPolicy) (*
return cb, nil
}

func (t *Translator) buildTimeout(policy *egv1a1.BackendTrafficPolicy, r *ir.HTTPRoute) (*ir.Timeout, error) {
func (t *Translator) buildTimeout(policy egv1a1.BackendTrafficPolicy, traffic *ir.TrafficFeatures) (*ir.Timeout, error) {
if policy.Spec.Timeout == nil {
if traffic != nil {
// Don't lose any existing timeout definitions.
return mergeTimeoutSettings(nil, traffic.Timeout), nil
}
return nil, nil
}
var (
tto *ir.TCPTimeout
hto *ir.HTTPTimeout
terr bool
errs error
to = &ir.Timeout{}
pto = policy.Spec.Timeout
)

pto := policy.Spec.Timeout

if pto.TCP != nil && pto.TCP.ConnectTimeout != nil {
d, err := time.ParseDuration(string(*pto.TCP.ConnectTimeout))
if err != nil {
terr = true
errs = errors.Join(errs, fmt.Errorf("invalid ConnectTimeout value %s", *pto.TCP.ConnectTimeout))
} else {
tto = &ir.TCPTimeout{
to.TCP = &ir.TCPTimeout{
ConnectTimeout: ptr.To(metav1.Duration{Duration: d}),
}
}
Expand All @@ -1098,7 +1098,6 @@ func (t *Translator) buildTimeout(policy *egv1a1.BackendTrafficPolicy, r *ir.HTT
if pto.HTTP.ConnectionIdleTimeout != nil {
d, err := time.ParseDuration(string(*pto.HTTP.ConnectionIdleTimeout))
if err != nil {
terr = true
errs = errors.Join(errs, fmt.Errorf("invalid ConnectionIdleTimeout value %s", *pto.HTTP.ConnectionIdleTimeout))
} else {
cit = ptr.To(metav1.Duration{Duration: d})
Expand All @@ -1108,51 +1107,51 @@ func (t *Translator) buildTimeout(policy *egv1a1.BackendTrafficPolicy, r *ir.HTT
if pto.HTTP.MaxConnectionDuration != nil {
d, err := time.ParseDuration(string(*pto.HTTP.MaxConnectionDuration))
if err != nil {
terr = true
errs = errors.Join(errs, fmt.Errorf("invalid MaxConnectionDuration value %s", *pto.HTTP.MaxConnectionDuration))
} else {
mcd = ptr.To(metav1.Duration{Duration: d})
}
}

hto = &ir.HTTPTimeout{
to.HTTP = &ir.HTTPTimeout{
ConnectionIdleTimeout: cit,
MaxConnectionDuration: mcd,
}
}

// http request timeout is translated during the gateway-api route resource translation
// merge route timeout setting with backendtrafficpolicy timeout settings
if terr {
if r != nil && r.Traffic != nil && r.Traffic.Timeout != nil {
return r.Traffic.Timeout.DeepCopy(), errs
}
} else {
// http request timeout is translated during the gateway-api route resource translation
// merge route timeout setting with backendtrafficpolicy timeout settings
if r != nil &&
r.Traffic != nil &&
r.Traffic.Timeout != nil &&
r.Traffic.Timeout.HTTP != nil &&
r.Traffic.Timeout.HTTP.RequestTimeout != nil {
if hto == nil {
hto = &ir.HTTPTimeout{
RequestTimeout: r.Traffic.Timeout.HTTP.RequestTimeout,
}
} else {
hto.RequestTimeout = r.Traffic.Timeout.HTTP.RequestTimeout
}
}

if hto != nil || tto != nil {
return &ir.Timeout{
TCP: tto,
HTTP: hto,
}, nil
}
// merge route timeout setting with backendtrafficpolicy timeout settings.
// Merging is done after the clustersettings definitions are translated so that
// clustersettings will override previous settings.
if traffic != nil {
to = mergeTimeoutSettings(to, traffic.Timeout)
}
return to, errs
}

return nil, errs
// merge secondary into main if both are not nil, otherwise return the
// one that is not nil. If both are nil, returns nil
func mergeTimeoutSettings(main, secondary *ir.Timeout) *ir.Timeout {
switch {
case main == nil && secondary == nil:
return nil
case main == nil:
return secondary.DeepCopy()
case secondary == nil:
return main
default: // Neither main nor secondary are nil here
if secondary.HTTP != nil {
setIfNil(&main.HTTP, &ir.HTTPTimeout{})
setIfNil(&main.HTTP.RequestTimeout, secondary.HTTP.RequestTimeout)
setIfNil(&main.HTTP.ConnectionIdleTimeout, secondary.HTTP.ConnectionIdleTimeout)
setIfNil(&main.HTTP.MaxConnectionDuration, secondary.HTTP.MaxConnectionDuration)
}
if secondary.TCP != nil {
setIfNil(&main.TCP, &ir.TCPTimeout{})
setIfNil(&main.TCP.ConnectTimeout, secondary.TCP.ConnectTimeout)
}
return main
}
}

func int64ToUint32(in int64) (uint32, bool) {
Expand Down
7 changes: 7 additions & 0 deletions internal/gatewayapi/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,10 @@ func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences,

return ret
}

// Sets *target to value if and only if *target is nil
func setIfNil[T any](target **T, value *T) {
if *target == nil {
*target = value
}
}
15 changes: 4 additions & 11 deletions internal/gatewayapi/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,9 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
return routeRoutes, nil
}

func processTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
func processRouteTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
if rule.Timeouts != nil {
var rto *ir.Timeout

// Timeout is translated from multiple resources and may already be partially set
if irRoute.Traffic != nil && irRoute.Traffic.Timeout != nil {
rto = irRoute.Traffic.Timeout.DeepCopy()
} else {
rto = &ir.Timeout{}
}
rto := &ir.Timeout{}

if rule.Timeouts.Request != nil {
d, err := time.ParseDuration(string(*rule.Timeouts.Request))
Expand Down Expand Up @@ -309,7 +302,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i
irRoute := &ir.HTTPRoute{
Name: irRouteName(httpRoute, ruleIdx, -1),
}
processTimeout(irRoute, rule)
processRouteTimeout(irRoute, rule)
applyHTTPFiltersContextToIRRoute(httpFiltersContext, irRoute)
ruleRoutes = append(ruleRoutes, irRoute)
}
Expand All @@ -321,7 +314,7 @@ func (t *Translator) processHTTPRouteRule(httpRoute *HTTPRouteContext, ruleIdx i
irRoute := &ir.HTTPRoute{
Name: irRouteName(httpRoute, ruleIdx, matchIdx),
}
processTimeout(irRoute, rule)
processRouteTimeout(irRoute, rule)

if match.Path != nil {
switch PathMatchTypeDerefOr(match.Path.Type, gwapiv1.PathMatchPathPrefix) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-1
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: default
name: httproute-1
spec:
hostnames:
- gateway.envoyproxy.io
parentRefs:
- namespace: envoy-gateway
name: gateway-1
sectionName: http
rules:
- matches:
- path:
value: "/"
backendRefs:
- name: service-1
port: 8080
timeouts:
request: 130s
backendTrafficPolicies:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: default
name: policy-for-http-route-1
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: httproute-1
useClientProtocol: true

Loading

0 comments on commit 7391c1c

Please sign in to comment.