diff --git a/VERSION b/VERSION
index 3b34d22941..5f152d81a5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v1.7.2
+v1.7.3
diff --git a/api/v1alpha1/healthcheck_types.go b/api/v1alpha1/healthcheck_types.go
index 7fd1cd6a15..b56d55a085 100644
--- a/api/v1alpha1/healthcheck_types.go
+++ b/api/v1alpha1/healthcheck_types.go
@@ -159,8 +159,9 @@ const (
// HTTPActiveHealthChecker defines the settings of http health check.
type HTTPActiveHealthChecker struct {
- // Hostname defines the HTTP host that will be requested during health checking.
- // Default: HTTPRoute or GRPCRoute hostname.
+ // Hostname defines the HTTP Host header used for active HTTP health checks.
+ // Host selection uses this order: this field, the associated Backend endpoint
+ // hostname if available, then the effective Route hostname.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 123e89c94a..8646634ba9 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -426,8 +426,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
index c7847dd52d..af8a7556d2 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
@@ -478,8 +478,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
index 1236d0d613..7bc5e5c580 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -11521,8 +11521,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -12773,8 +12774,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -14195,8 +14197,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -15522,8 +15525,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
index 6d20dad82e..f9231269a0 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -1094,8 +1094,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -2234,8 +2235,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -3575,8 +3577,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -4994,8 +4997,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 0e5b09c8e6..3f5343f86b 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -425,8 +425,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
index f87b1cacfa..8d86777ad6 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml
@@ -477,8 +477,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
index 66b39ecc0c..f1e6dec7d6 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
@@ -11520,8 +11520,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -12772,8 +12773,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -14194,8 +14196,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -15521,8 +15524,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
index 684d40a80d..e191a3fcb3 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -1093,8 +1093,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -2233,8 +2234,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -3574,8 +3576,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -4993,8 +4996,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/internal/gatewayapi/backend.go b/internal/gatewayapi/backend.go
index bd5aedc67a..60ed97e55a 100644
--- a/internal/gatewayapi/backend.go
+++ b/internal/gatewayapi/backend.go
@@ -20,8 +20,10 @@ import (
)
func (t *Translator) ProcessBackends(backends []*egv1a1.Backend, backendTLSPolicies []*gwapiv1.BackendTLSPolicy) []*egv1a1.Backend {
+ backendCopies := backendCopiesWithStatusDeepCopy(backends)
res := make([]*egv1a1.Backend, 0, len(backends))
- for _, backend := range backends {
+ for i := range backends {
+ backend := backendCopies[i]
// Ensure Backends are enabled
if !t.BackendEnabled {
status.UpdateBackendStatusAcceptedCondition(backend, false,
@@ -203,3 +205,15 @@ func validateIP(epIP *egv1a1.IPEndpoint, runningOnHost bool) status.Error {
}
return nil
}
+
+// backendCopiesWithStatusDeepCopy returns shallow copies with deep-copied Status fields.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func backendCopiesWithStatusDeepCopy(backends []*egv1a1.Backend) []*egv1a1.Backend {
+ copies := make([]*egv1a1.Backend, len(backends))
+ for i, b := range backends {
+ out := *b
+ b.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index a00870f687..1a70a0ee8a 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -95,6 +95,8 @@ func (t *Translator) ProcessBackendTrafficPolicies(
SectionIndex: make(map[types.NamespacedName]sets.Set[string], gatewayMapSize),
}
+ policyCopies := backendTrafficPolicyCopiesWithStatusDeepCopy(backendTrafficPolicies)
+
handledPolicies := make(map[types.NamespacedName]*egv1a1.BackendTrafficPolicy, policyMapSize)
// Translate
@@ -107,7 +109,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
t.buildGatewayPolicyMap(backendTrafficPolicies, gateways, gatewayMap, gatewayPolicyMap)
// Process the policies targeting RouteRules
- for _, currPolicy := range backendTrafficPolicies {
+ for i, currPolicy := range backendTrafficPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -115,7 +117,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -127,7 +129,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
}
// Process the policies targeting Routes
- for _, currPolicy := range backendTrafficPolicies {
+ for i, currPolicy := range backendTrafficPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -135,7 +137,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -147,7 +149,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
}
// Process the policies targeting Listeners
- for _, currPolicy := range backendTrafficPolicies {
+ for i, currPolicy := range backendTrafficPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -155,7 +157,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -166,7 +168,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
}
// Process the policies targeting Gateways
- for _, currPolicy := range backendTrafficPolicies {
+ for i, currPolicy := range backendTrafficPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -174,7 +176,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(
if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -818,9 +820,6 @@ func (t *Translator) applyTrafficFeatureToRoute(route RouteContext,
r.Traffic.Timeout = localTo
}
- // Update the Host field in HealthCheck, now that we have access to the Route Hostname.
- r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
-
if policy.Spec.UseClientProtocol != nil {
r.UseClientProtocol = policy.Spec.UseClientProtocol
}
@@ -1101,9 +1100,6 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
r.Traffic.Timeout = localTo
}
- // Update the Host field in HealthCheck, now that we have access to the Route Hostname.
- r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
-
if policy.Spec.UseClientProtocol != nil {
r.UseClientProtocol = policy.Spec.UseClientProtocol
}
@@ -1847,3 +1843,15 @@ func buildRouteStatName(routeStatName string, metadata *ir.ResourceMetadata) *st
return &statName
}
+
+// backendTrafficPolicyCopiesWithStatusDeepCopy returns shallow copies with deep-copied Status fields.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func backendTrafficPolicyCopiesWithStatusDeepCopy(policies []*egv1a1.BackendTrafficPolicy) []*egv1a1.BackendTrafficPolicy {
+ copies := make([]*egv1a1.BackendTrafficPolicy, len(policies))
+ for i, p := range policies {
+ out := *p
+ p.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go
index f9ed44a1b5..d8ecd0553e 100644
--- a/internal/gatewayapi/clienttrafficpolicy.go
+++ b/internal/gatewayapi/clienttrafficpolicy.go
@@ -71,13 +71,15 @@ func (t *Translator) ProcessClientTrafficPolicies(
gatewayMap[key] = &policyGatewayTargetContext{GatewayContext: gw}
}
+ policyCopies := clientTrafficPolicyCopiesWithStatusDeepCopy(clientTrafficPolicies)
+
handledPolicies := make(map[types.NamespacedName]*egv1a1.ClientTrafficPolicy)
// Translate
// 1. First translate Policies with a sectionName set
// 2. Then loop again and translate the policies without a sectionName
// TODO: Import sort order to ensure policy with same section always appear
// before policy with no section so below loops can be flattened into 1.
- for _, currPolicy := range clientTrafficPolicies {
+ for i, currPolicy := range clientTrafficPolicies {
policyName := utils.NamespacedName(currPolicy)
// This loop only handles policies that target a specific section. When
// targeting a policy with a selector, it's not possible to specify a SectionName
@@ -87,7 +89,7 @@ func (t *Translator) ProcessClientTrafficPolicies(
if hasSectionName(&currTarget) {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -174,7 +176,7 @@ func (t *Translator) ProcessClientTrafficPolicies(
}
// Policy with no section set (targeting all sections)
- for _, currPolicy := range clientTrafficPolicies {
+ for i, currPolicy := range clientTrafficPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -182,7 +184,7 @@ func (t *Translator) ProcessClientTrafficPolicies(
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
res = append(res, policy)
handledPolicies[policyName] = policy
}
@@ -1222,3 +1224,15 @@ func translateHeaderModifier(headerModifier *egv1a1.HTTPHeaderFilter, modType st
return addRequestHeaders, removeRequestHeaders, removeRequestHeadersOnMatch, errs
}
+
+// clientTrafficPolicyCopiesWithStatusDeepCopy returns shallow copies with deep-copied Status fields.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func clientTrafficPolicyCopiesWithStatusDeepCopy(policies []*egv1a1.ClientTrafficPolicy) []*egv1a1.ClientTrafficPolicy {
+ copies := make([]*egv1a1.ClientTrafficPolicy, len(policies))
+ for i, p := range policies {
+ out := *p
+ p.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go
index 83ea7be986..a53d24bd55 100644
--- a/internal/gatewayapi/envoyextensionpolicy.go
+++ b/internal/gatewayapi/envoyextensionpolicy.go
@@ -77,6 +77,8 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
// The routes are grouped by sectionNames of their targetRefs.
gatewayRouteMap := make(map[string]map[string]sets.Set[string])
+ policyCopies := envoyExtensionPolicyCopiesWithStatusDeepCopy(envoyExtensionPolicies)
+
handledPolicies := make(map[types.NamespacedName]*egv1a1.EnvoyExtensionPolicy)
// Translate
@@ -86,7 +88,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
// 4. Finally, the policies targeting Gateways
// Process the policies targeting RouteRules
- for _, currPolicy := range envoyExtensionPolicies {
+ for i, currPolicy := range envoyExtensionPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -94,7 +96,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
res = append(res, policy)
handledPolicies[policyName] = policy
}
@@ -106,7 +108,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
}
// Process the policies targeting xRoutes
- for _, currPolicy := range envoyExtensionPolicies {
+ for i, currPolicy := range envoyExtensionPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -114,7 +116,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
res = append(res, policy)
handledPolicies[policyName] = policy
}
@@ -126,7 +128,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
}
// Process the policies targeting Listeners
- for _, currPolicy := range envoyExtensionPolicies {
+ for i, currPolicy := range envoyExtensionPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -134,7 +136,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
res = append(res, policy)
handledPolicies[policyName] = policy
}
@@ -146,7 +148,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
}
// Process the policies targeting Gateways
- for _, currPolicy := range envoyExtensionPolicies {
+ for i, currPolicy := range envoyExtensionPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -154,7 +156,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies(
if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
res = append(res, policy)
handledPolicies[policyName] = policy
}
@@ -1107,3 +1109,15 @@ func irConfigNameForWasm(policy client.Object, index int) string {
irConfigName(policy),
strconv.Itoa(index))
}
+
+// envoyExtensionPolicyCopiesWithStatusDeepCopy returns shallow copies with deep-copied Status fields.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func envoyExtensionPolicyCopiesWithStatusDeepCopy(policies []*egv1a1.EnvoyExtensionPolicy) []*egv1a1.EnvoyExtensionPolicy {
+ copies := make([]*egv1a1.EnvoyExtensionPolicy, len(policies))
+ for i, p := range policies {
+ out := *p
+ p.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/envoypatchpolicy.go b/internal/gatewayapi/envoypatchpolicy.go
index 5101a504b9..f2f0f59468 100644
--- a/internal/gatewayapi/envoypatchpolicy.go
+++ b/internal/gatewayapi/envoypatchpolicy.go
@@ -17,10 +17,14 @@ import (
"github.com/envoyproxy/gateway/internal/ir"
)
-func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.EnvoyPatchPolicy, xdsIR resource.XdsIRMap) {
+func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.EnvoyPatchPolicy, xdsIR resource.XdsIRMap) []*egv1a1.EnvoyPatchPolicy {
// EnvoyPatchPolicies are already sorted by the provider layer (priority, then timestamp, then name)
+ policyCopies := envoyPatchPolicyCopiesWithStatusDeepCopy(envoyPatchPolicies)
+ res := make([]*egv1a1.EnvoyPatchPolicy, 0, len(envoyPatchPolicies))
- for _, policy := range envoyPatchPolicies {
+ for i := range envoyPatchPolicies {
+ policy := policyCopies[i]
+ res = append(res, policy)
var (
ancestorRef gwapiv1.ParentReference
resolveErr *status.PolicyResolveError
@@ -117,4 +121,18 @@ func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.Envo
// Set Accepted=True
status.SetAcceptedForPolicyAncestor(&policy.Status, &ancestorRef, t.GatewayControllerName, policy.Generation)
}
+
+ return res
+}
+
+// envoyPatchPolicyCopiesWithStatusDeepCopy returns shallow copies with deep-copied Status fields.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func envoyPatchPolicyCopiesWithStatusDeepCopy(policies []*egv1a1.EnvoyPatchPolicy) []*egv1a1.EnvoyPatchPolicy {
+ copies := make([]*egv1a1.EnvoyPatchPolicy, len(policies))
+ for i, p := range policies {
+ out := *p
+ p.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
}
diff --git a/internal/gatewayapi/extensionserverpolicy.go b/internal/gatewayapi/extensionserverpolicy.go
index 9a1b9ff2fc..12530c6f6f 100644
--- a/internal/gatewayapi/extensionserverpolicy.go
+++ b/internal/gatewayapi/extensionserverpolicy.go
@@ -9,9 +9,11 @@ import (
"encoding/json"
"errors"
"fmt"
+ "maps"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -36,14 +38,16 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst
gatewayMap[key] = &policyGatewayTargetContext{GatewayContext: gw}
}
+ policyCopies := extensionServerPolicyCopiesWithStatusDeepCopy(policies)
+
var errs error
// Process the policies targeting Gateways. Only update the policy status if it was accepted.
// A policy is considered accepted if at least one targetRef contained inside matched a listener.
- for policyIndex, policy := range policies {
- policy := &policy
+ for i := range policies {
+ policy := policyCopies[i]
var policyStatus gwapiv1.PolicyStatus
accepted := false
- targetRefs, err := extractTargetRefs(policy, gateways)
+ targetRefs, err := extractTargetRefs(&policy, gateways)
if err != nil {
errs = errors.Join(errs, fmt.Errorf("error finding targetRefs for policy %s: %w", policy.GetName(), err))
continue
@@ -55,7 +59,7 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst
}
// Negative statuses have already been assigned so its safe to skip
- gateway := resolveExtServerPolicyGatewayTargetRef(policy, currTarget, gatewayMap)
+ gateway := resolveExtServerPolicyGatewayTargetRef(&policy, currTarget, gatewayMap)
if gateway == nil {
// unable to find a matching Gateway for policy
continue
@@ -64,12 +68,12 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst
// Append policy extension server policy list for related gateway.
gatewayKey := t.getIRKey(gateway.Gateway)
unstructuredPolicy := &ir.UnstructuredRef{
- Object: &policies[policyIndex],
+ Object: &policy,
}
xdsIR[gatewayKey].ExtensionServerPolicies = append(xdsIR[gatewayKey].ExtensionServerPolicies, unstructuredPolicy)
// Set conditions for translation if it got any
- if t.translateExtServerPolicyForGateway(policy, gateway, currTarget, xdsIR) {
+ if t.translateExtServerPolicyForGateway(&policy, gateway, currTarget, xdsIR) {
// Set Accepted condition if it is unset
// Only add a status condition if the policy was added into the IR
// Find its ancestor reference by resolved gateway, even with resolve error
@@ -80,8 +84,8 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst
}
}
if accepted {
- res = append(res, *policy)
policy.Object["status"] = PolicyStatusToUnstructured(policyStatus)
+ res = append(res, policy)
}
}
@@ -188,3 +192,17 @@ func (t *Translator) translateExtServerPolicyForGateway(
}
return found
}
+
+// extensionServerPolicyCopiesWithStatusDeepCopy returns shallow copies with deep-copied status entries.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func extensionServerPolicyCopiesWithStatusDeepCopy(policies []unstructured.Unstructured) []unstructured.Unstructured {
+ copies := make([]unstructured.Unstructured, len(policies))
+ for i, p := range policies {
+ p.Object = maps.Clone(p.Object) // shallow copy map - no shared ref for "status" key
+ if statusObj, ok := policies[i].Object["status"].(map[string]any); ok {
+ p.Object["status"] = runtime.DeepCopyJSON(statusObj)
+ }
+ copies[i] = p
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go
index b1a709e53c..ce31bbd4c0 100644
--- a/internal/gatewayapi/route.go
+++ b/internal/gatewayapi/route.go
@@ -56,14 +56,15 @@ type RoutesTranslator interface {
func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways []*GatewayContext, resources *resource.Resources, xdsIR resource.XdsIRMap) []*HTTPRouteContext {
relevantHTTPRoutes := make([]*HTTPRouteContext, 0, len(httpRoutes))
+ httpRouteCopies := httpRouteCopiesWithStatusDeepCopy(httpRoutes)
// HTTPRoutes are already sorted by the provider layer
- for _, h := range httpRoutes {
+ for i, h := range httpRoutes {
if h == nil {
panic("received nil httproute")
}
- httpRoute := &HTTPRouteContext{HTTPRoute: h}
+ httpRoute := &HTTPRouteContext{HTTPRoute: httpRouteCopies[i]}
// Find out if this route attaches to one of our Gateway's listeners,
// and if so, get the list of listeners that allow it to attach for each
@@ -83,14 +84,15 @@ func (t *Translator) ProcessHTTPRoutes(httpRoutes []*gwapiv1.HTTPRoute, gateways
func (t *Translator) ProcessGRPCRoutes(grpcRoutes []*gwapiv1.GRPCRoute, gateways []*GatewayContext, resources *resource.Resources, xdsIR resource.XdsIRMap) []*GRPCRouteContext {
relevantGRPCRoutes := make([]*GRPCRouteContext, 0, len(grpcRoutes))
+ grpcRouteCopies := grpcRouteCopiesWithStatusDeepCopy(grpcRoutes)
// GRPCRoutes are already sorted by the provider layer
- for _, g := range grpcRoutes {
+ for i, g := range grpcRoutes {
if g == nil {
panic("received nil grpcroute")
}
- grpcRoute := &GRPCRouteContext{GRPCRoute: g}
+ grpcRoute := &GRPCRouteContext{GRPCRoute: grpcRouteCopies[i]}
// Find out if this route attaches to one of our Gateway's listeners,
// and if so, get the list of listeners that allow it to attach for each
@@ -1342,13 +1344,14 @@ func filterEGPrefix(in map[string]string) map[string]string {
func (t *Translator) ProcessTLSRoutes(tlsRoutes []*gwapiv1a3.TLSRoute, gateways []*GatewayContext, resources *resource.Resources, xdsIR resource.XdsIRMap) []*TLSRouteContext {
relevantTLSRoutes := make([]*TLSRouteContext, 0, len(tlsRoutes))
+ tlsRouteCopies := tlsRouteCopiesWithStatusDeepCopy(tlsRoutes)
// TLSRoutes are already sorted by the provider layer
- for _, tls := range tlsRoutes {
+ for i, tls := range tlsRoutes {
if tls == nil {
panic("received nil tlsroute")
}
- tlsRoute := &TLSRouteContext{TLSRoute: tls}
+ tlsRoute := &TLSRouteContext{TLSRoute: tlsRouteCopies[i]}
// Find out if this route attaches to one of our Gateway's listeners,
// and if so, get the list of listeners that allow it to attach for each
@@ -1508,13 +1511,14 @@ func (t *Translator) ProcessUDPRoutes(udpRoutes []*gwapiv1a2.UDPRoute, gateways
xdsIR resource.XdsIRMap,
) []*UDPRouteContext {
relevantUDPRoutes := make([]*UDPRouteContext, 0, len(udpRoutes))
+ udpRouteCopies := udpRouteCopiesWithStatusDeepCopy(udpRoutes)
// UDPRoutes are already sorted by the provider layer
- for _, u := range udpRoutes {
+ for i, u := range udpRoutes {
if u == nil {
panic("received nil udproute")
}
- udpRoute := &UDPRouteContext{UDPRoute: u}
+ udpRoute := &UDPRouteContext{UDPRoute: udpRouteCopies[i]}
// Find out if this route attaches to one of our Gateway's listeners,
// and if so, get the list of listeners that allow it to attach for each
@@ -1659,13 +1663,14 @@ func (t *Translator) ProcessTCPRoutes(tcpRoutes []*gwapiv1a2.TCPRoute, gateways
xdsIR resource.XdsIRMap,
) []*TCPRouteContext {
relevantTCPRoutes := make([]*TCPRouteContext, 0, len(tcpRoutes))
+ tcpRouteCopies := tcpRouteCopiesWithStatusDeepCopy(tcpRoutes)
// TCPRoutes are already sorted by the provider layer
- for _, tcp := range tcpRoutes {
+ for i, tcp := range tcpRoutes {
if tcp == nil {
panic("received nil tcproute")
}
- tcpRoute := &TCPRouteContext{TCPRoute: tcp}
+ tcpRoute := &TCPRouteContext{TCPRoute: tcpRouteCopies[i]}
// Find out if this route attaches to one of our Gateway's listeners,
// and if so, get the list of listeners that allow it to attach for each
@@ -2450,11 +2455,15 @@ func (t *Translator) processBackendDestinationSetting(
protocol ir.AppProtocol,
) *ir.DestinationSetting {
var dstAddrType *ir.DestinationAddressType
+ forceHTTP1Upstream := false
addrTypeMap := make(map[ir.DestinationAddressType]int)
backend := t.GetBackend(backendNamespace, string(backendRef.Name))
for _, ap := range backend.Spec.AppProtocols {
protocol = backendAppProtocolToIRAppProtocol(ap, protocol)
+ // For WebSocket backends, force HTTP/1.1 upstream to ensure Envoy can establish a successful connection,
+ // as WebSocket over HTTP/2 is not widely supported by upstreams and can lead to connection failures.
+ forceHTTP1Upstream = forceHTTP1Upstream || isWebSocketBackendAppProtocol(ap)
}
ds := &ir.DestinationSetting{Name: name}
@@ -2463,6 +2472,7 @@ func (t *Translator) processBackendDestinationSetting(
if backend.Spec.Type != nil && *backend.Spec.Type == egv1a1.BackendTypeDynamicResolver {
ds.IsDynamicResolver = true
ds.Protocol = protocol
+ ds.ForceHTTP1Upstream = forceHTTP1Upstream
return ds
}
@@ -2505,6 +2515,7 @@ func (t *Translator) processBackendDestinationSetting(
ds.Endpoints = dstEndpoints
ds.AddressType = dstAddrType
ds.Protocol = protocol
+ ds.ForceHTTP1Upstream = forceHTTP1Upstream
if backend.Spec.Fallback != nil {
// set only the secondary priority, the backend defaults to a primary priority if unset.
@@ -2537,6 +2548,8 @@ func backendAppProtocolToIRAppProtocol(ap egv1a1.AppProtocolType, defaultProtoco
switch ap {
case egv1a1.AppProtocolTypeH2C:
return ir.HTTP2
+ case egv1a1.AppProtocolTypeWS, egv1a1.AppProtocolTypeWSS:
+ return ir.HTTP
case "grpc":
return ir.GRPC
default:
@@ -2544,6 +2557,15 @@ func backendAppProtocolToIRAppProtocol(ap egv1a1.AppProtocolType, defaultProtoco
}
}
+func isWebSocketBackendAppProtocol(ap egv1a1.AppProtocolType) bool {
+ switch ap {
+ case egv1a1.AppProtocolTypeWS, egv1a1.AppProtocolTypeWSS:
+ return true
+ default:
+ return false
+ }
+}
+
func getStatPattern(routeContext RouteContext, parentRef *RouteParentContext, controllerName string) string {
var pattern string
var envoyProxy *egv1a1.EnvoyProxy
@@ -2571,3 +2593,56 @@ func buildStatName(pattern string, route RouteContext, ruleName *gwapiv1.Section
statName = strings.ReplaceAll(statName, egv1a1.StatFormatterBackendRefs, strings.Join(refs, "|"))
return statName
}
+
+// Shallow-copy helpers that deep-copy only the Status field.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+
+func httpRouteCopiesWithStatusDeepCopy(routes []*gwapiv1.HTTPRoute) []*gwapiv1.HTTPRoute {
+ copies := make([]*gwapiv1.HTTPRoute, len(routes))
+ for i, r := range routes {
+ out := *r
+ r.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
+
+func grpcRouteCopiesWithStatusDeepCopy(routes []*gwapiv1.GRPCRoute) []*gwapiv1.GRPCRoute {
+ copies := make([]*gwapiv1.GRPCRoute, len(routes))
+ for i, r := range routes {
+ out := *r
+ r.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
+
+func tlsRouteCopiesWithStatusDeepCopy(routes []*gwapiv1a3.TLSRoute) []*gwapiv1a3.TLSRoute {
+ copies := make([]*gwapiv1a3.TLSRoute, len(routes))
+ for i, r := range routes {
+ out := *r
+ r.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
+
+func udpRouteCopiesWithStatusDeepCopy(routes []*gwapiv1a2.UDPRoute) []*gwapiv1a2.UDPRoute {
+ copies := make([]*gwapiv1a2.UDPRoute, len(routes))
+ for i, r := range routes {
+ out := *r
+ r.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
+
+func tcpRouteCopiesWithStatusDeepCopy(routes []*gwapiv1a2.TCPRoute) []*gwapiv1a2.TCPRoute {
+ copies := make([]*gwapiv1a2.TCPRoute, len(routes))
+ for i, r := range routes {
+ out := *r
+ r.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/runner/runner.go b/internal/gatewayapi/runner/runner.go
index c68fd5d56e..674b183213 100644
--- a/internal/gatewayapi/runner/runner.go
+++ b/internal/gatewayapi/runner/runner.go
@@ -166,7 +166,7 @@ func (r *Runner) startWasmCache(ctx context.Context) {
}
r.wasmCache = wasm.NewHTTPServerWithFileCache(
// HTTP server options
- wasm.SeverOptions{
+ wasm.ServerOptions{
Salt: salt,
TLSConfig: tlsConfig,
},
diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go
index 65ac1a4f66..a0386a4c5b 100644
--- a/internal/gatewayapi/securitypolicy.go
+++ b/internal/gatewayapi/securitypolicy.go
@@ -114,6 +114,8 @@ func (t *Translator) ProcessSecurityPolicies(
// The routes are grouped by sectionNames of their targetRefs
gatewayRouteMap := make(map[string]map[string]sets.Set[string], gatewayMapSize)
+ policyCopies := securityPolicyCopiesWithStatusDeepCopy(securityPolicies)
+
handledPolicies := make(map[types.NamespacedName]*egv1a1.SecurityPolicy, policyMapSize)
// Translate
@@ -123,7 +125,7 @@ func (t *Translator) ProcessSecurityPolicies(
// 4. Finally, the policies targeting Gateways
// Process the policies targeting RouteRules (HTTP + TCP)
- for _, currPolicy := range securityPolicies {
+ for i, currPolicy := range securityPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -131,7 +133,7 @@ func (t *Translator) ProcessSecurityPolicies(
if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -142,7 +144,7 @@ func (t *Translator) ProcessSecurityPolicies(
}
}
// Process the policies targeting xRoutes (HTTP + TCP)
- for _, currPolicy := range securityPolicies {
+ for i, currPolicy := range securityPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -150,7 +152,7 @@ func (t *Translator) ProcessSecurityPolicies(
if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -161,7 +163,7 @@ func (t *Translator) ProcessSecurityPolicies(
}
}
// Process the policies targeting Listeners
- for _, currPolicy := range securityPolicies {
+ for i, currPolicy := range securityPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -169,7 +171,7 @@ func (t *Translator) ProcessSecurityPolicies(
if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -180,7 +182,7 @@ func (t *Translator) ProcessSecurityPolicies(
}
}
// Process the policies targeting Gateways
- for _, currPolicy := range securityPolicies {
+ for i, currPolicy := range securityPolicies {
policyName := utils.NamespacedName(currPolicy)
targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace)
for _, currTarget := range targetRefs {
@@ -188,7 +190,7 @@ func (t *Translator) ProcessSecurityPolicies(
if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil {
policy, found := handledPolicies[policyName]
if !found {
- policy = currPolicy
+ policy = policyCopies[i]
handledPolicies[policyName] = policy
res = append(res, policy)
}
@@ -2129,3 +2131,15 @@ func defaultAuthorizationRuleName(policy *egv1a1.SecurityPolicy, index int) stri
irConfigName(policy),
strconv.Itoa(index))
}
+
+// securityPolicyCopiesWithStatusDeepCopy returns shallow copies with deep-copied Status fields.
+// Status is mutated during translation and shares a pointer with the watchable coalesce goroutine.
+func securityPolicyCopiesWithStatusDeepCopy(policies []*egv1a1.SecurityPolicy) []*egv1a1.SecurityPolicy {
+ copies := make([]*egv1a1.SecurityPolicy, len(policies))
+ for i, p := range policies {
+ out := *p
+ p.Status.DeepCopyInto(&out.Status)
+ copies[i] = &out
+ }
+ return copies
+}
diff --git a/internal/gatewayapi/status/error.go b/internal/gatewayapi/status/error.go
index 7e310da62b..57b712fb8e 100644
--- a/internal/gatewayapi/status/error.go
+++ b/internal/gatewayapi/status/error.go
@@ -225,7 +225,7 @@ func (m *MultiStatusError) Reason() gwapiv1.RouteConditionReason {
reasonList = append(reasonList, reason)
}
sort.Strings(reasonList)
- return gwapiv1.RouteConditionReason(strings.Join(reasonList, ", "))
+ return gwapiv1.RouteConditionReason(strings.Join(reasonList, ","))
}
func (m *MultiStatusError) Type() gwapiv1.RouteConditionType {
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-healthcheck-host-hierarchy.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-healthcheck-host-hierarchy.in.yaml
new file mode 100644
index 0000000000..7a45a5401a
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-healthcheck-host-hierarchy.in.yaml
@@ -0,0 +1,171 @@
+# Health check Host header hierarchy:
+# 1. BackendTrafficPolicy healthCheck.active.http.hostname wins.
+# 2. Backend.spec.endpoints[].hostname overrides the route-derived default.
+# 3. Effective HTTPRoute/GRPCRoute hostname for the listener remains the fallback
+# when the endpoint has no hostname.
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: All
+backends:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ namespace: default
+ name: backend-with-hostname
+ spec:
+ endpoints:
+ - hostname: backend-endpoint.example.com
+ fqdn:
+ hostname: backend-address.example.com
+ port: 443
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ namespace: default
+ name: backend-without-hostname
+ spec:
+ endpoints:
+ - fqdn:
+ hostname: backend-no-endpoint-host.example.com
+ port: 443
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-btp-host
+ spec:
+ hostnames:
+ - btp-route.example.com
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: /btp-host
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-with-hostname
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-backend-host
+ spec:
+ hostnames:
+ - backend-route.example.com
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: /backend-host
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-with-hostname
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-route-host
+ spec:
+ hostnames:
+ - route-fallback.example.com
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: /route-host
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-without-hostname
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: default
+ name: policy-btp-host
+ spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-btp-host
+ healthCheck:
+ active:
+ timeout: 2s
+ interval: 5s
+ healthyThreshold: 1
+ unhealthyThreshold: 3
+ type: HTTP
+ http:
+ hostname: btp-health.example.com
+ path: /healthz
+ method: GET
+ expectedStatuses:
+ - 200
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: default
+ name: policy-backend-host
+ spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-backend-host
+ healthCheck:
+ active:
+ timeout: 2s
+ interval: 5s
+ healthyThreshold: 1
+ unhealthyThreshold: 3
+ type: HTTP
+ http:
+ path: /healthz
+ method: GET
+ expectedStatuses:
+ - 200
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: default
+ name: policy-route-host
+ spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-route-host
+ healthCheck:
+ active:
+ timeout: 2s
+ interval: 5s
+ healthyThreshold: 1
+ unhealthyThreshold: 3
+ type: HTTP
+ http:
+ path: /healthz
+ method: GET
+ expectedStatuses:
+ - 200
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-healthcheck-host-hierarchy.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-healthcheck-host-hierarchy.out.yaml
new file mode 100644
index 0000000000..97603fb6ad
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-healthcheck-host-hierarchy.out.yaml
@@ -0,0 +1,512 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-btp-host
+ namespace: default
+ spec:
+ healthCheck:
+ active:
+ healthyThreshold: 1
+ http:
+ expectedStatuses:
+ - 200
+ hostname: btp-health.example.com
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ type: HTTP
+ unhealthyThreshold: 3
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-btp-host
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-backend-host
+ namespace: default
+ spec:
+ healthCheck:
+ active:
+ healthyThreshold: 1
+ http:
+ expectedStatuses:
+ - 200
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ type: HTTP
+ unhealthyThreshold: 3
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-backend-host
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-route-host
+ namespace: default
+ spec:
+ healthCheck:
+ active:
+ healthyThreshold: 1
+ http:
+ expectedStatuses:
+ - 200
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ type: HTTP
+ unhealthyThreshold: 3
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-route-host
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+backends:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-with-hostname
+ namespace: default
+ spec:
+ endpoints:
+ - fqdn:
+ hostname: backend-address.example.com
+ port: 443
+ hostname: backend-endpoint.example.com
+ status:
+ conditions:
+ - lastTransitionTime: null
+ message: The Backend was accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-without-hostname
+ namespace: default
+ spec:
+ endpoints:
+ - fqdn:
+ hostname: backend-no-endpoint-host.example.com
+ port: 443
+ status:
+ conditions:
+ - lastTransitionTime: null
+ message: The Backend was accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 3
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-btp-host
+ namespace: default
+ spec:
+ hostnames:
+ - btp-route.example.com
+ parentRefs:
+ - name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-with-hostname
+ matches:
+ - path:
+ value: /btp-host
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-backend-host
+ namespace: default
+ spec:
+ hostnames:
+ - backend-route.example.com
+ parentRefs:
+ - name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-with-hostname
+ matches:
+ - path:
+ value: /backend-host
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-route-host
+ namespace: default
+ spec:
+ hostnames:
+ - route-fallback.example.com
+ parentRefs:
+ - name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-without-hostname
+ matches:
+ - path:
+ value: /route-host
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+infraIR:
+ envoy-gateway/gateway:
+ proxy:
+ listeners:
+ - address: null
+ name: envoy-gateway/gateway/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway
+ namespace: envoy-gateway-system
+xdsIR:
+ envoy-gateway/gateway:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-966d2c13
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-966d2c13
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ hostnames:
+ - '*'
+ isHTTP2: false
+ metadata:
+ kind: Gateway
+ name: gateway
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-backend-host
+ namespace: default
+ name: httproute/default/httproute-backend-host/rule/0
+ settings:
+ - addressType: FQDN
+ endpoints:
+ - host: backend-address.example.com
+ hostname: backend-endpoint.example.com
+ port: 443
+ metadata:
+ kind: Backend
+ name: backend-with-hostname
+ namespace: default
+ name: httproute/default/httproute-backend-host/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: backend-route.example.com
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-backend-host
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-backend-host
+ namespace: default
+ name: httproute/default/httproute-backend-host/rule/0/match/0/backend-route_example_com
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /backend-host
+ traffic:
+ healthCheck:
+ active:
+ healthyThreshold: 1
+ http:
+ expectedStatuses:
+ - 200
+ host: ""
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ unhealthyThreshold: 3
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-route-host
+ namespace: default
+ name: httproute/default/httproute-route-host/rule/0
+ settings:
+ - addressType: FQDN
+ endpoints:
+ - host: backend-no-endpoint-host.example.com
+ port: 443
+ metadata:
+ kind: Backend
+ name: backend-without-hostname
+ namespace: default
+ name: httproute/default/httproute-route-host/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: route-fallback.example.com
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-route-host
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-route-host
+ namespace: default
+ name: httproute/default/httproute-route-host/rule/0/match/0/route-fallback_example_com
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /route-host
+ traffic:
+ healthCheck:
+ active:
+ healthyThreshold: 1
+ http:
+ expectedStatuses:
+ - 200
+ host: ""
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ unhealthyThreshold: 3
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-btp-host
+ namespace: default
+ name: httproute/default/httproute-btp-host/rule/0
+ settings:
+ - addressType: FQDN
+ endpoints:
+ - host: backend-address.example.com
+ hostname: backend-endpoint.example.com
+ port: 443
+ metadata:
+ kind: Backend
+ name: backend-with-hostname
+ namespace: default
+ name: httproute/default/httproute-btp-host/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: btp-route.example.com
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-btp-host
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-btp-host
+ namespace: default
+ name: httproute/default/httproute-btp-host/rule/0/match/0/btp-route_example_com
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /btp-host
+ traffic:
+ healthCheck:
+ active:
+ healthyThreshold: 1
+ http:
+ expectedStatuses:
+ - 200
+ host: btp-health.example.com
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ unhealthyThreshold: 3
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
index 340025d579..b6b2065f24 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-healthcheck.out.yaml
@@ -882,7 +882,7 @@ xdsIR:
expectedStatuses:
- 200
- 300
- host: '*'
+ host: ""
method: GET
path: /healthz
interval: 3s
@@ -1171,7 +1171,7 @@ xdsIR:
text: pong
expectedStatuses:
- 200
- host: gateway.envoyproxy.io
+ host: ""
method: GET
path: /healthz
interval: 5s
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml
index 9a88aa4f50..7ce9618579 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml
@@ -1,3 +1,24 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-conn-buffer-bytes
+ namespace: envoy-gateway-2
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ type: JSONPatch
+ status:
+ ancestors: null
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml
index 105d725ccb..cf56d028e8 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-feature-disabled.out.yaml
@@ -1,3 +1,35 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-conn-buffer-bytes
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ conditions:
+ - lastTransitionTime: null
+ message: EnvoyPatchPolicy is disabled in the EnvoyGateway configuration
+ reason: Disabled
+ status: "False"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-merge-gateways.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-merge-gateways.out.yaml
index b7b4b0f47e..3bbf3494b2 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-merge-gateways.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-merge-gateways.out.yaml
@@ -1,3 +1,56 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: gateway-class-not-exists
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: gateway-class-not-exists
+ type: JSONPatch
+ status:
+ ancestors: null
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-ignore-global-limit
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /ignore_global_conn_limit
+ value: "true"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ priority: -1
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml
index b4b746b866..703d7211f7 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind-merge-gateways.out.yaml
@@ -1,3 +1,24 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-conn-buffer-bytes
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ type: JSONPatch
+ status:
+ ancestors: null
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml
index 73b9604334..9e5bb4fedf 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml
@@ -1,3 +1,38 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-conn-buffer-bytes
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: MyGateway
+ name: gateway-1
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: TargetRef.Group:gateway.networking.k8s.io TargetRef.Kind:MyGateway,
+ only TargetRef.Group:gateway.networking.k8s.io and TargetRef.Kind:Gateway
+ is supported.
+ reason: Invalid
+ status: "False"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-name.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-name.out.yaml
index cf77d13518..68ed8b9bff 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-name.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-name.out.yaml
@@ -1,3 +1,60 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ generation: 10
+ name: gateway-not-exists
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-not-exists
+ type: JSONPatch
+ status:
+ ancestors: null
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ generation: 10
+ name: edit-ignore-global-limit
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /ignore_global_conn_limit
+ value: "true"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ priority: -1
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ observedGeneration: 10
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml
index fca40f1fe3..2d295a28a9 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-no-status-for-unknown-gateway.out.yaml
@@ -1,2 +1,16 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: target-unknown-gateway
+ namespace: envoy-gateway
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: unknown-gateway
+ type: JSONPatch
+ status:
+ ancestors: null
infraIR: {}
xdsIR: {}
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml
index 6f72c47662..241305740c 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-valid-merge-gateways.out.yaml
@@ -1,3 +1,67 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-conn-buffer-bytes
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ name: edit-ignore-global-limit
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /ignore_global_conn_limit
+ value: "true"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ priority: -1
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: GatewayClass
+ name: envoy-gateway-class
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml
index 307508cc62..096008d69f 100644
--- a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml
+++ b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml
@@ -1,3 +1,73 @@
+envoyPatchPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ generation: 10
+ name: edit-conn-buffer-bytes
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /per_connection_buffer_limit_bytes
+ value: "1024"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ observedGeneration: 10
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: EnvoyPatchPolicy
+ metadata:
+ generation: 10
+ name: edit-ignore-global-limit
+ namespace: envoy-gateway
+ spec:
+ jsonPatches:
+ - name: envoy-gateway-gateway-1-http
+ operation:
+ op: replace
+ path: /ignore_global_conn_limit
+ value: "true"
+ type: type.googleapis.com/envoy.config.listener.v3.Listener
+ priority: -1
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ type: JSONPatch
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ observedGeneration: 10
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
diff --git a/internal/gatewayapi/testdata/grpcroute-with-conflicting-filters.out.yaml b/internal/gatewayapi/testdata/grpcroute-with-conflicting-filters.out.yaml
index 5057df4a69..443cecca1b 100644
--- a/internal/gatewayapi/testdata/grpcroute-with-conflicting-filters.out.yaml
+++ b/internal/gatewayapi/testdata/grpcroute-with-conflicting-filters.out.yaml
@@ -80,7 +80,7 @@ grpcRoutes:
message: |-
Dropped Rule(s) [0]: Invalid filter ExtensionRef: unknown kind gateway.envoyproxy.io/HTTPRouteFilter.
RequestMirror filter cannot be used when the rule also configures a DirectResponse filter.
- reason: IncompatibleFilters, UnsupportedValue
+ reason: IncompatibleFilters,UnsupportedValue
status: "True"
type: PartiallyInvalid
- lastTransitionTime: null
diff --git a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-websocket-app-protocols.in.yaml b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-websocket-app-protocols.in.yaml
new file mode 100644
index 0000000000..02cadc6d99
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-websocket-app-protocols.in.yaml
@@ -0,0 +1,107 @@
+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-ws
+ spec:
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ rules:
+ - matches:
+ - path:
+ value: "/ws"
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ws
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-wss
+ spec:
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ rules:
+ - matches:
+ - path:
+ value: "/wss"
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-wss
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-h2c
+ spec:
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ rules:
+ - matches:
+ - path:
+ value: "/h2c"
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-h2c
+backends:
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-ws
+ namespace: default
+ spec:
+ appProtocols:
+ - gateway.envoyproxy.io/ws
+ endpoints:
+ - ip:
+ address: 10.244.0.28
+ port: 3000
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-wss
+ namespace: default
+ spec:
+ appProtocols:
+ - gateway.envoyproxy.io/wss
+ endpoints:
+ - fqdn:
+ hostname: websocket.example.com
+ port: 443
+ tls:
+ wellKnownCACertificates: System
+ sni: websocket.example.com
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-h2c
+ namespace: default
+ spec:
+ appProtocols:
+ - gateway.envoyproxy.io/h2c
+ endpoints:
+ - fqdn:
+ hostname: primary.foo.com
+ port: 3000
diff --git a/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-websocket-app-protocols.out.yaml b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-websocket-app-protocols.out.yaml
new file mode 100644
index 0000000000..bd2ed04b2a
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-rule-with-non-service-backends-and-websocket-app-protocols.out.yaml
@@ -0,0 +1,367 @@
+backends:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-ws
+ namespace: default
+ spec:
+ appProtocols:
+ - gateway.envoyproxy.io/ws
+ endpoints:
+ - ip:
+ address: 10.244.0.28
+ port: 3000
+ status:
+ conditions:
+ - lastTransitionTime: null
+ message: The Backend was accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-wss
+ namespace: default
+ spec:
+ appProtocols:
+ - gateway.envoyproxy.io/wss
+ endpoints:
+ - fqdn:
+ hostname: websocket.example.com
+ port: 443
+ tls:
+ sni: websocket.example.com
+ wellKnownCACertificates: System
+ status:
+ conditions:
+ - lastTransitionTime: null
+ message: The Backend was accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: Backend
+ metadata:
+ name: backend-h2c
+ namespace: default
+ spec:
+ appProtocols:
+ - gateway.envoyproxy.io/h2c
+ endpoints:
+ - fqdn:
+ hostname: primary.foo.com
+ port: 3000
+ status:
+ conditions:
+ - lastTransitionTime: null
+ message: The Backend was accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 3
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-ws
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-ws
+ matches:
+ - path:
+ value: /ws
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-wss
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-wss
+ matches:
+ - path:
+ value: /wss
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-h2c
+ namespace: default
+ spec:
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ rules:
+ - backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-h2c
+ matches:
+ - path:
+ value: /h2c
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - address: null
+ name: envoy-gateway/gateway-1/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-1
+ namespace: envoy-gateway-system
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ hostnames:
+ - '*'
+ isHTTP2: false
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-wss
+ namespace: default
+ name: httproute/default/httproute-wss/rule/0
+ settings:
+ - addressType: FQDN
+ endpoints:
+ - host: websocket.example.com
+ port: 443
+ forceHTTP1Upstream: true
+ metadata:
+ kind: Backend
+ name: backend-wss
+ namespace: default
+ name: httproute/default/httproute-wss/rule/0/backend/0
+ protocol: HTTP
+ tls:
+ alpnProtocols: null
+ caCertificate:
+ name: backend-wss/default-ca
+ sni: websocket.example.com
+ useSystemTrustStore: true
+ weight: 1
+ hostname: '*'
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-wss
+ namespace: default
+ name: httproute/default/httproute-wss/rule/0/match/0/*
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /wss
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-h2c
+ namespace: default
+ name: httproute/default/httproute-h2c/rule/0
+ settings:
+ - addressType: FQDN
+ endpoints:
+ - host: primary.foo.com
+ port: 3000
+ metadata:
+ kind: Backend
+ name: backend-h2c
+ namespace: default
+ name: httproute/default/httproute-h2c/rule/0/backend/0
+ protocol: HTTP2
+ weight: 1
+ hostname: '*'
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-h2c
+ namespace: default
+ name: httproute/default/httproute-h2c/rule/0/match/0/*
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /h2c
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-ws
+ namespace: default
+ name: httproute/default/httproute-ws/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 10.244.0.28
+ port: 3000
+ forceHTTP1Upstream: true
+ metadata:
+ kind: Backend
+ name: backend-ws
+ namespace: default
+ name: httproute/default/httproute-ws/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: '*'
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-ws
+ namespace: default
+ name: httproute/default/httproute-ws/rule/0/match/0/*
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /ws
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml
index f39c951e5f..09b7115694 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml
@@ -125,6 +125,42 @@ httpRoutes:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /rewrite
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-5
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/not-found-filter"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: not-found-filter
+ filters:
+ - type: URLRewrite
+ urlRewrite:
+ path:
+ type: ReplacePrefixMatch
+ replacePrefixMatch: /rewrite
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: not-found-filter
httpFilters:
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: HTTPRouteFilter
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
index 5d4c76a9fa..5f23192906 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
@@ -16,7 +16,7 @@ gateways:
protocol: HTTP
status:
listeners:
- - attachedRoutes: 4
+ - attachedRoutes: 5
conditions:
- lastTransitionTime: null
message: Sending translated listener configuration to the data plane
@@ -222,6 +222,62 @@ httpRoutes:
name: gateway-1
namespace: envoy-gateway
sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-5
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: not-found-filter
+ type: ExtensionRef
+ name: service-1
+ port: 8080
+ filters:
+ - type: URLRewrite
+ urlRewrite:
+ path:
+ replacePrefixMatch: /rewrite
+ type: ReplacePrefixMatch
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: not-found-filter
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /not-found-filter
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: |-
+ Unable to translate HTTPRouteFilter: default/not-found-filter.
+ Failed to process route rule 0 backendRef 0: Unable to translate HTTPRouteFilter: default/not-found-filter.
+ reason: InvalidBackendFilters,UnsupportedValue
+ status: "False"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
infraIR:
envoy-gateway/gateway-1:
proxy:
@@ -357,6 +413,23 @@ xdsIR:
path:
fullReplace: null
prefixMatchReplace: /rewrite
+ - directResponse:
+ statusCode: 500
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-5
+ namespace: default
+ name: httproute/default/httproute-5/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /not-found-filter
+ urlRewrite:
+ path:
+ fullReplace: null
+ prefixMatchReplace: /rewrite
- destination:
metadata:
kind: HTTPRoute
diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go
index 08b72d9f1f..dece035e09 100644
--- a/internal/gatewayapi/translator.go
+++ b/internal/gatewayapi/translator.go
@@ -147,6 +147,7 @@ func newTranslateResult(
securityPolicies []*egv1a1.SecurityPolicy,
backendTLSPolicies []*gwapiv1.BackendTLSPolicy,
envoyExtensionPolicies []*egv1a1.EnvoyExtensionPolicy,
+ envoyPatchPolicies []*egv1a1.EnvoyPatchPolicy,
extPolicies []unstructured.Unstructured,
backends []*egv1a1.Backend,
xListenerSets []*gwapixv1a1.XListenerSet,
@@ -216,6 +217,9 @@ func newTranslateResult(
if len(envoyExtensionPolicies) > 0 {
translateResult.EnvoyExtensionPolicies = envoyExtensionPolicies
}
+ if len(envoyPatchPolicies) > 0 {
+ translateResult.EnvoyPatchPolicies = envoyPatchPolicies
+ }
if len(extPolicies) > 0 {
translateResult.ExtensionServerPolicies = extPolicies
}
@@ -264,7 +268,7 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult,
t.ProcessXListenerSetStatus(resources.XListenerSets)
// Process EnvoyPatchPolicies
- t.ProcessEnvoyPatchPolicies(resources.EnvoyPatchPolicies, xdsIR)
+ envoyPatchPolicies := t.ProcessEnvoyPatchPolicies(resources.EnvoyPatchPolicies, xdsIR)
// Process all Addresses for all relevant Gateways.
t.ProcessAddresses(acceptedGateways, xdsIR, infraIR)
@@ -358,7 +362,7 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult,
allGateways, httpRoutes, grpcRoutes, tlsRoutes,
tcpRoutes, udpRoutes, clientTrafficPolicies, backendTrafficPolicies,
securityPolicies, resources.BackendTLSPolicies, envoyExtensionPolicies,
- extServerPolicies, backends, resources.XListenerSets, xdsIR, infraIR), errs
+ envoyPatchPolicies, extServerPolicies, backends, resources.XListenerSets, xdsIR, infraIR), errs
}
// GetRelevantGateways returns GatewayContexts, containing a copy of the original
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index d15b55bd35..8ac2e0f33a 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -67,7 +67,6 @@ var (
ErrHealthCheckUnhealthyThresholdInvalid = errors.New("field HealthCheck.UnhealthyThreshold should be greater than 0")
ErrHealthCheckHealthyThresholdInvalid = errors.New("field HealthCheck.HealthyThreshold should be greater than 0")
ErrHealthCheckerInvalid = errors.New("health checker setting is invalid, only one health checker can be set")
- ErrHCHTTPHostInvalid = errors.New("field HTTPHealthChecker.Host should be specified")
ErrHCHTTPPathInvalid = errors.New("field HTTPHealthChecker.Path should be specified")
ErrHCHTTPMethodInvalid = errors.New("only one of the GET, HEAD, POST, DELETE, OPTIONS, TRACE, PATCH of HTTPHealthChecker.Method could be set")
ErrHCHTTPExpectedStatusesInvalid = errors.New("field HTTPHealthChecker.ExpectedStatuses should be specified")
@@ -1726,7 +1725,8 @@ func (r *RouteDestination) Validate() error {
func (r *RouteDestination) NeedsClusterPerSetting() bool {
return r.HasMixedEndpoints() ||
r.HasFiltersInSettings() ||
- (len(r.Settings) > 1 && r.HasPreferLocalZone())
+ (len(r.Settings) > 1 && r.HasPreferLocalZone()) ||
+ r.HasMixedUpstreamProtocolRequirements()
}
// HasMixedEndpoints returns true if the RouteDestination has endpoints of multiple types
@@ -1761,6 +1761,27 @@ func (r *RouteDestination) HasPreferLocalZone() bool {
return false
}
+// HasMixedUpstreamProtocolRequirements returns true if destination settings require
+// mutually exclusive cluster-level upstream protocol configuration.
+func (r *RouteDestination) HasMixedUpstreamProtocolRequirements() bool {
+ hasForceHTTP1Upstream := false
+ hasHTTP2Upstream := false
+
+ for _, setting := range r.Settings {
+ if setting == nil {
+ continue
+ }
+ if setting.ForceHTTP1Upstream {
+ hasForceHTTP1Upstream = true
+ }
+ if setting.Protocol == HTTP2 || setting.Protocol == GRPC {
+ hasHTTP2Upstream = true
+ }
+ }
+
+ return hasForceHTTP1Upstream && hasHTTP2Upstream
+}
+
func (r *RouteDestination) ToBackendWeights() *BackendWeights {
w := &BackendWeights{
Name: r.Name,
@@ -1810,8 +1831,11 @@ type DestinationSetting struct {
// Lower priority endpoints will be used only if higher priority levels are unavailable.
Priority *uint32 `json:"priority,omitempty"`
// Protocol associated with this destination/port.
- Protocol AppProtocol `json:"protocol,omitempty" yaml:"protocol,omitempty"`
- Endpoints []*DestinationEndpoint `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
+ Protocol AppProtocol `json:"protocol,omitempty" yaml:"protocol,omitempty"`
+ // ForceHTTP1Upstream requires Envoy to use explicit HTTP/1.1 upstream protocol selection.
+ // This is used for websocket backends where upstream HTTP/2 negotiation would break upgrades.
+ ForceHTTP1Upstream bool `json:"forceHTTP1Upstream,omitempty" yaml:"forceHTTP1Upstream,omitempty"`
+ Endpoints []*DestinationEndpoint `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
// AddressTypeState specifies the state of DestinationEndpoint address type.
AddressType *DestinationAddressType `json:"addressType,omitempty" yaml:"addressType,omitempty"`
// IPFamily specifies the IP family (IPv4 or IPv6) to use for this destination's endpoints.
@@ -2879,12 +2903,6 @@ type ActiveHealthCheck struct {
GRPC *GRPCHealthChecker `json:"grpc,omitempty" yaml:"grpc,omitempty"`
}
-func (h *HealthCheck) SetHTTPHostIfAbsent(host string) {
- if h != nil && h.Active != nil && h.Active.HTTP != nil && h.Active.HTTP.Host == "" {
- h.Active.HTTP.Host = host
- }
-}
-
// Validate the fields within the HealthCheck structure.
func (h *HealthCheck) Validate() error {
var errs error
@@ -2970,9 +2988,6 @@ type HTTPHealthChecker struct {
// Validate the fields within the HTTPHealthChecker structure.
func (c *HTTPHealthChecker) Validate() error {
var errs error
- if c.Host == "" {
- errs = errors.Join(errs, ErrHCHTTPHostInvalid)
- }
if c.Path == "" {
errs = errors.Join(errs, ErrHCHTTPPathInvalid)
}
diff --git a/internal/ir/xds_test.go b/internal/ir/xds_test.go
index ea14191c46..5194295c01 100644
--- a/internal/ir/xds_test.go
+++ b/internal/ir/xds_test.go
@@ -1107,6 +1107,95 @@ func TestRouteDestination_NeedsClusterPerSetting(t *testing.T) {
},
expected: true,
},
+ {
+ name: "cluster per setting mixed upstream protocol requirements",
+ input: RouteDestination{
+ Name: "valid hostname",
+ Settings: []*DestinationSetting{
+ {
+ Endpoints: []*DestinationEndpoint{
+ {
+ Host: "10.0.1.1",
+ Port: 8080,
+ },
+ },
+ AddressType: ptr.To(IP),
+ Protocol: HTTP,
+ ForceHTTP1Upstream: true,
+ },
+ {
+ Endpoints: []*DestinationEndpoint{
+ {
+ Host: "10.0.1.2",
+ Port: 8080,
+ },
+ },
+ AddressType: ptr.To(IP),
+ Protocol: HTTP2,
+ },
+ },
+ },
+ expected: true,
+ },
+ {
+ name: "single cluster compatible websocket and http backends",
+ input: RouteDestination{
+ Name: "valid hostname",
+ Settings: []*DestinationSetting{
+ {
+ Endpoints: []*DestinationEndpoint{
+ {
+ Host: "10.0.1.1",
+ Port: 8080,
+ },
+ },
+ AddressType: ptr.To(IP),
+ Protocol: HTTP,
+ ForceHTTP1Upstream: true,
+ },
+ {
+ Endpoints: []*DestinationEndpoint{
+ {
+ Host: "10.0.1.2",
+ Port: 8080,
+ },
+ },
+ AddressType: ptr.To(IP),
+ Protocol: HTTP,
+ },
+ },
+ },
+ expected: false,
+ },
+ {
+ name: "single cluster compatible http2 and grpc backends",
+ input: RouteDestination{
+ Name: "valid hostname",
+ Settings: []*DestinationSetting{
+ {
+ Endpoints: []*DestinationEndpoint{
+ {
+ Host: "10.0.1.1",
+ Port: 8080,
+ },
+ },
+ AddressType: ptr.To(IP),
+ Protocol: HTTP2,
+ },
+ {
+ Endpoints: []*DestinationEndpoint{
+ {
+ Host: "10.0.1.2",
+ Port: 8080,
+ },
+ },
+ AddressType: ptr.To(IP),
+ Protocol: GRPC,
+ },
+ },
+ },
+ expected: false,
+ },
}
for _, test := range tests {
@@ -1577,7 +1666,7 @@ func TestValidateHealthCheck(t *testing.T) {
want: ErrHealthCheckHealthyThresholdInvalid,
},
{
- name: "http-health-check: invalid host",
+ name: "http-health-check: empty host",
input: HealthCheck{
&ActiveHealthCheck{
Timeout: MetaV1DurationPtr(time.Second),
@@ -1593,7 +1682,6 @@ func TestValidateHealthCheck(t *testing.T) {
&OutlierDetection{},
ptr.To[uint32](10),
},
- want: ErrHCHTTPHostInvalid,
},
{
name: "http-health-check: invalid path",
diff --git a/internal/wasm/httpserver.go b/internal/wasm/httpserver.go
index e7cdfed2e3..f655b8a38e 100644
--- a/internal/wasm/httpserver.go
+++ b/internal/wasm/httpserver.go
@@ -30,7 +30,7 @@ const (
var _ Cache = &HTTPServer{}
-type SeverOptions struct {
+type ServerOptions struct {
// Salt is used as a hash salt to generate an unguessable path for the Wasm module.
Salt []byte
// TLSConfig is the TLS configuration for the HTTP server.
@@ -41,7 +41,7 @@ type SeverOptions struct {
}
// setDefault sets the default values for the server options if they are not set.
-func (o *SeverOptions) setDefault() {
+func (o *ServerOptions) setDefault() {
if o.MaxFailedAttempts == 0 {
o.MaxFailedAttempts = defaultMaxFailedAttempts
}
@@ -55,7 +55,7 @@ func (o *SeverOptions) setDefault() {
// HTTPServer wraps a local file cache and serves the Wasm modules over HTTP.
type HTTPServer struct {
- SeverOptions
+ ServerOptions
sync.Mutex
// map from the mapping path to the wasm file path in the local cache.
// The mapping path is a generated unguessable path to prevent unauthorized users
@@ -97,11 +97,11 @@ type wasmModuleEntry struct {
// NewHTTPServerWithFileCache creates a HTTP server with a local file cache for Wasm modules.
// The local file cache is used to store the Wasm modules downloaded from the original URL.
// The HTTP server serves the cached Wasm modules over HTTP to the Envoy Proxies.
-func NewHTTPServerWithFileCache(serverOptions SeverOptions, cacheOptions CacheOptions, controllerNamespace string, logger logging.Logger) *HTTPServer {
+func NewHTTPServerWithFileCache(serverOptions ServerOptions, cacheOptions CacheOptions, controllerNamespace string, logger logging.Logger) *HTTPServer {
logger = logger.WithName("wasm-cache")
serverOptions.setDefault()
return &HTTPServer{
- SeverOptions: serverOptions,
+ ServerOptions: serverOptions,
mappingPath2Cache: make(map[string]wasmModuleEntry),
failedAttempts: make(map[string]attemptEntry),
cache: newLocalFileCache(cacheOptions, logger),
diff --git a/internal/wasm/httpserver_test.go b/internal/wasm/httpserver_test.go
index ed3e636445..aa7d14a3af 100644
--- a/internal/wasm/httpserver_test.go
+++ b/internal/wasm/httpserver_test.go
@@ -330,7 +330,7 @@ func setupFakeRegistry(host string) error {
func startLocalHTTPServer(ctx context.Context, cacheDir string, maxFailedAttempts int, failedAttemptResetDelay, failedAttemptsResetInterval time.Duration) (*HTTPServer, error) {
logger := logging.DefaultLogger(os.Stdout, egv1a1.LogLevelInfo)
s := NewHTTPServerWithFileCache(
- SeverOptions{
+ ServerOptions{
Salt: []byte("salt"),
MaxFailedAttempts: maxFailedAttempts,
FailedAttemptResetDelay: failedAttemptResetDelay,
diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go
index 6f82c5d4a3..19a043f400 100644
--- a/internal/xds/translator/cluster.go
+++ b/internal/xds/translator/cluster.go
@@ -67,6 +67,7 @@ type xdsClusterArgs struct {
proxyProtocol *ir.ProxyProtocol
circuitBreaker *ir.CircuitBreaker
healthCheck *ir.HealthCheck
+ routeHostname string
http1Settings *ir.HTTP1Settings
http2Settings *ir.HTTP2Settings
timeout *ir.Timeout
@@ -246,12 +247,16 @@ func buildXdsCluster(args *xdsClusterArgs) (*buildClusterResult, error) {
// influence transport socket specific settings
requiresAutoHTTPConfig := len(args.settings) > 0
requiresHTTP2Options := false
+ forceHTTP1UpstreamProtocol := false
hasLiteralSNI := false
for _, ds := range args.settings {
if ds.Protocol == ir.GRPC ||
ds.Protocol == ir.HTTP2 {
requiresHTTP2Options = true
}
+ if ds.ForceHTTP1Upstream {
+ forceHTTP1UpstreamProtocol = true
+ }
if ds.Protocol == ir.TCP {
requiresAutoHTTPConfig = false
@@ -315,7 +320,7 @@ func buildXdsCluster(args *xdsClusterArgs) (*buildClusterResult, error) {
}
// build common, HTTP/1 and HTTP/2 protocol options for cluster
- epo, secrets, err := buildTypedExtensionProtocolOptions(args, requiresAutoHTTPConfig, requiresHTTP2Options, requiresAutoSNI)
+ epo, secrets, err := buildTypedExtensionProtocolOptions(args, requiresAutoHTTPConfig, requiresHTTP2Options, requiresAutoSNI, forceHTTP1UpstreamProtocol)
if err != nil {
return nil, err
}
@@ -445,7 +450,7 @@ func buildXdsCluster(args *xdsClusterArgs) (*buildClusterResult, error) {
}
if args.healthCheck != nil && args.healthCheck.Active != nil {
- cluster.HealthChecks = buildXdsHealthCheck(args.healthCheck.Active)
+ cluster.HealthChecks = buildXdsHealthCheck(args.healthCheck.Active, args.routeHostname)
}
if args.healthCheck != nil && args.healthCheck.Passive != nil {
@@ -566,7 +571,7 @@ func buildZoneAwareLbConfig(preferLocal *ir.PreferLocalZone) *commonv3.LocalityL
return lbConfig
}
-func buildXdsHealthCheck(healthcheck *ir.ActiveHealthCheck) []*corev3.HealthCheck {
+func buildXdsHealthCheck(healthcheck *ir.ActiveHealthCheck, routeHostname string) []*corev3.HealthCheck {
hc := &corev3.HealthCheck{
Timeout: durationpb.New(healthcheck.Timeout.Duration),
Interval: durationpb.New(healthcheck.Interval.Duration),
@@ -585,7 +590,7 @@ func buildXdsHealthCheck(healthcheck *ir.ActiveHealthCheck) []*corev3.HealthChec
switch {
case healthcheck.HTTP != nil:
httpChecker := &corev3.HealthCheck_HttpHealthCheck{
- Host: healthcheck.HTTP.Host,
+ Host: httpHealthCheckHost(healthcheck.HTTP, routeHostname),
Path: healthcheck.HTTP.Path,
}
if healthcheck.HTTP.Method != nil {
@@ -618,6 +623,13 @@ func buildXdsHealthCheck(healthcheck *ir.ActiveHealthCheck) []*corev3.HealthChec
return []*corev3.HealthCheck{hc}
}
+func httpHealthCheckHost(healthcheck *ir.HTTPHealthChecker, routeHostname string) string {
+ if healthcheck.Host != "" {
+ return healthcheck.Host
+ }
+ return routeHostname
+}
+
func buildXdsOutlierDetection(outlierDetection *ir.OutlierDetection) *clusterv3.OutlierDetection {
od := &clusterv3.OutlierDetection{
BaseEjectionTime: durationpb.New(outlierDetection.BaseEjectionTime.Duration),
@@ -899,7 +911,7 @@ func buildHealthCheckConfig(hc *ir.HealthCheck, ep *ir.DestinationEndpoint) *end
}
}
-func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPConfig, requiresHTTP2Options, requiresAutoSNI bool) (map[string]*anypb.Any, []*tlsv3.Secret, error) {
+func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPConfig, requiresHTTP2Options, requiresAutoSNI, forceHTTP1UpstreamProtocol bool) (map[string]*anypb.Any, []*tlsv3.Secret, error) {
requiresCommonHTTPOptions := (args.timeout != nil && args.timeout.HTTP != nil &&
(args.timeout.HTTP.MaxConnectionDuration != nil || args.timeout.HTTP.ConnectionIdleTimeout != nil)) ||
(args.circuitBreaker != nil && args.circuitBreaker.MaxRequestsPerConnection != nil)
@@ -910,7 +922,7 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPCo
requiresHTTPFilters := len(args.settings) > 0 && args.settings[0].Filters != nil && args.settings[0].Filters.CredentialInjection != nil
requiredHTTPProtocolOptions := args.useClientProtocol || requiresAutoHTTPConfig ||
- requiresCommonHTTPOptions || requiresHTTP1Options || requiresHTTP2Options || requiresHTTPFilters || requiresAutoSNI
+ requiresCommonHTTPOptions || requiresHTTP1Options || requiresHTTP2Options || requiresHTTPFilters || requiresAutoSNI || forceHTTP1UpstreamProtocol
if !requiredHTTPProtocolOptions {
return nil, nil, nil
@@ -940,6 +952,7 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPCo
// If translation requires HTTP2 enablement or HTTP1 trailers, set appropriate setting
// Default to http1 otherwise
switch {
+ // If useClientProtocol is set, force Envoy to use the same protocol upstream as downstream, regardless of other settings.
case args.useClientProtocol:
protocolOptions.UpstreamProtocolOptions = &httpv3.HttpProtocolOptions_UseDownstreamProtocolConfig{
UseDownstreamProtocolConfig: &httpv3.HttpProtocolOptions_UseDownstreamHttpConfig{
@@ -947,6 +960,15 @@ func buildTypedExtensionProtocolOptions(args *xdsClusterArgs, requiresAutoHTTPCo
Http2ProtocolOptions: http2Opts,
},
}
+ // If forceHTTP1UpstreamProtocol is set, force Envoy to use HTTP1 upstream regardless of other settings.
+ case forceHTTP1UpstreamProtocol:
+ protocolOptions.UpstreamProtocolOptions = &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{
+ ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{
+ ProtocolConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{
+ HttpProtocolOptions: http1Opts,
+ },
+ },
+ }
case requiresHTTP2Options:
protocolOptions.UpstreamProtocolOptions = &httpv3.HttpProtocolOptions_ExplicitHttpConfig_{
ExplicitHttpConfig: &httpv3.HttpProtocolOptions_ExplicitHttpConfig{
@@ -1281,6 +1303,7 @@ func (httpRoute *HTTPRouteTranslator) asClusterArgs(name string,
settings: settings,
tSocket: nil,
endpointType: buildEndpointType(settings),
+ routeHostname: httpRoute.Hostname,
metrics: extra.metrics,
http1Settings: extra.http1Settings,
http2Settings: extra.http2Settings,
diff --git a/internal/xds/translator/testdata/in/xds-ir/health-check-host-hierarchy.yaml b/internal/xds/translator/testdata/in/xds-ir/health-check-host-hierarchy.yaml
new file mode 100644
index 0000000000..7a3ce9243a
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/health-check-host-hierarchy.yaml
@@ -0,0 +1,88 @@
+# Health check Host header hierarchy:
+# 1. Explicit BackendTrafficPolicy hostname is a cluster-level health check host.
+# 2. Backend endpoint hostname is emitted as Endpoint.HealthCheckConfig.hostname
+# when the cluster-level host was only defaulted from the effective route
+# hostname for the listener.
+# 3. Effective route hostname stays as the cluster-level fallback when no
+# endpoint hostname exists.
+http:
+- name: health-check-host-hierarchy-listener
+ address: "::"
+ path:
+ mergeSlashes: true
+ escapedSlashesAction: UnescapeAndRedirect
+ port: 10080
+ hostnames:
+ - "*"
+ routes:
+ - name: btp-host-route
+ hostname: btp-route.example.com
+ traffic:
+ healthCheck:
+ active:
+ timeout: 2s
+ interval: 5s
+ healthyThreshold: 1
+ unhealthyThreshold: 3
+ http:
+ host: btp-health.example.com
+ path: /healthz
+ method: GET
+ expectedStatuses:
+ - 200
+ destination:
+ name: btp-host-route-dest
+ settings:
+ - name: btp-host-route-dest/backend/0
+ addressType: FQDN
+ endpoints:
+ - host: backend-address.example.com
+ hostname: backend-endpoint.example.com
+ port: 443
+ - name: backend-host-route
+ hostname: backend-route.example.com
+ traffic:
+ healthCheck:
+ active:
+ timeout: 2s
+ interval: 5s
+ healthyThreshold: 1
+ unhealthyThreshold: 3
+ http:
+ host: ""
+ path: /healthz
+ method: GET
+ expectedStatuses:
+ - 200
+ destination:
+ name: backend-host-route-dest
+ settings:
+ - name: backend-host-route-dest/backend/0
+ addressType: FQDN
+ endpoints:
+ - host: backend-address.example.com
+ hostname: backend-endpoint.example.com
+ port: 443
+ - name: route-host-route
+ hostname: route-fallback.example.com
+ traffic:
+ healthCheck:
+ active:
+ timeout: 2s
+ interval: 5s
+ healthyThreshold: 1
+ unhealthyThreshold: 3
+ http:
+ host: ""
+ path: /healthz
+ method: GET
+ expectedStatuses:
+ - 200
+ destination:
+ name: route-host-route-dest
+ settings:
+ - name: route-host-route-dest/backend/0
+ addressType: FQDN
+ endpoints:
+ - host: backend-no-endpoint-host.example.com
+ port: 443
diff --git a/internal/xds/translator/testdata/in/xds-ir/mixed-websocket-and-h2c-backends.yaml b/internal/xds/translator/testdata/in/xds-ir/mixed-websocket-and-h2c-backends.yaml
new file mode 100644
index 0000000000..560512b839
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/mixed-websocket-and-h2c-backends.yaml
@@ -0,0 +1,34 @@
+# mixed websocket and h2c backends should be split into separate clusters with correct protocol options.
+# This may not happen in real world, but we want to make sure the translator can handle this case correctly.
+http:
+ - name: "weighted-protocol-listener"
+ address: "0.0.0.0"
+ port: 10080
+ hostnames:
+ - "*"
+ path:
+ mergeSlashes: true
+ escapedSlashesAction: UnescapeAndRedirect
+ routes:
+ - name: "weighted-protocol-route"
+ hostname: "*"
+ pathMatch:
+ prefix: /
+ destination:
+ name: "weighted-protocol-route-dest"
+ settings:
+ - name: "weighted-protocol-route-dest/backend/0"
+ addressType: IP
+ endpoints:
+ - host: "10.0.0.1"
+ port: 8080
+ protocol: HTTP
+ forceHTTP1Upstream: true
+ weight: 1
+ - name: "weighted-protocol-route-dest/backend/1"
+ addressType: IP
+ endpoints:
+ - host: "10.0.0.2"
+ port: 8080
+ protocol: HTTP2
+ weight: 1
diff --git a/internal/xds/translator/testdata/in/xds-ir/websocket-backend-force-http1-upstream.yaml b/internal/xds/translator/testdata/in/xds-ir/websocket-backend-force-http1-upstream.yaml
new file mode 100644
index 0000000000..b86e8bfdbb
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/websocket-backend-force-http1-upstream.yaml
@@ -0,0 +1,43 @@
+http:
+ - name: "websocket-listener"
+ address: "0.0.0.0"
+ port: 10080
+ hostnames:
+ - "*"
+ path:
+ mergeSlashes: true
+ escapedSlashesAction: UnescapeAndRedirect
+ routes:
+ - name: "websocket-wss-route"
+ hostname: "*"
+ destination:
+ name: "websocket-wss-route-dest"
+ settings:
+ - endpoints:
+ - host: "websocket.example.com"
+ port: 443
+ name: "websocket-wss-route-dest/backend/0"
+ protocol: HTTP
+ forceHTTP1Upstream: true
+ tls:
+ alpnProtocols: null
+ caCertificate:
+ name: websocket-wss-route/ca
+ sni: "websocket.example.com"
+ useSystemTrustStore: true
+ - name: "ordinary-tls-http-route"
+ hostname: "*"
+ destination:
+ name: "ordinary-tls-http-route-dest"
+ settings:
+ - endpoints:
+ - host: "ordinary.example.com"
+ port: 443
+ name: "ordinary-tls-http-route-dest/backend/0"
+ protocol: HTTP
+ tls:
+ alpnProtocols: null
+ caCertificate:
+ name: ordinary-tls-http-route/ca
+ sni: "ordinary.example.com"
+ useSystemTrustStore: true
diff --git a/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.clusters.yaml
new file mode 100644
index 0000000000..396729f67c
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.clusters.yaml
@@ -0,0 +1,142 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ dnsRefreshRate: 30s
+ healthChecks:
+ - healthyThreshold: 1
+ httpHealthCheck:
+ expectedStatuses:
+ - end: "201"
+ start: "200"
+ host: btp-health.example.com
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ unhealthyThreshold: 3
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadAssignment:
+ clusterName: btp-host-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: backend-address.example.com
+ portValue: 443
+ hostname: backend-endpoint.example.com
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: btp-host-route-dest/backend/0
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: btp-host-route-dest
+ perConnectionBufferLimitBytes: 32768
+ respectDnsTtl: true
+ type: STRICT_DNS
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ dnsRefreshRate: 30s
+ healthChecks:
+ - healthyThreshold: 1
+ httpHealthCheck:
+ expectedStatuses:
+ - end: "201"
+ start: "200"
+ host: backend-route.example.com
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ unhealthyThreshold: 3
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadAssignment:
+ clusterName: backend-host-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: backend-address.example.com
+ portValue: 443
+ healthCheckConfig:
+ hostname: backend-endpoint.example.com
+ hostname: backend-endpoint.example.com
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: backend-host-route-dest/backend/0
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: backend-host-route-dest
+ perConnectionBufferLimitBytes: 32768
+ respectDnsTtl: true
+ type: STRICT_DNS
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ dnsRefreshRate: 30s
+ healthChecks:
+ - healthyThreshold: 1
+ httpHealthCheck:
+ expectedStatuses:
+ - end: "201"
+ start: "200"
+ host: route-fallback.example.com
+ method: GET
+ path: /healthz
+ interval: 5s
+ timeout: 2s
+ unhealthyThreshold: 3
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadAssignment:
+ clusterName: route-host-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: backend-no-endpoint-host.example.com
+ portValue: 443
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: route-host-route-dest/backend/0
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: route-host-route-dest
+ perConnectionBufferLimitBytes: 32768
+ respectDnsTtl: true
+ type: STRICT_DNS
diff --git a/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.endpoints.yaml
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.endpoints.yaml
@@ -0,0 +1 @@
+[]
diff --git a/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.listeners.yaml
new file mode 100644
index 0000000000..a265e79d2a
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.listeners.yaml
@@ -0,0 +1,35 @@
+- address:
+ socketAddress:
+ address: '::'
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: health-check-host-hierarchy-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: health-check-host-hierarchy-listener
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: health-check-host-hierarchy-listener
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.routes.yaml
new file mode 100644
index 0000000000..98f5a1acaa
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/health-check-host-hierarchy.routes.yaml
@@ -0,0 +1,36 @@
+- ignorePortInHostMatching: true
+ name: health-check-host-hierarchy-listener
+ virtualHosts:
+ - domains:
+ - btp-route.example.com
+ name: health-check-host-hierarchy-listener/btp-route_example_com
+ routes:
+ - match:
+ prefix: /
+ name: btp-host-route
+ route:
+ cluster: btp-host-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ - domains:
+ - backend-route.example.com
+ name: health-check-host-hierarchy-listener/backend-route_example_com
+ routes:
+ - match:
+ prefix: /
+ name: backend-host-route
+ route:
+ cluster: backend-host-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ - domains:
+ - route-fallback.example.com
+ name: health-check-host-hierarchy-listener/route-fallback_example_com
+ routes:
+ - match:
+ prefix: /
+ name: route-host-route
+ route:
+ cluster: route-host-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.clusters.yaml
new file mode 100644
index 0000000000..44cb21e465
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.clusters.yaml
@@ -0,0 +1,60 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: weighted-protocol-route-dest/backend/0
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: weighted-protocol-route-dest/backend/0
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ explicitHttpConfig:
+ httpProtocolOptions: {}
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: weighted-protocol-route-dest/backend/1
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: weighted-protocol-route-dest/backend/1
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ explicitHttpConfig:
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.endpoints.yaml
new file mode 100644
index 0000000000..1d09bdb894
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.endpoints.yaml
@@ -0,0 +1,24 @@
+- clusterName: weighted-protocol-route-dest/backend/0
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 10.0.0.1
+ portValue: 8080
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: weighted-protocol-route-dest/backend/0
+- clusterName: weighted-protocol-route-dest/backend/1
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 10.0.0.2
+ portValue: 8080
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: weighted-protocol-route-dest/backend/1
diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.listeners.yaml
new file mode 100644
index 0000000000..c2549418db
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.listeners.yaml
@@ -0,0 +1,35 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: weighted-protocol-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: weighted-protocol-listener
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: weighted-protocol-listener
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.routes.yaml
new file mode 100644
index 0000000000..9c65376470
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/mixed-websocket-and-h2c-backends.routes.yaml
@@ -0,0 +1,20 @@
+- ignorePortInHostMatching: true
+ name: weighted-protocol-listener
+ virtualHosts:
+ - domains:
+ - '*'
+ name: weighted-protocol-listener/*
+ routes:
+ - match:
+ prefix: /
+ name: weighted-protocol-route
+ route:
+ clusterNotFoundResponseCode: INTERNAL_SERVER_ERROR
+ upgradeConfigs:
+ - upgradeType: websocket
+ weightedClusters:
+ clusters:
+ - name: weighted-protocol-route-dest/backend/0
+ weight: 1
+ - name: weighted-protocol-route-dest/backend/1
+ weight: 1
diff --git a/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.clusters.yaml
new file mode 100644
index 0000000000..4dd451f0a5
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.clusters.yaml
@@ -0,0 +1,113 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: websocket-wss-route-dest
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: websocket-wss-route-dest
+ perConnectionBufferLimitBytes: 32768
+ transportSocket:
+ name: dummy.transport_socket
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+ commonTlsContext: {}
+ transportSocketMatches:
+ - match:
+ name: websocket-wss-route-dest/tls/0
+ name: websocket-wss-route-dest/tls/0
+ transportSocket:
+ name: envoy.transport_sockets.tls
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+ commonTlsContext:
+ combinedValidationContext:
+ defaultValidationContext:
+ matchTypedSubjectAltNames:
+ - matcher:
+ exact: websocket.example.com
+ sanType: DNS
+ validationContextSdsSecretConfig:
+ name: websocket-wss-route/ca
+ sdsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ sni: websocket.example.com
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ explicitHttpConfig:
+ httpProtocolOptions: {}
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: ordinary-tls-http-route-dest
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: ordinary-tls-http-route-dest
+ perConnectionBufferLimitBytes: 32768
+ transportSocket:
+ name: dummy.transport_socket
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+ commonTlsContext: {}
+ transportSocketMatches:
+ - match:
+ name: ordinary-tls-http-route-dest/tls/0
+ name: ordinary-tls-http-route-dest/tls/0
+ transportSocket:
+ name: envoy.transport_sockets.tls
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
+ commonTlsContext:
+ combinedValidationContext:
+ defaultValidationContext:
+ matchTypedSubjectAltNames:
+ - matcher:
+ exact: ordinary.example.com
+ sanType: DNS
+ validationContextSdsSecretConfig:
+ name: ordinary-tls-http-route/ca
+ sdsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ sni: ordinary.example.com
+ type: EDS
+ typedExtensionProtocolOptions:
+ envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
+ '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
+ autoConfig:
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ httpProtocolOptions: {}
diff --git a/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.endpoints.yaml
new file mode 100644
index 0000000000..b4dfc8cfc3
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.endpoints.yaml
@@ -0,0 +1,32 @@
+- clusterName: websocket-wss-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: websocket.example.com
+ portValue: 443
+ loadBalancingWeight: 1
+ metadata:
+ filterMetadata:
+ envoy.transport_socket_match:
+ name: websocket-wss-route-dest/tls/0
+ loadBalancingWeight: 1
+ locality:
+ region: websocket-wss-route-dest/backend/0
+- clusterName: ordinary-tls-http-route-dest
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: ordinary.example.com
+ portValue: 443
+ loadBalancingWeight: 1
+ metadata:
+ filterMetadata:
+ envoy.transport_socket_match:
+ name: ordinary-tls-http-route-dest/tls/0
+ loadBalancingWeight: 1
+ locality:
+ region: ordinary-tls-http-route-dest/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.listeners.yaml
new file mode 100644
index 0000000000..b9ef1b3a77
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.listeners.yaml
@@ -0,0 +1,35 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: websocket-listener
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: websocket-listener
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: websocket-listener
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.routes.yaml
new file mode 100644
index 0000000000..1c2ddeacac
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.routes.yaml
@@ -0,0 +1,21 @@
+- ignorePortInHostMatching: true
+ name: websocket-listener
+ virtualHosts:
+ - domains:
+ - '*'
+ name: websocket-listener/*
+ routes:
+ - match:
+ prefix: /
+ name: websocket-wss-route
+ route:
+ cluster: websocket-wss-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
+ - match:
+ prefix: /
+ name: ordinary-tls-http-route
+ route:
+ cluster: ordinary-tls-http-route-dest
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.secrets.yaml b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.secrets.yaml
new file mode 100644
index 0000000000..4abecf47fc
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/websocket-backend-force-http1-upstream.secrets.yaml
@@ -0,0 +1,8 @@
+- name: websocket-wss-route/ca
+ validationContext:
+ trustedCa:
+ filename: /etc/ssl/certs/ca-certificates.crt
+- name: ordinary-tls-http-route/ca
+ validationContext:
+ trustedCa:
+ filename: /etc/ssl/certs/ca-certificates.crt
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 8fadbeac36..136c98e4e4 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -25,7 +25,11 @@ bug fixes: |
BackendTLSPolicy was ignored when configuring TLS for telemetry backends (access logs, tracing, metrics).
Fixed client certificate secret never delivered when it is exclusively referenced by a SecurityPolicy `extAuth`/`jwt`/`oidc` Backend.
Fixed xRoute status condition when route has mirror filter and the mirror backend has no endpoints.
+ Fixed `ws` and `wss` Backend appProtocols to force HTTP/1.1 upstream connections instead of negotiating HTTP/2, avoiding compatibility issues with WebSocket backends that do not support RFC 8441 extended CONNECT.
Fixed gateway-helm RBAC in GatewayNamespace mode with explicit `watch.namespaces` list by adding controller-namespace secret read permissions to infra-manager.
+ Fixed a control plane panic caused by concurrent Status mutation racing with the watchable Map coalesce goroutine.
+ Fixed status conditions not being updated when a route is rejected due to multiple errors.
+ Fixed active HTTP health checks to use Backend endpoint hostnames before falling back to the effective Route hostname.
# Enhancements that improve performance.
performance improvements: |
diff --git a/release-notes/v1.7.3.yaml b/release-notes/v1.7.3.yaml
new file mode 100644
index 0000000000..25b9897286
--- /dev/null
+++ b/release-notes/v1.7.3.yaml
@@ -0,0 +1,30 @@
+date: May 8, 2026
+
+# Changes that are expected to cause an incompatibility with previous versions, such as deletions or modifications to existing APIs.
+breaking changes: |
+ Bumped `google.golang.org/grpc` to v1.79.3 to address CVE-2026-33186 (Critical, gRPC-Go authorization bypass via non-canonical HTTP/2 `:path` header).
+ Bumped `go.opentelemetry.io/otel/sdk` to v1.40.0 to address CVE-2026-24051 (High, OpenTelemetry Go SDK path hijacking on macOS/Darwin).
+
+# Updates addressing vulnerabilities, security flaws, or compliance requirements.
+security updates: |
+
+# New features or capabilities added in this release.
+new features: |
+
+
+bug fixes: |
+ Fixed a control plane panic caused by concurrent Status mutation racing with the watchable Map coalesce goroutine.
+ Fixed `ws` and `wss` Backend appProtocols to force HTTP/1.1 upstream connections instead of negotiating HTTP/2, avoiding compatibility issues with WebSocket backends that do not support RFC 8441 extended CONNECT.
+ Fixed status conditions not being updated when a route is rejected due to multiple errors.
+ Fixed benchmark JSON report emitting `0` for p99 and p999 percentiles by using the nearest Nighthawk histogram percentiles.
+ Fixed active HTTP health checks to use Backend endpoint hostnames before falling back to the effective Route hostname.
+
+# Enhancements that improve performance.
+performance improvements: |
+
+
+# Deprecated features or APIs.
+deprecations: |
+
+# Other notable changes not covered by the above sections.
+Other changes: |
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 0fa44d9347..25734c889f 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -2426,7 +2426,7 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
-| `hostname` | _string_ | false | | Hostname defines the HTTP host that will be requested during health checking.
Default: HTTPRoute or GRPCRoute hostname. |
+| `hostname` | _string_ | false | | Hostname defines the HTTP Host header used for active HTTP health checks.
Host selection uses this order: this field, the associated Backend endpoint
hostname if available, then the effective Route hostname. |
| `path` | _string_ | true | | Path defines the HTTP path that will be requested during health checking. |
| `method` | _string_ | false | | Method defines the HTTP method used for health checking.
Defaults to GET |
| `expectedStatuses` | _[HTTPStatus](#httpstatus) array_ | false | | ExpectedStatuses defines a list of HTTP response statuses considered healthy.
Defaults to 200 only |
diff --git a/test/benchmark/suite/json.go b/test/benchmark/suite/json.go
index a46e605565..3e25f80d2e 100644
--- a/test/benchmark/suite/json.go
+++ b/test/benchmark/suite/json.go
@@ -10,6 +10,8 @@ package suite
import (
"encoding/json"
"math"
+
+ "github.com/envoyproxy/gateway/test/benchmark/proto"
)
func ToJSON(report *BenchmarkSuiteReport) []byte {
@@ -53,25 +55,7 @@ func convertCaseReport(report *BenchmarkCaseReport) *JSONTestResult {
latency.Max = stat.GetMax().AsDuration().Seconds() * 1000
latency.Min = stat.GetMin().AsDuration().Seconds() * 1000
latency.Mean = stat.GetMean().AsDuration().Seconds() * 1000
- latency.Percentiles = Percentiles{}
- for _, p := range stat.Percentiles {
- switch p.Percentile {
- case 0.5:
- latency.Percentiles.P50 = p.GetDuration().AsDuration().Seconds() * 1000
- case 0.75:
- latency.Percentiles.P75 = p.GetDuration().AsDuration().Seconds() * 1000
- case 0.8:
- latency.Percentiles.P80 = p.GetDuration().AsDuration().Seconds() * 1000
- case 0.9:
- latency.Percentiles.P90 = p.GetDuration().AsDuration().Seconds() * 1000
- case 0.95:
- latency.Percentiles.P95 = p.GetDuration().AsDuration().Seconds() * 1000
- case 0.99:
- latency.Percentiles.P99 = p.GetDuration().AsDuration().Seconds() * 1000
- case 0.999:
- latency.Percentiles.P999 = p.GetDuration().AsDuration().Seconds() * 1000
- }
- }
+ latency.Percentiles = getLatencyPercentiles(stat.Percentiles)
}
}
@@ -158,3 +142,28 @@ func getResourceUsage(metrics []float64) *ResourceUsage {
Max: maxVal,
}
}
+
+func getLatencyPercentiles(percentiles []*proto.Percentile) Percentiles {
+ return Percentiles{
+ P50: getPercentileDurationMs(percentiles, 0.5),
+ P75: getPercentileDurationMs(percentiles, 0.75),
+ P80: getPercentileDurationMs(percentiles, 0.8),
+ P90: getPercentileDurationMs(percentiles, 0.9),
+ P95: getPercentileDurationMs(percentiles, 0.95),
+ P99: getPercentileDurationMs(percentiles, 0.99),
+ P999: getPercentileDurationMs(percentiles, 0.999),
+ }
+}
+
+func getPercentileDurationMs(percentiles []*proto.Percentile, target float64) float64 {
+ lastPercentile := float64(-1)
+ for _, percentile := range percentiles {
+ current := percentile.GetPercentile()
+ if current >= target && lastPercentile < current {
+ return percentile.GetDuration().AsDuration().Seconds() * 1000
+ }
+ lastPercentile = current
+ }
+
+ return 0
+}
diff --git a/test/benchmark/suite/json_test.go b/test/benchmark/suite/json_test.go
new file mode 100644
index 0000000000..7a63720710
--- /dev/null
+++ b/test/benchmark/suite/json_test.go
@@ -0,0 +1,30 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+//go:build benchmark
+
+package suite
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetLatencyPercentiles(t *testing.T) {
+ result := fakeCaseResult()
+ require.NotNil(t, result)
+ require.NotEmpty(t, result.Statistics)
+
+ percentiles := getLatencyPercentiles(result.Statistics[1].Percentiles)
+
+ require.InDelta(t, 0.438127, percentiles.P50, 1e-9)
+ require.InDelta(t, 0.546047, percentiles.P75, 1e-9)
+ require.InDelta(t, 0.584511, percentiles.P80, 1e-9)
+ require.InDelta(t, 0.751775, percentiles.P90, 1e-9)
+ require.InDelta(t, 1.100415, percentiles.P95, 1e-9)
+ require.InDelta(t, 55.601151, percentiles.P99, 1e-9)
+ require.InDelta(t, 60.461055, percentiles.P999, 1e-9)
+}
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index 02472b4be5..7a23fd9718 100644
--- a/test/helm/gateway-crds-helm/all.out.yaml
+++ b/test/helm/gateway-crds-helm/all.out.yaml
@@ -21573,8 +21573,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -26515,8 +26516,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -40237,8 +40239,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -41489,8 +41492,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -42911,8 +42915,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -44238,8 +44243,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -46912,8 +46918,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -48052,8 +48059,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -49393,8 +49401,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -50812,8 +50821,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml
index eada234bb8..737b27dcc6 100644
--- a/test/helm/gateway-crds-helm/e2e.out.yaml
+++ b/test/helm/gateway-crds-helm/e2e.out.yaml
@@ -926,8 +926,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -6073,8 +6074,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -20102,8 +20104,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -21430,8 +21433,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -22934,8 +22938,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -24341,8 +24346,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -27185,8 +27191,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -28398,8 +28405,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -29812,8 +29820,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -31310,8 +31319,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
index a3ddf7d7ec..98f7c14138 100644
--- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
+++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
@@ -903,8 +903,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -5845,8 +5846,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -19567,8 +19569,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -20819,8 +20822,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -22241,8 +22245,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -23568,8 +23573,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -26242,8 +26248,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -27382,8 +27389,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -28723,8 +28731,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
@@ -30142,8 +30151,9 @@ spec:
type: array
hostname:
description: |-
- Hostname defines the HTTP host that will be requested during health checking.
- Default: HTTPRoute or GRPCRoute hostname.
+ Hostname defines the HTTP Host header used for active HTTP health checks.
+ Host selection uses this order: this field, the associated Backend endpoint
+ hostname if available, then the effective Route hostname.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$