Skip to content
Open
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
3 changes: 3 additions & 0 deletions artifacts/flagger/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ spec:
timeout:
description: HTTP or gRPC request timeout
type: string
backendTimeout:
description: Backend request timeout (Gateway API only)
type: string
meshName:
description: AppMesh mesh name
type: string
Expand Down
3 changes: 3 additions & 0 deletions charts/flagger/crds/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ spec:
timeout:
description: HTTP or gRPC request timeout
type: string
backendTimeout:
description: Backend request timeout (Gateway API only)
type: string
meshName:
description: AppMesh mesh name
type: string
Expand Down
32 changes: 32 additions & 0 deletions docs/gitbook/tutorials/gatewayapi-progressive-delivery.md
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,38 @@ The above procedures can be extended with [custom metrics](../usage/metrics.md)
Besides the `hosts` and `gatewayRefs` fields, you can customize the generated HTTPRoute with various options
exposed under the `spec.service` field of the Canary.

### Timeouts

You can configure request timeouts on the generated HTTPRoute using the `spec.service.timeout` and
`spec.service.backendTimeout` fields of the Canary. Both values are
[Gateway API Duration](https://gateway-api.sigs.k8s.io/geps/gep-2257/) strings (e.g. `5s`, `500ms`).

- `timeout` maps to `HTTPRouteTimeouts.Request` and bounds the total request duration from when the gateway
receives the request to when the response is sent back to the client.
- `backendTimeout` maps to `HTTPRouteTimeouts.BackendRequest` and bounds the duration of an individual
request from the gateway to a backend. When set together with `timeout`, `backendTimeout` must be
less than or equal to `timeout`.

> **Note:** Timeouts require a Gateway API implementation that supports
> [`HTTPRouteTimeouts`](https://gateway-api.sigs.k8s.io/api-types/httproute/#timeouts).
> `backendTimeout` additionally requires support for the `BackendRequest` timeout field.

Example configuration:

```yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
service:
# Total request timeout (gateway to client)
timeout: 5s
# Per-attempt backend request timeout (gateway to backend)
backendTimeout: 2s
```

### Header Manipulation

You can configure request and response header manipulation using the `spec.service.headers` field of the Canary.
Expand Down
3 changes: 3 additions & 0 deletions docs/gitbook/usage/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ spec:
attempts: 3
perTryTimeout: 1s
timeout: 5s
# backendTimeout is only supported by the Gateway API provider and
# maps to HTTPRouteTimeouts.BackendRequest.
backendTimeout: 2s
```

When using **Istio** as the mesh provider, you can also specify HTTP header operations,
Expand Down
3 changes: 3 additions & 0 deletions kustomize/base/flagger/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ spec:
timeout:
description: HTTP or gRPC request timeout
type: string
backendTimeout:
description: Backend request timeout (Gateway API only)
type: string
meshName:
description: AppMesh mesh name
type: string
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/flagger/v1beta1/canary.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ type CanaryService struct {
// +optional
Timeout string `json:"timeout,omitempty"`

// BackendTimeout specifies a timeout for an individual request from the gateway
// to a backend. Only supported by the Gateway API provider.
// +optional
BackendTimeout string `json:"backendTimeout,omitempty"`

// Gateways attached to the generated Istio virtual service
// Defaults to the internal mesh gateway
// +optional
Expand Down
41 changes: 21 additions & 20 deletions pkg/router/gateway_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,8 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
},
},
}
if canary.Spec.Service.Timeout != "" {
timeout := v1.Duration(canary.Spec.Service.Timeout)
httpRouteSpec.Rules[0].Timeouts = &v1.HTTPRouteTimeouts{
Request: &timeout,
}
if canary.Spec.Service.Timeout != "" || canary.Spec.Service.BackendTimeout != "" {
httpRouteSpec.Rules[0].Timeouts = makeHTTPRouteTimeouts(canary)
}

// A/B testing
Expand All @@ -129,11 +126,8 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
},
},
})
if canary.Spec.Service.Timeout != "" {
timeout := v1.Duration(canary.Spec.Service.Timeout)
httpRouteSpec.Rules[1].Timeouts = &v1.HTTPRouteTimeouts{
Request: &timeout,
}
if canary.Spec.Service.Timeout != "" || canary.Spec.Service.BackendTimeout != "" {
httpRouteSpec.Rules[1].Timeouts = makeHTTPRouteTimeouts(canary)
}
}

Expand Down Expand Up @@ -382,11 +376,8 @@ func (gwr *GatewayAPIRouter) SetRoutes(
},
},
}
if canary.Spec.Service.Timeout != "" {
timeout := v1.Duration(canary.Spec.Service.Timeout)
weightedRouteRule.Timeouts = &v1.HTTPRouteTimeouts{
Request: &timeout,
}
if canary.Spec.Service.Timeout != "" || canary.Spec.Service.BackendTimeout != "" {
weightedRouteRule.Timeouts = makeHTTPRouteTimeouts(canary)
}

// If B/G mirroring is enabled, then add a route filter which mirrors the traffic
Expand Down Expand Up @@ -439,11 +430,8 @@ func (gwr *GatewayAPIRouter) SetRoutes(
},
})

if canary.Spec.Service.Timeout != "" {
timeout := v1.Duration(canary.Spec.Service.Timeout)
hrClone.Spec.Rules[1].Timeouts = &v1.HTTPRouteTimeouts{
Request: &timeout,
}
if canary.Spec.Service.Timeout != "" || canary.Spec.Service.BackendTimeout != "" {
hrClone.Spec.Rules[1].Timeouts = makeHTTPRouteTimeouts(canary)
}
}

Expand Down Expand Up @@ -711,6 +699,19 @@ func (gwr *GatewayAPIRouter) mapRouteMatches(requestMatches []istiov1beta1.HTTPM
return matches, nil
}

func makeHTTPRouteTimeouts(canary *flaggerv1.Canary) *v1.HTTPRouteTimeouts {
timeouts := &v1.HTTPRouteTimeouts{}
if canary.Spec.Service.Timeout != "" {
timeout := v1.Duration(canary.Spec.Service.Timeout)
timeouts.Request = &timeout
}
if canary.Spec.Service.BackendTimeout != "" {
backendTimeout := v1.Duration(canary.Spec.Service.BackendTimeout)
timeouts.BackendRequest = &backendTimeout
}
return timeouts
}

func (gwr *GatewayAPIRouter) makeBackendRef(svcName string, weight, port int32) v1.BackendRef {
return v1.BackendRef{
BackendObjectReference: v1.BackendObjectReference{
Expand Down
1 change: 1 addition & 0 deletions pkg/router/gateway_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func TestGatewayAPIRouter_Reconcile(t *testing.T) {

timeout := routeRules[0].Timeouts
assert.Equal(t, string(*timeout.Request), canary.Spec.Service.Timeout)
assert.Equal(t, string(*timeout.BackendRequest), canary.Spec.Service.BackendTimeout)

// assert that http route annotations injected by the networking controller is preserved.
httpRoute.Annotations["foo"] = "bar"
Expand Down
3 changes: 2 additions & 1 deletion pkg/router/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,8 @@ func newTestGatewayAPICanary() *flaggerv1.Canary {
Name: "podinfo",
},
},
Timeout: "10s",
Timeout: "10s",
BackendTimeout: "5s",
},
Analysis: &flaggerv1.CanaryAnalysis{
Threshold: 10,
Expand Down