From 7e77522d380a9262f505faecfefdaee266cf77bf Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Sat, 14 Oct 2023 02:33:20 -0700 Subject: [PATCH] Implement Downstream TcpKeepalive (#1939) * Implement Downstream TcpKeepalive Relates to https://github.com/envoyproxy/gateway/issues/1031 Signed-off-by: Arko Dasgupta * rename files Signed-off-by: Arko Dasgupta * default comments Signed-off-by: Arko Dasgupta * comments Signed-off-by: Arko Dasgupta * rebase and fix test case Signed-off-by: Arko Dasgupta --------- Signed-off-by: Arko Dasgupta --- internal/gatewayapi/clienttrafficpolicy.go | 80 ++++++++- .../clienttrafficpolicy-tcp-keepalive.in.yaml | 50 ++++++ ...clienttrafficpolicy-tcp-keepalive.out.yaml | 163 ++++++++++++++++++ internal/ir/xds.go | 22 ++- internal/ir/zz_generated.deepcopy.go | 40 +++++ internal/xds/translator/listener.go | 6 +- internal/xds/translator/tcpkeepalive.go | 66 +++++++ .../in/xds-ir/listener-tcp-keepalive.yaml | 60 +++++++ .../listener-tcp-keepalive.clusters.yaml | 56 ++++++ .../listener-tcp-keepalive.endpoints.yaml | 44 +++++ .../listener-tcp-keepalive.listeners.yaml | 135 +++++++++++++++ .../xds-ir/listener-tcp-keepalive.routes.yaml | 24 +++ internal/xds/translator/translator.go | 4 +- internal/xds/translator/translator_test.go | 3 + 14 files changed, 743 insertions(+), 10 deletions(-) create mode 100644 internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml create mode 100755 internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml create mode 100644 internal/xds/translator/tcpkeepalive.go create mode 100644 internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index e13c8fb1873..78af1beb09d 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -8,6 +8,7 @@ package gatewayapi import ( "fmt" "sort" + "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -16,6 +17,7 @@ import ( gwv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/status" "github.com/envoyproxy/gateway/internal/utils/ptr" ) @@ -86,8 +88,13 @@ func ProcessClientTrafficPolicies(clientTrafficPolicies []*egv1a1.ClientTrafficP } policyMap[key].Insert(section) - translateClientTrafficPolicy(policy, xdsIR) - + // Translate for listener matching section name + for _, l := range gateway.listeners { + if string(l.Name) == section { + translateClientTrafficPolicyForListener(&policy.Spec, l, xdsIR) + break + } + } // Set Accepted=True status.SetClientTrafficPolicyCondition(policy, gwv1a2.PolicyConditionAccepted, @@ -154,7 +161,15 @@ func ProcessClientTrafficPolicies(clientTrafficPolicies []*egv1a1.ClientTrafficP } policyMap[key].Insert(AllSections) - translateClientTrafficPolicy(policy, xdsIR) + // Translate sections that have not yet been targeted + for _, l := range gateway.listeners { + // Skip if section has already been targeted + if s != nil && s.Has(string(l.Name)) { + continue + } + + translateClientTrafficPolicyForListener(&policy.Spec, l, xdsIR) + } // Set Accepted=True status.SetClientTrafficPolicyCondition(policy, @@ -229,7 +244,7 @@ func getGatewayTargetRef(policy *egv1a1.ClientTrafficPolicy, gateways []*Gateway // If sectionName is set, make sure its valid if policy.Spec.TargetRef.SectionName != nil { found := false - for _, l := range gateway.Spec.Listeners { + for _, l := range gateway.listeners { if l.Name == *(policy.Spec.TargetRef.SectionName) { found = true break @@ -250,6 +265,59 @@ func getGatewayTargetRef(policy *egv1a1.ClientTrafficPolicy, gateways []*Gateway return gateway } -func translateClientTrafficPolicy(policy *egv1a1.ClientTrafficPolicy, xdsIR XdsIRMap) { - // TODO +func translateClientTrafficPolicyForListener(policySpec *egv1a1.ClientTrafficPolicySpec, l *ListenerContext, xdsIR XdsIRMap) { + // Find IR + irKey := irStringKey(l.gateway.Namespace, l.gateway.Name) + // It must exist since we've already finished processing the gateways + gwXdsIR := xdsIR[irKey] + + // Find Listener IR + // TODO: Support TLSRoute and TCPRoute once + // https://github.com/envoyproxy/gateway/issues/1635 is completed + + irListenerName := irHTTPListenerName(l) + var httpIR *ir.HTTPListener + for _, http := range gwXdsIR.HTTP { + if http.Name == irListenerName { + httpIR = http + break + } + } + + // IR must exist since we're past validation + if httpIR != nil { + // Translate TCPKeepalive + translateListenerTCPKeepalive(policySpec.TCPKeepalive, httpIR) + } +} + +func translateListenerTCPKeepalive(tcpKeepAlive *egv1a1.TCPKeepalive, httpIR *ir.HTTPListener) { + // Return early if not set + if tcpKeepAlive == nil { + return + } + + irTCPKeepalive := &ir.TCPKeepalive{} + + if tcpKeepAlive.Probes != nil { + irTCPKeepalive.Probes = tcpKeepAlive.Probes + } + + if tcpKeepAlive.IdleTime != nil { + d, err := time.ParseDuration(string(*tcpKeepAlive.IdleTime)) + if err != nil { + return + } + irTCPKeepalive.IdleTime = ptr.To(uint32(d.Seconds())) + } + + if tcpKeepAlive.Interval != nil { + d, err := time.ParseDuration(string(*tcpKeepAlive.Interval)) + if err != nil { + return + } + irTCPKeepalive.Interval = ptr.To(uint32(d.Seconds())) + } + + httpIR.TCPKeepalive = irTCPKeepalive } diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml new file mode 100644 index 00000000000..8f913eb26a6 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.in.yaml @@ -0,0 +1,50 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1 + spec: + tcpKeepalive: {} + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + namespace: envoy-gateway + name: target-gateway-1-section-http-1 + spec: + tcpKeepalive: + probes: 3 + idleTime: 20m + interval: 60s + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + sectionName: http-1 + namespace: envoy-gateway +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http-1 + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + - name: http-2 + protocol: HTTP + port: 8080 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml new file mode 100755 index 00000000000..9eb9771a824 --- /dev/null +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-tcp-keepalive.out.yaml @@ -0,0 +1,163 @@ +clientTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1-section-http-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http-1 + tcpKeepalive: + idleTime: 20m + interval: 60s + probes: 3 + status: + conditions: + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: ClientTrafficPolicy + metadata: + creationTimestamp: null + name: target-gateway-1 + namespace: envoy-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + tcpKeepalive: {} + status: + conditions: + - lastTransitionTime: null + message: There are existing ClientTrafficPolicies that are overriding these + sections [http-1] + reason: Overridden + status: "True" + type: Overridden + - lastTransitionTime: null + message: ClientTrafficPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http-1 + port: 80 + protocol: HTTP + - allowedRoutes: + namespaces: + from: Same + name: http-2 + port: 8080 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + 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-1 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute + - attachedRoutes: 0 + 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-2 + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http-1 + protocol: HTTP + servicePort: 80 + - containerPort: 8080 + name: http-2 + protocol: HTTP + servicePort: 8080 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http-1 + port: 10080 + tcpKeepalive: + idleTime: 1200 + interval: 60 + probes: 3 + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http-2 + port: 8080 + tcpKeepalive: {} diff --git a/internal/ir/xds.go b/internal/ir/xds.go index e80a09769fe..39c7ecae50b 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -173,6 +173,8 @@ type HTTPListener struct { Routes []*HTTPRoute `json:"routes,omitempty" yaml:"routes,omitempty"` // IsHTTP2 is set if the upstream client as well as the downstream server are configured to serve HTTP2 traffic. IsHTTP2 bool `json:"isHTTP2" yaml:"isHTTP2"` + // TCPKeepalive configuration for the listener + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` } // Validate the fields within the HTTPListener structure @@ -696,6 +698,8 @@ type TCPListener struct { TLS *TLS `json:"tls,omitempty" yaml:"tls,omitempty"` // Destinations associated with TCP traffic to the service. Destination *RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` + // TCPKeepalive configuration for the listener + TCPKeepalive *TCPKeepalive `json:"tcpKeepalive,omitempty" yaml:"tcpKeepalive,omitempty"` } // TLS holds information for configuring TLS on a listener @@ -930,5 +934,21 @@ type Tracing struct { // Metrics defines the configuration for metrics generated by Envoy // +k8s:deepcopy-gen=true type Metrics struct { - EnableVirtualHostStats bool `json:"enableVirtualHostStats"` + EnableVirtualHostStats bool `json:"enableVirtualHostStats" yaml:"enableVirtualHostStats"` +} + +// TCPKeepalive define the TCP Keepalive configuration. +// +k8s:deepcopy-gen=true +type TCPKeepalive struct { + // The total number of unacknowledged probes to send before deciding + // the connection is dead. + // Defaults to 9. + Probes *uint32 `json:"probes,omitempty" yaml:"probes,omitempty"` + // The duration, in seconds, a connection needs to be idle before keep-alive + // probes start being sent. + // Defaults to `7200s`. + IdleTime *uint32 `json:"idleTime,omitempty" yaml:"idleTime,omitempty"` + // The duration, in seconds, between keep-alive probes. + // Defaults to `75s`. + Interval *uint32 `json:"interval,omitempty" yaml:"interval,omitempty"` } diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 3c5c228a11d..ec9ad1aa952 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -247,6 +247,11 @@ func (in *HTTPListener) DeepCopyInto(out *HTTPListener) { } } } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPListener. @@ -835,6 +840,36 @@ func (in *StringMatch) DeepCopy() *StringMatch { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPKeepalive) DeepCopyInto(out *TCPKeepalive) { + *out = *in + if in.Probes != nil { + in, out := &in.Probes, &out.Probes + *out = new(uint32) + **out = **in + } + if in.IdleTime != nil { + in, out := &in.IdleTime, &out.IdleTime + *out = new(uint32) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(uint32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPKeepalive. +func (in *TCPKeepalive) DeepCopy() *TCPKeepalive { + if in == nil { + return nil + } + out := new(TCPKeepalive) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPListener) DeepCopyInto(out *TCPListener) { *out = *in @@ -848,6 +883,11 @@ func (in *TCPListener) DeepCopyInto(out *TCPListener) { *out = new(RouteDestination) (*in).DeepCopyInto(*out) } + if in.TCPKeepalive != nil { + in, out := &in.TCPKeepalive, &out.TCPKeepalive + *out = new(TCPKeepalive) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPListener. diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go index 887436c5e15..5c5d9a80d5a 100644 --- a/internal/xds/translator/listener.go +++ b/internal/xds/translator/listener.go @@ -53,11 +53,15 @@ func http2ProtocolOptions() *corev3.Http2ProtocolOptions { } } -func buildXdsTCPListener(name, address string, port uint32, accesslog *ir.AccessLog) *listenerv3.Listener { +// buildXdsTCPListener creates a xds Listener resource +// TODO: Improve function parameters +func buildXdsTCPListener(name, address string, port uint32, keepalive *ir.TCPKeepalive, accesslog *ir.AccessLog) *listenerv3.Listener { + socketOptions := buildTCPSocketOptions(keepalive) al := buildXdsAccessLog(accesslog, true) return &listenerv3.Listener{ Name: name, AccessLog: al, + SocketOptions: socketOptions, PerConnectionBufferLimitBytes: wrapperspb.UInt32(tcpListenerPerConnectionBufferLimitBytes), Address: &corev3.Address{ Address: &corev3.Address_SocketAddress{ diff --git a/internal/xds/translator/tcpkeepalive.go b/internal/xds/translator/tcpkeepalive.go new file mode 100644 index 00000000000..9d4c71d6d30 --- /dev/null +++ b/internal/xds/translator/tcpkeepalive.go @@ -0,0 +1,66 @@ +// 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. + +package translator + +import ( + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + + "github.com/envoyproxy/gateway/internal/ir" +) + +// buildTCPSocketOptions converts listener downstream settings to xds socketOptions +func buildTCPSocketOptions(keepAlive *ir.TCPKeepalive) []*corev3.SocketOption { + if keepAlive == nil { + return nil + } + + socketOptions := make([]*corev3.SocketOption, 0) + // Enable Keep Alives + socketOption := &corev3.SocketOption{ + Description: "socket option to enable tcp keep alive", + Level: 0x1, // syscall.SOL_SOCKET has a different value for Darwin, resulting in `go test` failing + Name: 0x9, // syscall.SO_KEEPALIVE has a different value for Darwin, resulting in `go test` failing + Value: &corev3.SocketOption_IntValue{IntValue: 1}, // Enable + } + + socketOptions = append(socketOptions, socketOption) + + if keepAlive.Probes != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive probes", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x6, // Darwin lacks syscall.TCP_KEEPCNT, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.Probes)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + if keepAlive.IdleTime != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive idle time", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x4, // Darwin lacks syscall.TCP_KEEPIDLE, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.IdleTime)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + if keepAlive.Interval != nil { + socketOption = &corev3.SocketOption{ + Description: "socket option for keep alive interval", + Level: 0x6, // Darwin lacks syscall.SOL_TCP + Name: 0x5, // Darwin lacks syscall.TCP_KEEPINTVL, + Value: &corev3.SocketOption_IntValue{IntValue: int64(*keepAlive.Interval)}, + State: corev3.SocketOption_STATE_PREBIND, + } + socketOptions = append(socketOptions, socketOption) + } + + return socketOptions + +} diff --git a/internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml b/internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml new file mode 100644 index 00000000000..35905935a44 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/listener-tcp-keepalive.yaml @@ -0,0 +1,60 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "foo.com" + tcpKeepalive: {} + routes: + - name: "first-route" + hostname: "*" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "second-listener" + address: "0.0.0.0" + port: 10081 + hostnames: + - "foo.net" + tcpKeepalive: + probes: 7 + interval: 200 + idleTime: 50 + routes: + - name: "second-route" + hostname: "*" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +tcp: +- name: "third-listener" + address: "0.0.0.0" + port: 10082 + tcpKeepalive: {} + tls: + passthrough: + snis: + - bar.com + destination: + name: "tls-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 +- name: "fourth-listener" + address: "0.0.0.0" + tcpKeepalive: + probes: 10 + port: 10083 + destination: + name: "tcp-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml new file mode 100755 index 00000000000..1bcfa8d1104 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.clusters.yaml @@ -0,0 +1,56 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tls-route-dest + lbPolicy: LEAST_REQUEST + name: tls-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: tcp-route-dest + lbPolicy: LEAST_REQUEST + name: tcp-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml new file mode 100755 index 00000000000..1ef9c4df3a7 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.endpoints.yaml @@ -0,0 +1,44 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} +- clusterName: tls-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} +- clusterName: tcp-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml new file mode 100755 index 00000000000..09334586970 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.listeners.yaml @@ -0,0 +1,135 @@ +- 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 + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10081 + 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 + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: second-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: second-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" + - description: socket option for keep alive probes + intValue: "7" + level: "6" + name: "6" + - description: socket option for keep alive idle time + intValue: "50" + level: "6" + name: "4" + - description: socket option for keep alive interval + intValue: "200" + level: "6" + name: "5" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10082 + filterChains: + - filterChainMatch: + serverNames: + - bar.com + filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tls-route-dest + statPrefix: passthrough + listenerFilters: + - name: envoy.filters.listener.tls_inspector + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + name: third-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10083 + filterChains: + - filters: + - name: envoy.filters.network.tcp_proxy + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + cluster: tcp-route-dest + statPrefix: tcp + name: fourth-listener + perConnectionBufferLimitBytes: 32768 + socketOptions: + - description: socket option to enable tcp keep alive + intValue: "1" + level: "1" + name: "9" + - description: socket option for keep alive probes + intValue: "10" + level: "6" + name: "6" diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml new file mode 100755 index 00000000000..d36b9824e31 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/listener-tcp-keepalive.routes.yaml @@ -0,0 +1,24 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest +- ignorePortInHostMatching: true + name: second-listener + virtualHosts: + - domains: + - '*' + name: second-listener/* + routes: + - match: + prefix: / + name: second-route + route: + cluster: second-route-dest diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index 0b30491c1f5..5aa6deeef2a 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -105,7 +105,7 @@ func (t *Translator) processHTTPListenerXdsTranslation(tCtx *types.ResourceVersi // Search for an existing listener, if it does not exist, create one. xdsListener := findXdsListenerByHostPort(tCtx, httpListener.Address, httpListener.Port, corev3.SocketAddress_TCP) if xdsListener == nil { - xdsListener = buildXdsTCPListener(httpListener.Name, httpListener.Address, httpListener.Port, accesslog) + xdsListener = buildXdsTCPListener(httpListener.Name, httpListener.Address, httpListener.Port, httpListener.TCPKeepalive, accesslog) if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { return err } @@ -291,7 +291,7 @@ func processTCPListenerXdsTranslation(tCtx *types.ResourceVersionTable, tcpListe // Search for an existing listener, if it does not exist, create one. xdsListener := findXdsListenerByHostPort(tCtx, tcpListener.Address, tcpListener.Port, corev3.SocketAddress_TCP) if xdsListener == nil { - xdsListener = buildXdsTCPListener(tcpListener.Name, tcpListener.Address, tcpListener.Port, accesslog) + xdsListener = buildXdsTCPListener(tcpListener.Name, tcpListener.Address, tcpListener.Port, tcpListener.TCPKeepalive, accesslog) if err := tCtx.AddXdsResource(resourcev3.ListenerType, xdsListener); err != nil { return err } diff --git a/internal/xds/translator/translator_test.go b/internal/xds/translator/translator_test.go index 83d15580dd8..dacf70abd30 100644 --- a/internal/xds/translator/translator_test.go +++ b/internal/xds/translator/translator_test.go @@ -184,6 +184,9 @@ func TestTranslateXds(t *testing.T) { name: "jsonpatch-invalid-patch", requireEnvoyPatchPolicies: true, }, + { + name: "listener-tcp-keepalive", + }, } for _, tc := range testCases {