diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 8137ce1ae7..026239b6a0 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -1713,25 +1713,27 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, o ds = translateDNS(&policy.Spec.ClusterSettings, utils.NamespacedName(policy).String()) return &ir.TrafficFeatures{ - RateLimit: rl, - BandwidthLimit: bl, - LoadBalancer: lb, - ProxyProtocol: pp, - HealthCheck: hc, - CircuitBreaker: cb, - FaultInjection: fi, - AdmissionControl: ac, - TCPKeepalive: ka, - Retry: rt, - BackendConnection: bc, - HTTP2: h2, - DNS: ds, - Timeout: to, - ResponseOverride: ro, - RequestBuffer: rb, - Compression: cp, - HTTPUpgrade: httpUpgrade, - Telemetry: buildBackendTelemetry(policy.Spec.Telemetry), + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: lb, + ProxyProtocol: pp, + HealthCheck: hc, + AdmissionControl: ac, + CircuitBreaker: cb, + Timeout: to, + TCPKeepalive: ka, + BackendConnection: bc, + HTTP2: h2, + DNS: ds, + }, + RateLimit: rl, + BandwidthLimit: bl, + FaultInjection: fi, + Retry: rt, + ResponseOverride: ro, + Compression: cp, + HTTPUpgrade: httpUpgrade, + Telemetry: buildBackendTelemetry(policy.Spec.Telemetry), + RequestBuffer: rb, }, errs } @@ -1916,7 +1918,10 @@ func (t *Translator) translateBackendTrafficPolicyForListeners( // so those routes stay on this shared cluster and incorrectly inherit it anyway. if applyToBackendClusters && errs == nil { for _, bc := range x.BackendClusters { - bc.Traffic = tf.DeepCopy() + bc.Traffic = tf.ClusterTrafficFeatures.DeepCopy() + // Drop the route-scoped timeout members: they are never read from a cluster, and a + // merged cluster must not advertise settings it cannot honor. + bc.Traffic.Timeout = tf.Timeout.ClusterOnly().AsTimeout() bc.UseClientProtocol = policy.Spec.UseClientProtocol } } diff --git a/internal/gatewayapi/clustersettings.go b/internal/gatewayapi/clustersettings.go index d442c2414d..692862f164 100644 --- a/internal/gatewayapi/clustersettings.go +++ b/internal/gatewayapi/clustersettings.go @@ -164,11 +164,13 @@ func buildClusterSettingsTimeout(policy *egv1a1.ClusterSettings) (*ir.Timeout, e } to.HTTP = &ir.HTTPTimeout{ - ConnectionIdleTimeout: cit, - MaxConnectionDuration: mcd, - RequestTimeout: rt, - MaxStreamDuration: msd, - StreamIdleTimeout: sit, + ClusterHTTPTimeout: ir.ClusterHTTPTimeout{ + ConnectionIdleTimeout: cit, + MaxConnectionDuration: mcd, + MaxStreamDuration: msd, + }, + RequestTimeout: rt, + StreamIdleTimeout: sit, } } return to, errs diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 0cf1778775..195c194f41 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -1135,32 +1135,27 @@ type Compression struct { MinContentLength *uint32 `json:"minContentLength,omitempty" yaml:"minContentLength,omitempty"` } -// TrafficFeatures holds the information associated with the Backend Traffic Policy. +// ClusterTrafficFeatures holds the TrafficFeatures fields that translate to Envoy cluster (CDS) +// configuration. Route- and HCM-scoped features live on TrafficFeatures instead. // +k8s:deepcopy-gen=true -type TrafficFeatures struct { - // RateLimit defines the more specific match conditions as well as limits for ratelimiting - // the requests on this route. - RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` - // BandwidthLimit defines bandwidth limiting for the backend. - BandwidthLimit *BandwidthLimit `json:"bandwidthLimit,omitempty" yaml:"bandwidthLimit,omitempty"` +type ClusterTrafficFeatures struct { // load balancer policy to use when routing to the backend endpoints. LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"` // Proxy Protocol Settings ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty"` // HealthCheck defines the configuration for health checking on the upstream. HealthCheck *HealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"` - // FaultInjection defines the schema for injecting faults into HTTP requests. - FaultInjection *FaultInjection `json:"faultInjection,omitempty" yaml:"faultInjection,omitempty"` // AdmissionControl defines the schema for admission control based on success rate. AdmissionControl *AdmissionControl `json:"admissionControl,omitempty" yaml:"admissionControl,omitempty"` // Circuit Breaker Settings CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty" yaml:"circuitBreaker,omitempty"` - // Request and connection timeout settings + // Request and connection timeout settings. Holds the full Timeout rather than ClusterTimeout + // because TrafficFeatures inlines this struct, and its route and filter paths read the + // route-scoped members through the promoted field. Cluster translation takes + // Timeout.ClusterOnly(), which is what keeps those members out of CDS. Timeout *Timeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TcpKeepalive settings associated with the upstream client connection. TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` - // Retry settings - Retry *Retry `json:"retry,omitempty" yaml:"retry,omitempty"` // settings of upstream connection BackendConnection *BackendConnection `json:"backendConnection,omitempty" yaml:"backendConnection,omitempty"` // HTTP2 provides HTTP/2 configuration for clusters @@ -1168,6 +1163,23 @@ type TrafficFeatures struct { HTTP2 *HTTP2Settings `json:"http2,omitempty" yaml:"http2,omitempty"` // DNS is used to configure how DNS resolution is handled by the Envoy Proxy cluster DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"` +} + +// TrafficFeatures holds the information associated with the Backend Traffic Policy. +// +k8s:deepcopy-gen=true +type TrafficFeatures struct { + // ClusterTrafficFeatures holds the cluster (CDS) scoped fields. Inlined, so serialization and + // promoted field access (e.g. tf.CircuitBreaker) are unchanged. + ClusterTrafficFeatures `json:",inline" yaml:",inline"` + // RateLimit defines the more specific match conditions as well as limits for ratelimiting + // the requests on this route. + RateLimit *RateLimit `json:"rateLimit,omitempty" yaml:"rateLimit,omitempty"` + // BandwidthLimit defines bandwidth limiting for the backend. + BandwidthLimit *BandwidthLimit `json:"bandwidthLimit,omitempty" yaml:"bandwidthLimit,omitempty"` + // FaultInjection defines the schema for injecting faults into HTTP requests. + FaultInjection *FaultInjection `json:"faultInjection,omitempty" yaml:"faultInjection,omitempty"` + // Retry settings + Retry *Retry `json:"retry,omitempty" yaml:"retry,omitempty"` // ResponseOverride defines the schema for overriding the response. ResponseOverride *ResponseOverride `json:"responseOverride,omitempty" yaml:"responseOverride,omitempty"` // Compression settings for HTTP Response @@ -1180,6 +1192,16 @@ type TrafficFeatures struct { RequestBuffer *RequestBuffer `json:"requestBuffer,omitempty" yaml:"requestBuffer,omitempty"` } +// ClusterFeatures returns the cluster-scoped subset of these traffic features, or nil if there are +// none. Nil-safe, so callers holding a possibly-nil *TrafficFeatures can pass the result straight +// to the cluster translation path. +func (b *TrafficFeatures) ClusterFeatures() *ClusterTrafficFeatures { + if b == nil { + return nil + } + return &b.ClusterTrafficFeatures +} + // BackendTelemetry defines the telemetry configuration for the backend. // +k8s:deepcopy-gen=true type BackendTelemetry struct { @@ -2155,9 +2177,10 @@ type BackendCluster struct { Setting *DestinationSetting `json:"setting,omitempty" yaml:"setting,omitempty"` // Metadata describes the backend resource (Service, Backend, etc.) Metadata *ResourceMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` - // Traffic holds the accepted whole-gateway BackendTrafficPolicy's settings, if any - - // gateway level is the only one guaranteed uniform across a merged cluster's routes. - Traffic *TrafficFeatures `json:"traffic,omitempty" yaml:"traffic,omitempty"` + // Traffic holds the cluster-scoped settings from the accepted whole-gateway + // BackendTrafficPolicy, if any - gateway level is the only one guaranteed uniform across a + // merged cluster's routes. + Traffic *ClusterTrafficFeatures `json:"traffic,omitempty" yaml:"traffic,omitempty"` // UseClientProtocol holds the accepted whole-gateway BackendTrafficPolicy's UseClientProtocol, // if any - same gateway-level-only reasoning as Traffic. UseClientProtocol *bool `json:"useClientProtocol,omitempty" yaml:"useClientProtocol,omitempty"` @@ -3663,11 +3686,46 @@ type TCPTimeout struct { ConnectTimeout *metav1.Duration `json:"connectTimeout,omitempty" yaml:"connectTimeout,omitempty"` } +// ClusterTimeout holds the Timeout members that translate to Envoy cluster (CDS) configuration. // +k8s:deepcopy-gen=true -type HTTPTimeout struct { - // RequestTimeout is the time until which entire response is received from the upstream. - RequestTimeout *metav1.Duration `json:"requestTimeout,omitempty" yaml:"requestTimeout,omitempty"` +type ClusterTimeout struct { + // Timeout settings for TCP. + TCP *TCPTimeout `json:"tcp,omitempty" yaml:"tcp,omitempty"` + + // Timeout settings for HTTP. + HTTP *ClusterHTTPTimeout `json:"http,omitempty" yaml:"http,omitempty"` +} + +// AsTimeout widens t back to a Timeout, for the IR fields that hold the full type. +func (t *ClusterTimeout) AsTimeout() *Timeout { + if t == nil { + return nil + } + out := &Timeout{TCP: t.TCP.DeepCopy()} + if t.HTTP != nil { + out.HTTP = &HTTPTimeout{ClusterHTTPTimeout: *t.HTTP.DeepCopy()} + } + return out +} +// ClusterOnly returns the cluster-scoped subset of t, or nil. It is built from ClusterHTTPTimeout +// rather than by clearing the route-scoped members, so a member added to HTTPTimeout stays out of +// cluster configuration unless it is added to ClusterHTTPTimeout deliberately. +func (t *Timeout) ClusterOnly() *ClusterTimeout { + if t == nil { + return nil + } + out := &ClusterTimeout{TCP: t.TCP.DeepCopy()} + if t.HTTP != nil { + out.HTTP = t.HTTP.ClusterHTTPTimeout.DeepCopy() + } + return out +} + +// ClusterHTTPTimeout holds the HTTPTimeout members that translate to Envoy cluster (CDS) +// configuration. +// +k8s:deepcopy-gen=true +type ClusterHTTPTimeout struct { // The idle timeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection. ConnectionIdleTimeout *metav1.Duration `json:"connectionIdleTimeout,omitempty" yaml:"connectionIdleTimeout,omitempty"` @@ -3676,6 +3734,16 @@ type HTTPTimeout struct { // The maximum duration of an HTTP stream. MaxStreamDuration *metav1.Duration `json:"maxStreamDuration,omitempty" yaml:"maxStreamDuration,omitempty"` +} + +// +k8s:deepcopy-gen=true +type HTTPTimeout struct { + // ClusterHTTPTimeout holds the members that translate to cluster (CDS) configuration. Inlined, + // so serialization and promoted field access (e.g. to.MaxStreamDuration) are unchanged. + ClusterHTTPTimeout `json:",inline" yaml:",inline"` + + // RequestTimeout is the time until which entire response is received from the upstream. + RequestTimeout *metav1.Duration `json:"requestTimeout,omitempty" yaml:"requestTimeout,omitempty"` // The stream idle timeout defines the amount of time a stream can exist without any upstream or downstream activity. // If not specified, StreamIdleTimeout is inherited from the listener-level setting. diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index f71396097a..299871cc51 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -461,7 +461,7 @@ func (in *BackendCluster) DeepCopyInto(out *BackendCluster) { } if in.Traffic != nil { in, out := &in.Traffic, &out.Traffic - *out = new(TrafficFeatures) + *out = new(ClusterTrafficFeatures) (*in).DeepCopyInto(*out) } if in.UseClientProtocol != nil { @@ -1005,6 +1005,126 @@ func (in *ClientTimeout) DeepCopy() *ClientTimeout { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterHTTPTimeout) DeepCopyInto(out *ClusterHTTPTimeout) { + *out = *in + if in.ConnectionIdleTimeout != nil { + in, out := &in.ConnectionIdleTimeout, &out.ConnectionIdleTimeout + *out = new(metav1.Duration) + **out = **in + } + if in.MaxConnectionDuration != nil { + in, out := &in.MaxConnectionDuration, &out.MaxConnectionDuration + *out = new(metav1.Duration) + **out = **in + } + if in.MaxStreamDuration != nil { + in, out := &in.MaxStreamDuration, &out.MaxStreamDuration + *out = new(metav1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterHTTPTimeout. +func (in *ClusterHTTPTimeout) DeepCopy() *ClusterHTTPTimeout { + if in == nil { + return nil + } + out := new(ClusterHTTPTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTimeout) DeepCopyInto(out *ClusterTimeout) { + *out = *in + if in.TCP != nil { + in, out := &in.TCP, &out.TCP + *out = new(TCPTimeout) + (*in).DeepCopyInto(*out) + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + *out = new(ClusterHTTPTimeout) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTimeout. +func (in *ClusterTimeout) DeepCopy() *ClusterTimeout { + if in == nil { + return nil + } + out := new(ClusterTimeout) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTrafficFeatures) DeepCopyInto(out *ClusterTrafficFeatures) { + *out = *in + if in.LoadBalancer != nil { + in, out := &in.LoadBalancer, &out.LoadBalancer + *out = new(LoadBalancer) + (*in).DeepCopyInto(*out) + } + if in.ProxyProtocol != nil { + in, out := &in.ProxyProtocol, &out.ProxyProtocol + *out = new(ProxyProtocol) + **out = **in + } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(HealthCheck) + (*in).DeepCopyInto(*out) + } + if in.AdmissionControl != nil { + in, out := &in.AdmissionControl, &out.AdmissionControl + *out = new(AdmissionControl) + (*in).DeepCopyInto(*out) + } + if in.CircuitBreaker != nil { + in, out := &in.CircuitBreaker, &out.CircuitBreaker + *out = new(CircuitBreaker) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(Timeout) + (*in).DeepCopyInto(*out) + } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } + if in.BackendConnection != nil { + in, out := &in.BackendConnection, &out.BackendConnection + *out = new(BackendConnection) + (*in).DeepCopyInto(*out) + } + if in.HTTP2 != nil { + in, out := &in.HTTP2, &out.HTTP2 + *out = new(HTTP2Settings) + (*in).DeepCopyInto(*out) + } + if in.DNS != nil { + in, out := &in.DNS, &out.DNS + *out = new(DNS) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTrafficFeatures. +func (in *ClusterTrafficFeatures) DeepCopy() *ClusterTrafficFeatures { + if in == nil { + return nil + } + out := new(ClusterTrafficFeatures) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Compression) DeepCopyInto(out *Compression) { *out = *in @@ -2717,26 +2837,12 @@ func (in *HTTPSuccessCriteria) DeepCopy() *HTTPSuccessCriteria { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPTimeout) DeepCopyInto(out *HTTPTimeout) { *out = *in + in.ClusterHTTPTimeout.DeepCopyInto(&out.ClusterHTTPTimeout) if in.RequestTimeout != nil { in, out := &in.RequestTimeout, &out.RequestTimeout *out = new(metav1.Duration) **out = **in } - if in.ConnectionIdleTimeout != nil { - in, out := &in.ConnectionIdleTimeout, &out.ConnectionIdleTimeout - *out = new(metav1.Duration) - **out = **in - } - if in.MaxConnectionDuration != nil { - in, out := &in.MaxConnectionDuration, &out.MaxConnectionDuration - *out = new(metav1.Duration) - **out = **in - } - if in.MaxStreamDuration != nil { - in, out := &in.MaxStreamDuration, &out.MaxStreamDuration - *out = new(metav1.Duration) - **out = **in - } if in.StreamIdleTimeout != nil { in, out := &in.StreamIdleTimeout, &out.StreamIdleTimeout *out = new(metav1.Duration) @@ -5266,6 +5372,7 @@ func (in *Tracing) DeepCopy() *Tracing { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) { *out = *in + in.ClusterTrafficFeatures.DeepCopyInto(&out.ClusterTrafficFeatures) if in.RateLimit != nil { in, out := &in.RateLimit, &out.RateLimit *out = new(RateLimit) @@ -5276,66 +5383,16 @@ func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) { *out = new(BandwidthLimit) (*in).DeepCopyInto(*out) } - if in.LoadBalancer != nil { - in, out := &in.LoadBalancer, &out.LoadBalancer - *out = new(LoadBalancer) - (*in).DeepCopyInto(*out) - } - if in.ProxyProtocol != nil { - in, out := &in.ProxyProtocol, &out.ProxyProtocol - *out = new(ProxyProtocol) - **out = **in - } - if in.HealthCheck != nil { - in, out := &in.HealthCheck, &out.HealthCheck - *out = new(HealthCheck) - (*in).DeepCopyInto(*out) - } if in.FaultInjection != nil { in, out := &in.FaultInjection, &out.FaultInjection *out = new(FaultInjection) (*in).DeepCopyInto(*out) } - if in.AdmissionControl != nil { - in, out := &in.AdmissionControl, &out.AdmissionControl - *out = new(AdmissionControl) - (*in).DeepCopyInto(*out) - } - if in.CircuitBreaker != nil { - in, out := &in.CircuitBreaker, &out.CircuitBreaker - *out = new(CircuitBreaker) - (*in).DeepCopyInto(*out) - } - if in.Timeout != nil { - in, out := &in.Timeout, &out.Timeout - *out = new(Timeout) - (*in).DeepCopyInto(*out) - } - if in.TCPKeepalive != nil { - in, out := &in.TCPKeepalive, &out.TCPKeepalive - *out = new(TCPKeepalive) - (*in).DeepCopyInto(*out) - } if in.Retry != nil { in, out := &in.Retry, &out.Retry *out = new(Retry) (*in).DeepCopyInto(*out) } - if in.BackendConnection != nil { - in, out := &in.BackendConnection, &out.BackendConnection - *out = new(BackendConnection) - (*in).DeepCopyInto(*out) - } - if in.HTTP2 != nil { - in, out := &in.HTTP2, &out.HTTP2 - *out = new(HTTP2Settings) - (*in).DeepCopyInto(*out) - } - if in.DNS != nil { - in, out := &in.DNS, &out.DNS - *out = new(DNS) - (*in).DeepCopyInto(*out) - } if in.ResponseOverride != nil { in, out := &in.ResponseOverride, &out.ResponseOverride *out = new(ResponseOverride) diff --git a/internal/xds/translator/accesslog.go b/internal/xds/translator/accesslog.go index 02c3684a3f..e177b9a51f 100644 --- a/internal/xds/translator/accesslog.go +++ b/internal/xds/translator/accesslog.go @@ -536,7 +536,7 @@ func processClusterForAccessLog(tCtx *types.ResourceVersionTable, al *ir.AccessL endpointType: buildEndpointType(als.Destination.Settings), metadata: als.Destination.Metadata, } - applyTraffic(args, als.Traffic) + applyTraffic(args, als.Traffic.ClusterFeatures()) if err := addXdsCluster(tCtx, args); err != nil { return err @@ -553,7 +553,7 @@ func processClusterForAccessLog(tCtx *types.ResourceVersionTable, al *ir.AccessL metrics: metrics, metadata: otel.Destination.Metadata, } - applyTraffic(args, otel.Traffic) + applyTraffic(args, otel.Traffic.ClusterFeatures()) if err := addXdsCluster(tCtx, args); err != nil { return err } diff --git a/internal/xds/translator/cluster.go b/internal/xds/translator/cluster.go index 35ad2f88c9..6e9b36178c 100644 --- a/internal/xds/translator/cluster.go +++ b/internal/xds/translator/cluster.go @@ -79,7 +79,7 @@ type xdsClusterArgs struct { routeHostname string http1Settings *ir.HTTP1Settings http2Settings *ir.HTTP2Settings - timeout *ir.Timeout + timeout *ir.ClusterTimeout tcpkeepalive *ir.TCPKeepalive metrics *ir.Metrics backendConnection *ir.BackendConnection @@ -1327,7 +1327,7 @@ func buildProxyProtocolSocket(proxyProtocol *ir.ProxyProtocol, tSocket *corev3.T } } -func buildConnectTimeout(to *ir.Timeout) *durationpb.Duration { +func buildConnectTimeout(to *ir.ClusterTimeout) *durationpb.Duration { if to != nil && to.TCP != nil && to.TCP.ConnectTimeout != nil { return durationpb.New(to.TCP.ConnectTimeout.Duration) } @@ -1425,7 +1425,7 @@ type ExtraArgs struct { extensionMgr *extensionTypes.Manager unstructuredRefs []*unstructured.Unstructured logger logging.Logger - traffic *ir.TrafficFeatures + traffic *ir.ClusterTrafficFeatures useClientProtocol *bool } @@ -1472,7 +1472,7 @@ func (route *TCPRouteTranslator) asClusterArgs(name string, circuitBreaker: route.CircuitBreaker, tcpkeepalive: route.TCPKeepalive, healthCheck: route.HealthCheck, - timeout: route.Timeout, + timeout: route.Timeout.ClusterOnly(), endpointType: buildEndpointType(settings), metrics: extra.metrics, backendConnection: route.BackendConnection, @@ -1512,7 +1512,7 @@ func (httpRoute *HTTPRouteTranslator) asClusterArgs(name string, } // Populate traffic features. - applyTraffic(clusterArgs, httpRoute.Traffic) + applyTraffic(clusterArgs, httpRoute.Traffic.ClusterFeatures()) return clusterArgs } diff --git a/internal/xds/translator/cluster_test.go b/internal/xds/translator/cluster_test.go index 47420de63e..22ea7d86b8 100644 --- a/internal/xds/translator/cluster_test.go +++ b/internal/xds/translator/cluster_test.go @@ -661,7 +661,7 @@ func TestGetHealthCheckOverridesHostname(t *testing.T) { func TestBackendClusterTranslatorAsClusterArgsAppliesTraffic(t *testing.T) { circuitBreaker := &ir.CircuitBreaker{} - traffic := &ir.TrafficFeatures{CircuitBreaker: circuitBreaker} + traffic := &ir.ClusterTrafficFeatures{CircuitBreaker: circuitBreaker} args := BackendClusterTranslator{}.asClusterArgs("backend-1", nil, &ExtraArgs{traffic: traffic}, nil) diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go index fba5f7da53..e191c30a29 100644 --- a/internal/xds/translator/oidc.go +++ b/internal/xds/translator/oidc.go @@ -500,7 +500,7 @@ func createOAuth2TokenEndpointCluster(tCtx *types.ResourceVersionTable, } // Apply traffic features if they exist. - applyTraffic(clusterArgs, oidc.Provider.Traffic) + applyTraffic(clusterArgs, oidc.Provider.Traffic.ClusterFeatures()) return addXdsCluster(tCtx, clusterArgs) } diff --git a/internal/xds/translator/route_test.go b/internal/xds/translator/route_test.go index a0df2fde99..5d55af5bdf 100644 --- a/internal/xds/translator/route_test.go +++ b/internal/xds/translator/route_test.go @@ -90,7 +90,9 @@ func TestBuildHashPolicy(t *testing.T) { name: "Nil ConsistentHash in LoadBalancer", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{}, + }, }, }, want: nil, @@ -99,7 +101,9 @@ func TestBuildHashPolicy(t *testing.T) { name: "ConsistentHash with nil SourceIP and Header", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{}}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{}}, + }, }, }, want: nil, @@ -108,7 +112,9 @@ func TestBuildHashPolicy(t *testing.T) { name: "ConsistentHash with SourceIP set to false", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: new(false)}}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: new(false)}}, + }, }, }, want: nil, @@ -117,7 +123,9 @@ func TestBuildHashPolicy(t *testing.T) { name: "ConsistentHash with SourceIP set to true", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: new(true)}}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{SourceIP: new(true)}}, + }, }, }, want: []*routev3.RouteAction_HashPolicy{ @@ -134,7 +142,9 @@ func TestBuildHashPolicy(t *testing.T) { name: "ConsistentHash with Header", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Headers: []*egv1a1.Header{{Name: "name"}}}}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Headers: []*egv1a1.Header{{Name: "name"}}}}, + }, }, }, want: []*routev3.RouteAction_HashPolicy{ @@ -151,11 +161,13 @@ func TestBuildHashPolicy(t *testing.T) { name: "ConsistentHash with multiple Headers", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Headers: []*egv1a1.Header{ - {Name: "name"}, - {Name: "bazz"}, - {Name: "buzz"}, - }}}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{Headers: []*egv1a1.Header{ + {Name: "name"}, + {Name: "bazz"}, + {Name: "buzz"}, + }}}, + }, }, }, want: []*routev3.RouteAction_HashPolicy{ @@ -186,11 +198,13 @@ func TestBuildHashPolicy(t *testing.T) { name: "ConsistentHash with multiple QueryParams", httpRoute: &ir.HTTPRoute{ Traffic: &ir.TrafficFeatures{ - LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{QueryParams: []*egv1a1.QueryParam{ - {Name: "name"}, - {Name: "bazz"}, - {Name: "buzz"}, - }}}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + LoadBalancer: &ir.LoadBalancer{ConsistentHash: &ir.ConsistentHash{QueryParams: []*egv1a1.QueryParam{ + {Name: "name"}, + {Name: "bazz"}, + {Name: "buzz"}, + }}}, + }, }, }, want: []*routev3.RouteAction_HashPolicy{ diff --git a/internal/xds/translator/tracing.go b/internal/xds/translator/tracing.go index 2473729a1c..fe7deb2075 100644 --- a/internal/xds/translator/tracing.go +++ b/internal/xds/translator/tracing.go @@ -171,7 +171,7 @@ func processClusterForTracing(tCtx *types.ResourceVersionTable, tracing *ir.Trac metadata: tracing.Destination.Metadata, } - applyTraffic(args, tracing.Traffic) + applyTraffic(args, tracing.Traffic.ClusterFeatures()) return addXdsCluster(tCtx, args) } diff --git a/internal/xds/translator/utils.go b/internal/xds/translator/utils.go index e2cdc3c88f..52797d22b9 100644 --- a/internal/xds/translator/utils.go +++ b/internal/xds/translator/utils.go @@ -138,10 +138,6 @@ func createExtServiceXDSCluster(rd *ir.RouteDestination, traffic *ir.TrafficFeat tSocket *corev3.TransportSocket ) - // Make sure that there are safe defaults for the traffic - if traffic == nil { - traffic = &ir.TrafficFeatures{} - } // Get the address type from the first setting. // This is safe because no mixed address types in the settings. addrTypeState := rd.Settings[0].AddressType @@ -159,7 +155,7 @@ func createExtServiceXDSCluster(rd *ir.RouteDestination, traffic *ir.TrafficFeat metadata: rd.Metadata, } - applyTraffic(args, traffic) + applyTraffic(args, traffic.ClusterFeatures()) return addXdsCluster(tCtx, args) } @@ -199,12 +195,13 @@ func addClusterFromURL(url string, traffic *ir.TrafficFeatures, tCtx *types.Reso clusterArgs.tSocket = tSocket } - applyTraffic(clusterArgs, traffic) + applyTraffic(clusterArgs, traffic.ClusterFeatures()) return addXdsCluster(tCtx, clusterArgs) } -func applyTraffic(args *xdsClusterArgs, traffic *ir.TrafficFeatures) { +// applyTraffic copies the cluster-scoped traffic features onto the cluster args. +func applyTraffic(args *xdsClusterArgs, traffic *ir.ClusterTrafficFeatures) { if traffic == nil { return } @@ -212,7 +209,7 @@ func applyTraffic(args *xdsClusterArgs, traffic *ir.TrafficFeatures) { args.proxyProtocol = traffic.ProxyProtocol args.circuitBreaker = traffic.CircuitBreaker args.healthCheck = traffic.HealthCheck - args.timeout = traffic.Timeout + args.timeout = traffic.Timeout.ClusterOnly() args.tcpkeepalive = traffic.TCPKeepalive args.backendConnection = traffic.BackendConnection args.dns = traffic.DNS diff --git a/internal/xds/translator/utils_test.go b/internal/xds/translator/utils_test.go index 21aee3d5e4..db09b8dea4 100644 --- a/internal/xds/translator/utils_test.go +++ b/internal/xds/translator/utils_test.go @@ -118,9 +118,11 @@ func TestDetermineIPFamily(t *testing.T) { func TestAddClusterFromURLWithTraffic(t *testing.T) { tCtx := &types.ResourceVersionTable{} traffic := &ir.TrafficFeatures{ - Timeout: &ir.Timeout{ - TCP: &ir.TCPTimeout{ - ConnectTimeout: &metav1.Duration{Duration: 2 * time.Second}, + ClusterTrafficFeatures: ir.ClusterTrafficFeatures{ + Timeout: &ir.Timeout{ + TCP: &ir.TCPTimeout{ + ConnectTimeout: &metav1.Duration{Duration: 2 * time.Second}, + }, }, }, }