Skip to content

Commit

Permalink
feat(translator): Implement BTP Timeout API (envoyproxy#2454)
Browse files Browse the repository at this point in the history
Implement BTP Timeout API

Signed-off-by: Guy Daich <guy.daich@sap.com>
  • Loading branch information
guydc authored Jan 29, 2024
1 parent b8cbee2 commit f1a0a42
Show file tree
Hide file tree
Showing 24 changed files with 1,660 additions and 44 deletions.
104 changes: 99 additions & 5 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net"
"sort"
"strings"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -248,6 +249,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
hc *ir.HealthCheck
cb *ir.CircuitBreaker
fi *ir.FaultInjection
to *ir.Timeout
)

// Build IR
Expand Down Expand Up @@ -283,6 +285,12 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
r.HealthCheck = hc
r.CircuitBreaker = cb
r.FaultInjection = fi

// some timeout setting originate from the route
if policy.Spec.Timeout != nil {
to = t.buildTimeout(policy, r)
r.Timeout = to
}
}
}
}
Expand All @@ -298,6 +306,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
hc *ir.HealthCheck
cb *ir.CircuitBreaker
fi *ir.FaultInjection
ct *ir.Timeout
)

// Build IR
Expand Down Expand Up @@ -348,6 +357,13 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
if r.FaultInjection == nil {
r.FaultInjection = fi
}

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

}
Expand Down Expand Up @@ -776,7 +792,7 @@ func (t *Translator) buildCircuitBreaker(policy *egv1a1.BackendTrafficPolicy) *i
if ui32, ok := int64ToUint32(*pcb.MaxConnections); ok {
cb.MaxConnections = &ui32
} else {
setCircuitBreakerPolicyErrorCondition(policy, fmt.Sprintf("invalid MaxConnections value %d", *pcb.MaxConnections))
setBackendTrafficPolicyTranslationErrorCondition(policy, "Circuit Breaker", fmt.Sprintf("invalid MaxConnections value %d", *pcb.MaxConnections))
return nil
}
}
Expand All @@ -785,7 +801,7 @@ func (t *Translator) buildCircuitBreaker(policy *egv1a1.BackendTrafficPolicy) *i
if ui32, ok := int64ToUint32(*pcb.MaxParallelRequests); ok {
cb.MaxParallelRequests = &ui32
} else {
setCircuitBreakerPolicyErrorCondition(policy, fmt.Sprintf("invalid MaxParallelRequests value %d", *pcb.MaxParallelRequests))
setBackendTrafficPolicyTranslationErrorCondition(policy, "Circuit Breaker", fmt.Sprintf("invalid MaxParallelRequests value %d", *pcb.MaxParallelRequests))
return nil
}
}
Expand All @@ -794,7 +810,7 @@ func (t *Translator) buildCircuitBreaker(policy *egv1a1.BackendTrafficPolicy) *i
if ui32, ok := int64ToUint32(*pcb.MaxPendingRequests); ok {
cb.MaxPendingRequests = &ui32
} else {
setCircuitBreakerPolicyErrorCondition(policy, fmt.Sprintf("invalid MaxPendingRequests value %d", *pcb.MaxPendingRequests))
setBackendTrafficPolicyTranslationErrorCondition(policy, "Circuit Breaker", fmt.Sprintf("invalid MaxPendingRequests value %d", *pcb.MaxPendingRequests))
return nil
}
}
Expand All @@ -803,8 +819,86 @@ func (t *Translator) buildCircuitBreaker(policy *egv1a1.BackendTrafficPolicy) *i
return cb
}

