Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(translator): Implement Retry in BTP #2678

Merged
merged 4 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/v1alpha1/retry_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type RetryOn struct {
Triggers []TriggerEnum `json:"triggers,omitempty"`

// HttpStatusCodes specifies the http status codes to be retried.
// The retriable-status-codes trigger must also be configured for these status codes to trigger a retry.
//
// +optional
HTTPStatusCodes []HTTPStatus `json:"httpStatusCodes,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,8 @@ spec:
properties:
httpStatusCodes:
description: HttpStatusCodes specifies the http status codes
to be retried.
to be retried. The retriable-status-codes trigger must also
be configured for these status codes to trigger a retry.
items:
description: HTTPStatus defines the http status code.
exclusiveMaximum: true
Expand Down
99 changes: 99 additions & 0 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
fi *ir.FaultInjection
to *ir.Timeout
ka *ir.TCPKeepalive
rt *ir.Retry
)

// Build IR
Expand All @@ -276,6 +277,9 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
if policy.Spec.TCPKeepalive != nil {
ka = t.buildTCPKeepAlive(policy)
}
if policy.Spec.Retry != nil {
rt = t.buildRetry(policy)
}
// Apply IR to all relevant routes
prefix := irRoutePrefix(route)
for _, ir := range xdsIR {
Expand All @@ -290,6 +294,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
r.CircuitBreaker = cb
r.FaultInjection = fi
r.TCPKeepalive = ka
r.Retry = rt

// some timeout setting originate from the route
if policy.Spec.Timeout != nil {
Expand All @@ -313,6 +318,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
fi *ir.FaultInjection
ct *ir.Timeout
ka *ir.TCPKeepalive
rt *ir.Retry
)

// Build IR
Expand All @@ -337,6 +343,9 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
if policy.Spec.TCPKeepalive != nil {
ka = t.buildTCPKeepAlive(policy)
}
if policy.Spec.Retry != nil {
rt = t.buildRetry(policy)
}

// Apply IR to all the routes within the specific Gateway
// If the feature is already set, then skip it, since it must be have
Expand Down Expand Up @@ -369,6 +378,9 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
if r.TCPKeepalive == nil {
r.TCPKeepalive = ka
}
if r.Retry == nil {
r.Retry = rt
}

if policy.Spec.Timeout != nil {
ct = t.buildTimeout(policy, r)
Expand Down Expand Up @@ -1016,3 +1028,90 @@ func (t *Translator) buildTCPKeepAlive(policy *egv1a1.BackendTrafficPolicy) *ir.
}
return ka
}

func (t *Translator) buildRetry(policy *egv1a1.BackendTrafficPolicy) *ir.Retry {
var rt *ir.Retry
if policy.Spec.Retry != nil {
prt := policy.Spec.Retry
rt = &ir.Retry{}

if prt.NumRetries != nil {
rt.NumRetries = ptr.To(uint32(*prt.NumRetries))
}

if prt.RetryOn != nil {
ro := &ir.RetryOn{}
bro := false
if prt.RetryOn.HTTPStatusCodes != nil {
ro.HTTPStatusCodes = makeIrStatusSet(prt.RetryOn.HTTPStatusCodes)
bro = true
}

if prt.RetryOn.Triggers != nil {
ro.Triggers = makeIrTriggerSet(prt.RetryOn.Triggers)
bro = true
}

if bro {
rt.RetryOn = ro
}
}

if prt.PerRetry != nil {
pr := &ir.PerRetryPolicy{}
bpr := false

if prt.PerRetry.Timeout != nil {
pr.Timeout = prt.PerRetry.Timeout
bpr = true
}

if prt.PerRetry.BackOff != nil {
if prt.PerRetry.BackOff.MaxInterval != nil || prt.PerRetry.BackOff.BaseInterval != nil {
bop := &ir.BackOffPolicy{}
if prt.PerRetry.BackOff.MaxInterval != nil {
bop.MaxInterval = prt.PerRetry.BackOff.MaxInterval
}

if prt.PerRetry.BackOff.BaseInterval != nil {
bop.BaseInterval = prt.PerRetry.BackOff.BaseInterval
}
pr.BackOff = bop
bpr = true
}
}

if bpr {
rt.PerRetry = pr
}
}
}

return rt
}

func makeIrStatusSet(in []egv1a1.HTTPStatus) []ir.HTTPStatus {
var irStatuses []ir.HTTPStatus
// deduplicate http statuses
statusSet := make(map[egv1a1.HTTPStatus]bool, len(in))
for _, r := range in {
if _, ok := statusSet[r]; !ok {
statusSet[r] = true
irStatuses = append(irStatuses, ir.HTTPStatus(r))
}
}
return irStatuses
}

func makeIrTriggerSet(in []egv1a1.TriggerEnum) []ir.TriggerEnum {
var irTriggers []ir.TriggerEnum
// deduplicate http statuses
triggerSet := make(map[egv1a1.TriggerEnum]bool, len(in))
for _, r := range in {
if _, ok := triggerSet[r]; !ok {
triggerSet[r] = true
irTriggers = append(irTriggers, ir.TriggerEnum(r))
}
}
return irTriggers
}
1 change: 1 addition & 0 deletions internal/gatewayapi/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route
Mirrors: routeRoute.Mirrors,
ExtensionRefs: routeRoute.ExtensionRefs,
Timeout: routeRoute.Timeout,
Retry: routeRoute.Retry,
}
// 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,110 @@
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
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
namespace: envoy-gateway
name: gateway-2
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
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-2
sectionName: http
rules:
- matches:
- path:
value: "/"
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
retry:
retryOn:
triggers:
- cancelled
perRetry:
timeout: 250ms
backoff:
baseInterval: 100ms
maxInterval: 10s
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
namespace: default
name: policy-for-route
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: httproute-1
namespace: default
retry:
numRetries: 5
retryOn:
httpStatusCodes:
- 429
- 503
triggers:
- connect-failure
- retriable-status-codes
perRetry:
timeout: 250ms
backoff:
baseInterval: 100ms
maxInterval: 10s
Loading
Loading