Skip to content

Commit

Permalink
Implement Downstream TcpKeepalive (#1939)
Browse files Browse the repository at this point in the history
* Implement Downstream TcpKeepalive

Relates to #1031

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* rename files

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* default comments

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* comments

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* rebase and fix test case

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

---------

Signed-off-by: Arko Dasgupta <arko@tetrate.io>
  • Loading branch information
arkodg authored Oct 14, 2023
1 parent 2ecc54d commit 7e77522
Show file tree
Hide file tree
Showing 14 changed files with 743 additions and 10 deletions.
80 changes: 74 additions & 6 deletions internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package gatewayapi
import (
"fmt"
"sort"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -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"
)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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: {}
22 changes: 21 additions & 1 deletion internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"`
}
Loading

0 comments on commit 7e77522

Please sign in to comment.