func setCircuitBreakerPolicyErrorCondition(policy *egv1a1.BackendTrafficPolicy, errMsg string) {
message := fmt.Sprintf("Unable to translate Circuit Breaker: %s", errMsg)
func (t *Translator) buildTimeout(policy *egv1a1.BackendTrafficPolicy, r *ir.HTTPRoute) *ir.Timeout {
var tto *ir.TCPTimeout
var hto *ir.HTTPTimeout
terr := false

pto := policy.Spec.Timeout

if pto.TCP != nil && pto.TCP.ConnectTimeout != nil {
d, err := time.ParseDuration(string(*pto.TCP.ConnectTimeout))
if err != nil {
setBackendTrafficPolicyTranslationErrorCondition(policy, "TCP Timeout", fmt.Sprintf("invalid ConnectTimeout value %s", *pto.TCP.ConnectTimeout))
terr = true
} else {
tto = &ir.TCPTimeout{
ConnectTimeout: ptr.To(metav1.Duration{Duration: d}),
}
}
}

if pto.HTTP != nil {
var cit *metav1.Duration
var mcd *metav1.Duration

if pto.HTTP.ConnectionIdleTimeout != nil {
d, err := time.ParseDuration(string(*pto.HTTP.ConnectionIdleTimeout))
if err != nil {
setBackendTrafficPolicyTranslationErrorCondition(policy, "HTTP Timeout", fmt.Sprintf("invalid ConnectionIdleTimeout value %s", *pto.HTTP.ConnectionIdleTimeout))
terr = true
} else {
cit = ptr.To(metav1.Duration{Duration: d})
}
}

if pto.HTTP.MaxConnectionDuration != nil {
d, err := time.ParseDuration(string(*pto.HTTP.MaxConnectionDuration))
if err != nil {
setBackendTrafficPolicyTranslationErrorCondition(policy, "HTTP Timeout", fmt.Sprintf("invalid MaxConnectionDuration value %s", *pto.HTTP.MaxConnectionDuration))
terr = true
} else {
mcd = ptr.To(metav1.Duration{Duration: d})
}
}

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

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

if hto != nil || tto != nil {
return &ir.Timeout{
TCP: tto,
HTTP: hto,
}
}
}

return nil
}

func setBackendTrafficPolicyTranslationErrorCondition(policy *egv1a1.BackendTrafficPolicy, field, errMsg string) {
message := fmt.Sprintf("Unable to translate %s: %s", field, errMsg)
status.SetBackendTrafficPolicyCondition(policy,
gwv1a2.PolicyConditionAccepted,
metav1.ConditionFalse,
Expand Down
34 changes: 31 additions & 3 deletions internal/gatewayapi/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,21 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe

func processTimeout(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.Timeout != nil {
rto = irRoute.Timeout.DeepCopy()
} else {
rto = &ir.Timeout{}
}

if rule.Timeouts.Request != nil {
d, err := time.ParseDuration(string(*rule.Timeouts.Request))
if err != nil {
d, _ = time.ParseDuration(HTTPRequestTimeout)
}
irRoute.Timeout = ptr.To(metav1.Duration{Duration: d})
setRequestTimeout(rto, metav1.Duration{Duration: d})
}

// Also set the IR Route Timeout to the backend request timeout
Expand All @@ -242,8 +251,27 @@ func processTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
if err != nil {
d, _ = time.ParseDuration(HTTPRequestTimeout)
}
irRoute.Timeout = ptr.To(metav1.Duration{Duration: d})
setRequestTimeout(rto, metav1.Duration{Duration: d})
}

irRoute.Timeout = rto
}
}

func setRequestTimeout(irTimeout *ir.Timeout, d metav1.Duration) {
switch {
case irTimeout == nil:
irTimeout = &ir.Timeout{
HTTP: &ir.HTTPTimeout{
RequestTimeout: ptr.To(d),
},
}
case irTimeout.HTTP == nil:
irTimeout.HTTP = &ir.HTTPTimeout{
RequestTimeout: ptr.To(d),
}
default:
irTimeout.HTTP.RequestTimeout = ptr.To(d)
}
}

Expand Down Expand Up @@ -627,8 +655,8 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route
DirectResponse: routeRoute.DirectResponse,
URLRewrite: routeRoute.URLRewrite,
Mirrors: routeRoute.Mirrors,
Timeout: routeRoute.Timeout,
ExtensionRefs: routeRoute.ExtensionRefs,
Timeout: routeRoute.Timeout,
}
// Don't bother copying over the weights unless the route has invalid backends.
if routeRoute.BackendWeights.Invalid > 0 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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
grpcRoutes:
- apiVersion: gateway.networking.k8s.io/v1alpha2
kind: GRPCRoute
metadata:
namespace: default
name: grpcroute-1
spec:
parentRefs:
- namespace: envoy-gateway
name: gateway-1
sectionName: http
rules:
- backendRefs:
- name: service-1
port: 8080
backendTrafficPolicies:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: envoy-gateway
name: policy-for-gateway
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: gateway-1
namespace: envoy-gateway
timeout:
http:
connectionIdleTimeout: 21s
maxConnectionDuration: 22mib
tcp:
connectTimeout: 20s

Loading

0 comments on commit f1a0a42

Please sign in to comment.