From df7215a1448cb8626ab81c07cb5767b99925f271 Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Wed, 2 Oct 2024 12:47:51 +0000 Subject: [PATCH] unit test kube-proxy conntrack flags Assert the flag behavior on kube-proxy using unit tests. --- cmd/kube-proxy/app/options_test.go | 16 +++ cmd/kube-proxy/app/server_linux.go | 7 +- cmd/kube-proxy/app/server_linux_test.go | 168 +++++++++++++++++++++++- 3 files changed, 186 insertions(+), 5 deletions(-) diff --git a/cmd/kube-proxy/app/options_test.go b/cmd/kube-proxy/app/options_test.go index f103b8d8d20..efeb01fd4f5 100644 --- a/cmd/kube-proxy/app/options_test.go +++ b/cmd/kube-proxy/app/options_test.go @@ -525,6 +525,22 @@ kind: KubeProxyConfiguration "empty": { expected: expected, }, + "conntrack": { + flags: []string{ + "--conntrack-max-per-core=0", + "--conntrack-min=0", + "--conntrack-tcp-timeout-established=0", + "--conntrack-tcp-timeout-close-wait=0", + }, + expected: func() *kubeproxyconfig.KubeProxyConfiguration { + c := expected.DeepCopy() + c.Linux.Conntrack.MaxPerCore = ptr.To(int32(0)) + c.Linux.Conntrack.Min = ptr.To(int32(0)) + c.Linux.Conntrack.TCPEstablishedTimeout = ptr.To(metav1.Duration{}) + c.Linux.Conntrack.TCPCloseWaitTimeout = ptr.To(metav1.Duration{}) + return c + }(), + }, "empty-config": { config: header, expected: expected, diff --git a/cmd/kube-proxy/app/server_linux.go b/cmd/kube-proxy/app/server_linux.go index d29ba6f3c1b..46873e821ea 100644 --- a/cmd/kube-proxy/app/server_linux.go +++ b/cmd/kube-proxy/app/server_linux.go @@ -93,7 +93,8 @@ func (s *ProxyServer) platformSetup(ctx context.Context) error { logger.Info("NodeInfo", "podCIDRs", node.Spec.PodCIDRs) } - err := s.setupConntrack(ctx) + ct := &realConntracker{} + err := s.setupConntrack(ctx, ct) if err != nil { return err } @@ -334,9 +335,7 @@ func (s *ProxyServer) createProxier(ctx context.Context, config *proxyconfigapi. return proxier, nil } -func (s *ProxyServer) setupConntrack(ctx context.Context) error { - ct := &realConntracker{} - +func (s *ProxyServer) setupConntrack(ctx context.Context, ct Conntracker) error { max, err := getConntrackMax(ctx, s.Config.Linux.Conntrack) if err != nil { return err diff --git a/cmd/kube-proxy/app/server_linux_test.go b/cmd/kube-proxy/app/server_linux_test.go index 4a2e17721ba..b224daf057e 100644 --- a/cmd/kube-proxy/app/server_linux_test.go +++ b/cmd/kube-proxy/app/server_linux_test.go @@ -20,6 +20,8 @@ limitations under the License. package app import ( + "context" + "errors" "fmt" "net" "os" @@ -30,8 +32,8 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/spf13/pflag" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -719,3 +721,167 @@ func TestProxyServer_platformSetup(t *testing.T) { }) } } + +type fakeConntracker struct { + called []string + err error +} + +// SetMax value is calculated based on the number of CPUs by getConntrackMax() +func (fc *fakeConntracker) SetMax(ctx context.Context, max int) error { + fc.called = append(fc.called, "SetMax") + return fc.err +} +func (fc *fakeConntracker) SetTCPEstablishedTimeout(ctx context.Context, seconds int) error { + fc.called = append(fc.called, fmt.Sprintf("SetTCPEstablishedTimeout(%d)", seconds)) + return fc.err +} +func (fc *fakeConntracker) SetTCPCloseWaitTimeout(ctx context.Context, seconds int) error { + fc.called = append(fc.called, fmt.Sprintf("SetTCPCloseWaitTimeout(%d)", seconds)) + return fc.err +} +func (fc *fakeConntracker) SetTCPBeLiberal(ctx context.Context, value int) error { + fc.called = append(fc.called, fmt.Sprintf("SetTCPBeLiberal(%d)", value)) + return fc.err +} +func (fc *fakeConntracker) SetUDPTimeout(ctx context.Context, seconds int) error { + fc.called = append(fc.called, fmt.Sprintf("SetUDPTimeout(%d)", seconds)) + return fc.err +} +func (fc *fakeConntracker) SetUDPStreamTimeout(ctx context.Context, seconds int) error { + fc.called = append(fc.called, fmt.Sprintf("SetUDPStreamTimeout(%d)", seconds)) + return fc.err +} + +func TestSetupConntrack(t *testing.T) { + _, ctx := ktesting.NewTestContext(t) + tests := []struct { + name string + config proxyconfigapi.KubeProxyConntrackConfiguration + expect []string + conntrackErr error + wantErr bool + }{ + { + name: "do nothing if conntrack config is empty", + config: proxyconfigapi.KubeProxyConntrackConfiguration{}, + expect: nil, + }, + { + name: "SetMax is called if conntrack.maxPerCore is specified", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To(int32(12)), + }, + expect: []string{"SetMax"}, + }, + { + name: "SetMax is not called if conntrack.maxPerCore is 0", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To(int32(0)), + }, + expect: nil, + }, + { + name: "SetTCPEstablishedTimeout is called if conntrack.tcpEstablishedTimeout is specified", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + expect: []string{"SetTCPEstablishedTimeout(5)"}, + }, + { + name: "SetTCPEstablishedTimeout is not called if conntrack.tcpEstablishedTimeout is 0", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPEstablishedTimeout: &metav1.Duration{Duration: 0 * time.Second}, + }, + expect: nil, + }, + { + name: "SetTCPCloseWaitTimeout is called if conntrack.tcpCloseWaitTimeout is specified", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + expect: []string{"SetTCPCloseWaitTimeout(5)"}, + }, + { + name: "SetTCPCloseWaitTimeout is not called if conntrack.tcpCloseWaitTimeout is 0", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPCloseWaitTimeout: &metav1.Duration{Duration: 0 * time.Second}, + }, + expect: nil, + }, + { + name: "SetTCPBeLiberal is called if conntrack.tcpBeLiberal is true", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPBeLiberal: true, + }, + expect: []string{"SetTCPBeLiberal(1)"}, + }, + { + name: "SetTCPBeLiberal is not called if conntrack.tcpBeLiberal is false", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPBeLiberal: false, + }, + expect: nil, + }, + { + name: "SetUDPTimeout is called if conntrack.udpTimeout is specified", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + UDPTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + expect: []string{"SetUDPTimeout(5)"}, + }, + { + name: "SetUDPTimeout is called if conntrack.udpTimeout is zero", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + UDPTimeout: metav1.Duration{Duration: 0 * time.Second}, + }, + expect: nil, + }, + { + name: "SetUDPStreamTimeout is called if conntrack.udpStreamTimeout is specified", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + UDPStreamTimeout: metav1.Duration{Duration: 5 * time.Second}, + }, + expect: []string{"SetUDPStreamTimeout(5)"}, + }, + { + name: "SetUDPStreamTimeout is called if conntrack.udpStreamTimeout is zero", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + UDPStreamTimeout: metav1.Duration{Duration: 0 * time.Second}, + }, + expect: nil, + }, + { + name: "an error is returned if conntrack.SetTCPEstablishedTimeout fails", + config: proxyconfigapi.KubeProxyConntrackConfiguration{ + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + expect: []string{"SetTCPEstablishedTimeout(5)"}, + conntrackErr: errors.New("random error"), + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fc := &fakeConntracker{err: test.conntrackErr} + s := &ProxyServer{ + Config: &proxyconfigapi.KubeProxyConfiguration{ + Linux: proxyconfigapi.KubeProxyLinuxConfiguration{ + Conntrack: test.config, + }, + }, + } + err := s.setupConntrack(ctx, fc) + if test.wantErr && err == nil { + t.Errorf("Test %q: Expected error, got nil", test.name) + } + if !test.wantErr && err != nil { + t.Errorf("Test %q: Expected no error, got %v", test.name, err) + } + if !cmp.Equal(fc.called, test.expect) { + t.Errorf("Test %q: Expected conntrack calls: %v, got: %v", test.name, test.expect, fc.called) + } + }) + } +}