diff --git a/docs/content/reference/install-configuration/configuration-options.md b/docs/content/reference/install-configuration/configuration-options.md
index 04a17d4119..ebae1fd328 100644
--- a/docs/content/reference/install-configuration/configuration-options.md
+++ b/docs/content/reference/install-configuration/configuration-options.md
@@ -385,6 +385,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
| providers.kubernetesgateway.namespaces | Kubernetes namespaces. | |
| providers.kubernetesgateway.nativelbbydefault | Defines whether to use Native Kubernetes load-balancing by default. | false |
| providers.kubernetesgateway.qps | Defines the maximum QPS to the Kubernetes API server. Setting this to a negative value will disable client-side ratelimiting. | 50 |
+| providers.kubernetesgateway.retrymaxrequestbodybytes | Defines the maximum request body size (in bytes) buffered for HTTPRoute retries. If negative, the limit is disabled. | 2097152 |
| providers.kubernetesgateway.statusaddress.hostname | Hostname used for Kubernetes Gateway status address. | |
| providers.kubernetesgateway.statusaddress.ip | IP used to set Kubernetes Gateway status address. | |
| providers.kubernetesgateway.statusaddress.service | Published Kubernetes Service to copy status addresses from. | |
diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md
index f9f3c59b9c..38206b7ade 100644
--- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md
+++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md
@@ -89,6 +89,7 @@ providers:
| `providers.kubernetesGateway.gateways[0].namespace` | Gateway namespace. | "" | No |
| `providers.kubernetesGateway.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No |
| `providers.kubernetesGateway.nativeLBByDefault` | Defines whether to use Native Kubernetes load-balancing mode by default. For more information, please check out the `traefik.io/service.nativelb` service annotation documentation. | false | No |
+| `providers.kubernetesGateway.retryMaxRequestBodyBytes` | Defines the maximum request body size (in bytes) buffered for HTTPRoute retries.
If negative, the limit is disabled.
A request with a larger body is forwarded without retry. | 2097152 | No |
| `providers.kubernetesGateway.`
`statusAddress.hostname` | Hostname copied to the Gateway `status.addresses`. | "" | No |
| `providers.kubernetesGateway.`
`statusAddress.ip` | IP address copied to the Gateway `status.addresses`, and currently only supports one IP value (IPv4 or IPv6). | "" | No |
| `providers.kubernetesGateway.`
`statusAddress.service.namespace` | The namespace of the Kubernetes service to copy status addresses from.
When using third parties tools like External-DNS, this option can be used to copy the service `loadbalancer.status` (containing the service's endpoints IPs) to the Gateway `status.addresses`. | "" | No |
diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml
index 8b4538a0fc..f16507463b 100644
--- a/docs/content/reference/static-configuration/file.toml
+++ b/docs/content/reference/static-configuration/file.toml
@@ -200,6 +200,7 @@
throttleDuration = "42s"
experimentalChannel = true
nativeLBByDefault = true
+ retryMaxRequestBodyBytes = 42
[providers.kubernetesGateway.statusAddress]
ip = "foobar"
hostname = "foobar"
diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml
index c3b55a3e28..c12cc2becc 100644
--- a/docs/content/reference/static-configuration/file.yaml
+++ b/docs/content/reference/static-configuration/file.yaml
@@ -226,6 +226,7 @@ providers:
name: foobar
namespace: foobar
nativeLBByDefault: true
+ retryMaxRequestBodyBytes: 42
rest:
insecure: true
consulCatalog:
diff --git a/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-default-report.yaml b/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-default-report.yaml
index 195f24aa1d..9643339b2d 100644
--- a/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-default-report.yaml
+++ b/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-default-report.yaml
@@ -55,7 +55,7 @@ profiles:
result: success
statistics:
Failed: 0
- Passed: 56
+ Passed: 60
Skipped: 0
supportedFeatures:
- BackendTLSPolicy
@@ -89,6 +89,8 @@ profiles:
- HTTPRouteQueryParamMatching
- HTTPRouteRequestTimeout
- HTTPRouteResponseHeaderModification
+ - HTTPRouteRetryCodes
+ - HTTPRouteRetryConnectionError
- HTTPRouteSchemeRedirect
- ListenerSet
unsupportedFeatures:
@@ -100,8 +102,6 @@ profiles:
- HTTPRouteRequestMirror
- HTTPRouteRequestMultipleMirrors
- HTTPRouteRequestPercentageMirror
- - HTTPRouteRetryCodes
- - HTTPRouteRetryConnectionError
name: GATEWAY-HTTP
summary: Core tests succeeded. Extended tests succeeded.
- core:
@@ -176,3 +176,5 @@ succeededProvisionalTests:
- GatewayPreserveForeignConditions
- HTTPRouteBackendURLRewrite
- HTTPRoutePreserveForeignStatus
+- HTTPRouteRetryWithBackendTimeout
+- HTTPRouteRetryWithRequestTimeout
diff --git a/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-merged-report.yaml b/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-merged-report.yaml
index f5f16fe0f6..5abf5ebf3c 100644
--- a/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-merged-report.yaml
+++ b/integration/gateway-api-conformance-reports/v1.7.0-rc.3/experimental-v3.8-merged-report.yaml
@@ -68,7 +68,7 @@ profiles:
- GatewayInvalidFrontendClientCertificateValidation
statistics:
Failed: 0
- Passed: 49
+ Passed: 53
Skipped: 5
supportedFeatures:
- BackendTLSPolicy
@@ -100,6 +100,8 @@ profiles:
- HTTPRouteQueryParamMatching
- HTTPRouteRequestTimeout
- HTTPRouteResponseHeaderModification
+ - HTTPRouteRetryCodes
+ - HTTPRouteRetryConnectionError
- HTTPRouteSchemeRedirect
- ListenerSet
unsupportedFeatures:
@@ -113,8 +115,6 @@ profiles:
- HTTPRouteRequestMirror
- HTTPRouteRequestMultipleMirrors
- HTTPRouteRequestPercentageMirror
- - HTTPRouteRetryCodes
- - HTTPRouteRetryConnectionError
name: GATEWAY-HTTP
summary: Core tests partially succeeded with 1 test skips. Extended tests partially
succeeded with 5 test skips.
@@ -197,3 +197,5 @@ succeededProvisionalTests:
- GatewayPreserveForeignConditions
- HTTPRouteBackendURLRewrite
- HTTPRoutePreserveForeignStatus
+- HTTPRouteRetryWithBackendTimeout
+- HTTPRouteRetryWithRequestTimeout
diff --git a/pkg/provider/kubernetes/gateway/features.go b/pkg/provider/kubernetes/gateway/features.go
index 8de7a69ebe..b4645ac2f7 100644
--- a/pkg/provider/kubernetes/gateway/features.go
+++ b/pkg/provider/kubernetes/gateway/features.go
@@ -74,6 +74,8 @@ func extendedHTTPRouteFeatures() sets.Set[features.Feature] {
features.HTTPRouteRequestTimeoutFeature,
features.HTTPRouteBackendTimeoutFeature,
features.HTTPRouteBackendURLRewriteFeature,
+ features.HTTPRouteRetryCodesFeature,
+ features.HTTPRouteRetryConnectionErrorFeature,
)
}
diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_retry.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_retry.yml
new file mode 100644
index 0000000000..54e47ffd64
--- /dev/null
+++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_retry.yml
@@ -0,0 +1,68 @@
+---
+kind: GatewayClass
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway-class
+spec:
+ controllerName: traefik.io/gateway-controller
+
+---
+kind: Gateway
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway
+ namespace: default
+spec:
+ gatewayClassName: my-gateway-class
+ listeners: # Use GatewayClass defaults for listener definition.
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ kinds:
+ - kind: HTTPRoute
+ group: gateway.networking.k8s.io
+ namespaces:
+ from: Same
+
+---
+kind: HTTPRoute
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: http-app-1
+ namespace: default
+spec:
+ parentRefs:
+ - name: my-gateway
+ kind: Gateway
+ group: gateway.networking.k8s.io
+ hostnames:
+ - "example.org"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: whoami
+ port: 80
+ weight: 1
+ kind: Service
+ group: ""
+ filters:
+ - type: RequestHeaderModifier
+ requestHeaderModifier:
+ set:
+ - name: X-Foo
+ value: Bar
+ add:
+ - name: X-Bar
+ value: Foo
+ remove:
+ - X-Baz
+ retry:
+ codes:
+ - 500
+ - 503
+ attempts: 3
+ backoff: 100ms
diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_retry_empty.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_retry_empty.yml
new file mode 100644
index 0000000000..14583b83c9
--- /dev/null
+++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_retry_empty.yml
@@ -0,0 +1,52 @@
+---
+kind: GatewayClass
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway-class
+spec:
+ controllerName: traefik.io/gateway-controller
+
+---
+kind: Gateway
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: my-gateway
+ namespace: default
+spec:
+ gatewayClassName: my-gateway-class
+ listeners: # Use GatewayClass defaults for listener definition.
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ kinds:
+ - kind: HTTPRoute
+ group: gateway.networking.k8s.io
+ namespaces:
+ from: Same
+
+---
+kind: HTTPRoute
+apiVersion: gateway.networking.k8s.io/v1
+metadata:
+ name: http-app-1
+ namespace: default
+spec:
+ parentRefs:
+ - name: my-gateway
+ kind: Gateway
+ group: gateway.networking.k8s.io
+ hostnames:
+ - "example.org"
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /
+ backendRefs:
+ - name: whoami
+ port: 80
+ weight: 1
+ kind: Service
+ group: ""
+ retry: {}
diff --git a/pkg/provider/kubernetes/gateway/httproute.go b/pkg/provider/kubernetes/gateway/httproute.go
index 9398a11651..a47cd6b166 100644
--- a/pkg/provider/kubernetes/gateway/httproute.go
+++ b/pkg/provider/kubernetes/gateway/httproute.go
@@ -158,6 +158,11 @@ func (p *Provider) loadHTTPRouteConfiguration(ctx context.Context, gatewayName,
}
}
+ var retry *dynamic.Middleware
+ if routeRule.Retry != nil {
+ retry = createRetry(ctx, routeRule.Retry, p.RetryMaxRequestBodyBytes)
+ }
+
for _, match := range routeRule.Matches {
rule, priority := buildMatchRule(hostnames, match)
router := dynamic.Router{
@@ -223,6 +228,13 @@ func (p *Provider) loadHTTPRouteConfiguration(ctx context.Context, gatewayName,
}
}
+ if err == nil && retry != nil {
+ // The retry middleware comes last, so that only the call to the backend is retried.
+ retryName := routerName + "-retry"
+ routerConf.HTTP.Middlewares[retryName] = retry
+ router.Middlewares = append(router.Middlewares, retryName)
+ }
+
p.applyRouterTransform(ctx, &router, route)
routerConf.HTTP.Routers[routerName] = &router
@@ -1088,6 +1100,30 @@ func createCORS(filter *gatev1.HTTPCORSFilter) *dynamic.Middleware {
}
}
+func createRetry(ctx context.Context, retry *gatev1.HTTPRouteRetry, maxRequestBodyBytes int64) *dynamic.Middleware {
+ // The Gateway API attempts are the number of retries,
+ // whereas the Traefik ones also count the initial request, hence the +1.
+ r := &dynamic.Retry{
+ Attempts: ptr.Deref(retry.Attempts, 1) + 1,
+ MaxRequestBodyBytes: &maxRequestBodyBytes,
+ }
+
+ for _, code := range retry.Codes {
+ r.Status = append(r.Status, strconv.Itoa(int(code)))
+ }
+
+ if retry.Backoff != nil {
+ // The CRD validation guarantees a valid GEP-2257 duration string.
+ if backoff, err := time.ParseDuration(string(*retry.Backoff)); err != nil {
+ log.Ctx(ctx).Error().Err(err).Msg("Ignoring HTTPRoute retry.backoff: invalid duration")
+ } else {
+ r.InitialInterval = ptypes.Duration(backoff)
+ }
+ }
+
+ return &dynamic.Middleware{Retry: r}
+}
+
func getHTTPServiceProtocol(portSpec corev1.ServicePort) (string, error) {
if portSpec.Protocol != corev1.ProtocolTCP {
return "", errors.New("only TCP protocol is supported")
diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go
index fb4135bbce..0fce10dfb0 100644
--- a/pkg/provider/kubernetes/gateway/kubernetes.go
+++ b/pkg/provider/kubernetes/gateway/kubernetes.go
@@ -92,13 +92,14 @@ type Provider struct {
Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
GatewayClass string `description:"Name of the GatewayClass to watch for. If empty, all GatewayClasses are processed." json:"gatewayClass,omitempty" toml:"gatewayClass,omitempty" yaml:"gatewayClass,omitempty" export:"true"`
// Deprecated: please use the GatewayClass option instead.
- LabelSelector string `description:"Kubernetes label selector to select specific GatewayClasses (Deprecated, please use the GatewayClass option instead)." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
- Gateways []NamespacedName `description:"Scopes the provider to specific Gateways." json:"gateways,omitempty" toml:"gateways,omitempty" yaml:"gateways,omitempty" export:"true"`
- ThrottleDuration ptypes.Duration `description:"Kubernetes refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
- ExperimentalChannel bool `description:"Toggles Experimental Channel resources support. Requires the Experimental Channel CRDs." json:"experimentalChannel,omitempty" toml:"experimentalChannel,omitempty" yaml:"experimentalChannel,omitempty" export:"true"`
- StatusAddress *StatusAddress `description:"Defines the Kubernetes Gateway status address." json:"statusAddress,omitempty" toml:"statusAddress,omitempty" yaml:"statusAddress,omitempty" export:"true"`
- NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
- CrossProviderNamespaces []string `description:"List of namespaces from which Gateway API routes are allowed to declare TraefikService backendRef references." json:"crossProviderNamespaces,omitempty" toml:"crossProviderNamespaces,omitempty" yaml:"crossProviderNamespaces,omitempty" export:"true"`
+ LabelSelector string `description:"Kubernetes label selector to select specific GatewayClasses (Deprecated, please use the GatewayClass option instead)." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
+ Gateways []NamespacedName `description:"Scopes the provider to specific Gateways." json:"gateways,omitempty" toml:"gateways,omitempty" yaml:"gateways,omitempty" export:"true"`
+ ThrottleDuration ptypes.Duration `description:"Kubernetes refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
+ ExperimentalChannel bool `description:"Toggles Experimental Channel resources support. Requires the Experimental Channel CRDs." json:"experimentalChannel,omitempty" toml:"experimentalChannel,omitempty" yaml:"experimentalChannel,omitempty" export:"true"`
+ StatusAddress *StatusAddress `description:"Defines the Kubernetes Gateway status address." json:"statusAddress,omitempty" toml:"statusAddress,omitempty" yaml:"statusAddress,omitempty" export:"true"`
+ NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
+ CrossProviderNamespaces []string `description:"List of namespaces from which Gateway API routes are allowed to declare TraefikService backendRef references." json:"crossProviderNamespaces,omitempty" toml:"crossProviderNamespaces,omitempty" yaml:"crossProviderNamespaces,omitempty" export:"true"`
+ RetryMaxRequestBodyBytes int64 `description:"Defines the maximum request body size (in bytes) buffered for HTTPRoute retries. If negative, the limit is disabled." json:"retryMaxRequestBodyBytes,omitempty" toml:"retryMaxRequestBodyBytes,omitempty" yaml:"retryMaxRequestBodyBytes,omitempty" export:"true"`
EntryPoints map[string]Entrypoint `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
@@ -114,6 +115,7 @@ type Provider struct {
func (p *Provider) SetDefaults() {
p.QPS = 50 // the default value for the QPS is 10x the default Kubernetes client QPS value.
p.Burst = 100 // the default value for the Burst is 10x the default Kubernetes client Burst value.
+ p.RetryMaxRequestBodyBytes = dynamic.RetryDefaultMaxRequestBodyBytes
}
// Entrypoint defines the available entry points.
diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go
index 2aa5ba08ae..f1a5a75688 100644
--- a/pkg/provider/kubernetes/gateway/kubernetes_test.go
+++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go
@@ -220,12 +220,13 @@ func TestGatewayScoping(t *testing.T) {
func TestLoadHTTPRoutes(t *testing.T) {
testCases := []struct {
- desc string
- ingressClass string
- paths []string
- expected *dynamic.Configuration
- entryPoints map[string]Entrypoint
- nativeLB bool
+ desc string
+ ingressClass string
+ paths []string
+ expected *dynamic.Configuration
+ entryPoints map[string]Entrypoint
+ nativeLB bool
+ retryMaxRequestBodyBytes int64
}{
{
desc: "Empty",
@@ -2880,6 +2881,255 @@ func TestLoadHTTPRoutes(t *testing.T) {
},
},
},
+ {
+ desc: "Simple HTTPRoute, retry",
+ paths: []string{"services.yml", "httproute/with_retry.yml"},
+ retryMaxRequestBodyBytes: dynamic.RetryDefaultMaxRequestBodyBytes,
+ entryPoints: map[string]Entrypoint{"web": {
+ Address: ":80",
+ }},
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{},
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "listener-web-http-5b3226d4ebd42b7200f1": {
+ EntryPoints: []string{"web"},
+ Rule: `Host("*")`,
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
+ ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
+ Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
+ Rule: `Host("example.org") && PathPrefix("/")`,
+ Priority: 13,
+ RuleSyntax: "default",
+ Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestheadermodifier-0", "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-retry"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestheadermodifier-0": {
+ RequestHeaderModifier: &dynamic.HeaderModifier{
+ Set: map[string]string{"X-Foo": "Bar"},
+ Add: map[string]string{"X-Bar": "Foo"},
+ Remove: []string{"X-Baz"},
+ },
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-retry": {
+ Retry: &dynamic.Retry{
+ Attempts: 4,
+ Status: []string{"500", "503"},
+ InitialInterval: ptypes.Duration(100 * time.Millisecond),
+ MaxRequestBodyBytes: new(dynamic.RetryDefaultMaxRequestBodyBytes),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
+ Weighted: &dynamic.WeightedRoundRobin{
+ Services: []dynamic.WRRService{
+ {
+ Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0",
+ Weight: new(1),
+ },
+ },
+ },
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Strategy: dynamic.BalancerStrategyWRR,
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ PassHostHeader: new(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: ptypes.Duration(100 * time.Millisecond),
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{
+ Options: map[string]tls.Options{},
+ },
+ },
+ },
+ {
+ desc: "Simple HTTPRoute, retry with maxRequestBodyBytes",
+ paths: []string{"services.yml", "httproute/with_retry.yml"},
+ retryMaxRequestBodyBytes: 1024,
+ entryPoints: map[string]Entrypoint{"web": {
+ Address: ":80",
+ }},
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{},
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "listener-web-http-5b3226d4ebd42b7200f1": {
+ EntryPoints: []string{"web"},
+ Rule: `Host("*")`,
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
+ ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
+ Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
+ Rule: `Host("example.org") && PathPrefix("/")`,
+ Priority: 13,
+ RuleSyntax: "default",
+ Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestheadermodifier-0", "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-retry"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestheadermodifier-0": {
+ RequestHeaderModifier: &dynamic.HeaderModifier{
+ Set: map[string]string{"X-Foo": "Bar"},
+ Add: map[string]string{"X-Bar": "Foo"},
+ Remove: []string{"X-Baz"},
+ },
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-retry": {
+ Retry: &dynamic.Retry{
+ Attempts: 4,
+ Status: []string{"500", "503"},
+ InitialInterval: ptypes.Duration(100 * time.Millisecond),
+ MaxRequestBodyBytes: new(int64(1024)),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
+ Weighted: &dynamic.WeightedRoundRobin{
+ Services: []dynamic.WRRService{
+ {
+ Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0",
+ Weight: new(1),
+ },
+ },
+ },
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Strategy: dynamic.BalancerStrategyWRR,
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ PassHostHeader: new(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: ptypes.Duration(100 * time.Millisecond),
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{
+ Options: map[string]tls.Options{},
+ },
+ },
+ },
+ {
+ desc: "Simple HTTPRoute, retry with defaults",
+ paths: []string{"services.yml", "httproute/with_retry_empty.yml"},
+ retryMaxRequestBodyBytes: dynamic.RetryDefaultMaxRequestBodyBytes,
+ entryPoints: map[string]Entrypoint{"web": {
+ Address: ":80",
+ }},
+ expected: &dynamic.Configuration{
+ UDP: &dynamic.UDPConfiguration{
+ Routers: map[string]*dynamic.UDPRouter{},
+ Services: map[string]*dynamic.UDPService{},
+ },
+ TCP: &dynamic.TCPConfiguration{
+ Routers: map[string]*dynamic.TCPRouter{},
+ Middlewares: map[string]*dynamic.TCPMiddleware{},
+ Services: map[string]*dynamic.TCPService{},
+ ServersTransports: map[string]*dynamic.TCPServersTransport{},
+ },
+ HTTP: &dynamic.HTTPConfiguration{
+ Routers: map[string]*dynamic.Router{
+ "listener-web-http-5b3226d4ebd42b7200f1": {
+ EntryPoints: []string{"web"},
+ Rule: `Host("*")`,
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
+ ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
+ Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
+ Rule: `Host("example.org") && PathPrefix("/")`,
+ Priority: 13,
+ RuleSyntax: "default",
+ Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-retry"},
+ },
+ },
+ Middlewares: map[string]*dynamic.Middleware{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-retry": {
+ Retry: &dynamic.Retry{
+ Attempts: 2,
+ MaxRequestBodyBytes: new(dynamic.RetryDefaultMaxRequestBodyBytes),
+ },
+ },
+ },
+ Services: map[string]*dynamic.Service{
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
+ Weighted: &dynamic.WeightedRoundRobin{
+ Services: []dynamic.WRRService{
+ {
+ Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0",
+ Weight: new(1),
+ },
+ },
+ },
+ },
+ "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0": {
+ LoadBalancer: &dynamic.ServersLoadBalancer{
+ Strategy: dynamic.BalancerStrategyWRR,
+ Servers: []dynamic.Server{
+ {
+ URL: "http://10.10.0.1:80",
+ },
+ {
+ URL: "http://10.10.0.2:80",
+ },
+ },
+ PassHostHeader: new(true),
+ ResponseForwarding: &dynamic.ResponseForwarding{
+ FlushInterval: ptypes.Duration(100 * time.Millisecond),
+ },
+ },
+ },
+ },
+ ServersTransports: map[string]*dynamic.ServersTransport{},
+ },
+ TLS: &dynamic.TLSConfiguration{
+ Options: map[string]tls.Options{},
+ },
+ },
+ },
{
desc: "Simple HTTPRoute, response header modifier",
paths: []string{"services.yml", "httproute/filter_response_header_modifier.yml"},
@@ -4894,9 +5144,10 @@ func TestLoadHTTPRoutes(t *testing.T) {
}
p := Provider{
- EntryPoints: test.entryPoints,
- NativeLBByDefault: test.nativeLB,
- client: client,
+ EntryPoints: test.entryPoints,
+ NativeLBByDefault: test.nativeLB,
+ RetryMaxRequestBodyBytes: test.retryMaxRequestBodyBytes,
+ client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())