From 7320e54e0e5fbc5679f8886d418af82200657563 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 22 Apr 2024 09:17:04 -0400 Subject: [PATCH 1/5] Split Linux/Windows TestValidateKubeProxyMode --- .../apis/config/validation/validation_test.go | 78 +++++++++++++++---- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go index cfeb5ccf57f..ca82b756d67 100644 --- a/pkg/proxy/apis/config/validation/validation_test.go +++ b/pkg/proxy/apis/config/validation/validation_test.go @@ -825,34 +825,80 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { } func TestValidateProxyMode(t *testing.T) { - newPath := field.NewPath("KubeProxyConfiguration") - successCases := []kubeproxyconfig.ProxyMode{""} - expectedNonExistentErrorMsg := "must be iptables, ipvs, nftables or blank (blank means the best-available proxy [currently iptables])" - if runtime.GOOS == "windows" { - successCases = append(successCases, kubeproxyconfig.ProxyModeKernelspace) - expectedNonExistentErrorMsg = "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])" + testValidateProxyModeWindows(t) } else { - successCases = append(successCases, kubeproxyconfig.ProxyModeIPTables, kubeproxyconfig.ProxyModeIPVS) - } - - for _, successCase := range successCases { - if errs := validateProxyMode(successCase, newPath.Child("ProxyMode")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } + testValidateProxyModeLinux(t) } +} +func testValidateProxyModeLinux(t *testing.T) { + newPath := field.NewPath("KubeProxyConfiguration") testCases := map[string]struct { mode kubeproxyconfig.ProxyMode expectedErrs field.ErrorList }{ "blank mode should default": { - mode: kubeproxyconfig.ProxyMode(""), - expectedErrs: field.ErrorList{}, + mode: kubeproxyconfig.ProxyMode(""), + }, + "iptables is allowed": { + mode: kubeproxyconfig.ProxyModeIPTables, + }, + "ipvs is allowed": { + mode: kubeproxyconfig.ProxyModeIPVS, + }, + "nftables is allowed": { + mode: kubeproxyconfig.ProxyModeNFTables, + }, + "winkernel is not allowed": { + mode: kubeproxyconfig.ProxyModeKernelspace, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "kernelspace", "must be iptables, ipvs, nftables or blank (blank means the best-available proxy [currently iptables])")}, }, "invalid mode non-existent": { mode: kubeproxyconfig.ProxyMode("non-existing"), - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", expectedNonExistentErrorMsg)}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", "must be iptables, ipvs, nftables or blank (blank means the best-available proxy [currently iptables])")}, + }, + } + for _, testCase := range testCases { + errs := validateProxyMode(testCase.mode, newPath) + if len(testCase.expectedErrs) != len(errs) { + t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) + } + for i, err := range errs { + if err.Error() != testCase.expectedErrs[i].Error() { + t.Errorf("Expected error: %s, got %v", testCase.expectedErrs[i], err.Error()) + } + } + } +} + +func testValidateProxyModeWindows(t *testing.T) { + newPath := field.NewPath("KubeProxyConfiguration") + testCases := map[string]struct { + mode kubeproxyconfig.ProxyMode + expectedErrs field.ErrorList + }{ + "blank mode should default": { + mode: kubeproxyconfig.ProxyMode(""), + }, + "winkernel is allowed": { + mode: kubeproxyconfig.ProxyModeKernelspace, + }, + "iptables is not allowed": { + mode: kubeproxyconfig.ProxyModeIPTables, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "iptables", "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])")}, + }, + "ipvs is not allowed": { + mode: kubeproxyconfig.ProxyModeIPVS, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "ipvs", "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])")}, + }, + "nftables is not allowed": { + mode: kubeproxyconfig.ProxyModeNFTables, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "nftables", "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])")}, + }, + "invalid mode non-existent": { + mode: kubeproxyconfig.ProxyMode("non-existing"), + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])")}, }, } for _, testCase := range testCases { From c7f3caf498a7df16e4958f49c94d85ae335c5bd1 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 6 Mar 2024 10:27:09 -0500 Subject: [PATCH 2/5] Add names to all KubeProxyConfiguration validation test cases --- .../apis/config/validation/validation_test.go | 527 ++++++++++-------- 1 file changed, 307 insertions(+), 220 deletions(-) diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go index ca82b756d67..9813b38eb4a 100644 --- a/pkg/proxy/apis/config/validation/validation_test.go +++ b/pkg/proxy/apis/config/validation/validation_test.go @@ -37,203 +37,234 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { } else { proxyMode = kubeproxyconfig.ProxyModeIPVS } - successCases := []kubeproxyconfig.KubeProxyConfiguration{{ - BindAddress: "192.168.59.103", - HealthzBindAddress: "0.0.0.0:10256", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + + successCases := map[string]struct { + config kubeproxyconfig.KubeProxyConfiguration + }{ + "Mode specified, extra mode-specific configs": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "192.168.59.103", + HealthzBindAddress: "0.0.0.0:10256", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Mode: proxyMode, + IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{ + SyncPeriod: metav1.Duration{Duration: 10 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Mode: proxyMode, - IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{ - SyncPeriod: metav1.Duration{Duration: 10 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + "basic config, unspecified Mode": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "192.168.59.103", + HealthzBindAddress: "0.0.0.0:10256", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + "empty HealthzBindAddress": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "192.168.59.103", + HealthzBindAddress: "", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", + "IPv6": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "fd00:192:168:59::103", + HealthzBindAddress: "", + MetricsBindAddress: "[::1]:10249", + ClusterCIDR: "fd00:192:168:59::/64", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - }, { - BindAddress: "192.168.59.103", - HealthzBindAddress: "0.0.0.0:10256", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + "alternate healthz port": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + "ClusterCIDR is wrong IP family": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "fd00:192:168::/64", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", + "ClusterCIDR is dual-stack": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - }, { - BindAddress: "192.168.59.103", - HealthzBindAddress: "", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + "LocalModeInterfaceNamePrefix": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + DetectLocalMode: kubeproxyconfig.LocalModeInterfaceNamePrefix, + DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ + InterfaceNamePrefix: "vethabcde", + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + "LocalModeBridgeInterface": { + config: kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "10.10.12.11", + HealthzBindAddress: "0.0.0.0:12345", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + DetectLocalMode: kubeproxyconfig.LocalModeBridgeInterface, + DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ + BridgeInterface: "avz", + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, + }, }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, { - BindAddress: "fd00:192:168:59::103", - HealthzBindAddress: "", - MetricsBindAddress: "[::1]:10249", - ClusterCIDR: "fd00:192:168:59::/64", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, { - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, { - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "fd00:192:168::/64", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, { - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, { - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: kubeproxyconfig.LocalModeInterfaceNamePrefix, - DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ - InterfaceNamePrefix: "vethabcde", - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, { - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: kubeproxyconfig.LocalModeBridgeInterface, - DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ - BridgeInterface: "avz", - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }} + } for _, successCase := range successCases { - if errs := Validate(&successCase); len(errs) != 0 { + if errs := Validate(&successCase.config); len(errs) != 0 { t.Errorf("expected success: %v", errs) } } @@ -951,14 +982,22 @@ func TestValidateClientConnectionConfiguration(t *testing.T) { func TestValidateHostPort(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - successCases := []string{ - "0.0.0.0:10256", - "127.0.0.1:10256", - "10.10.10.10:10256", + successCases := map[string]struct { + ip string + }{ + "all IPs": { + ip: "0.0.0.0:10256", + }, + "localhost": { + ip: "127.0.0.1:10256", + }, + "specific IP": { + ip: "10.10.10.10:10256", + }, } for _, successCase := range successCases { - if errs := validateHostPort(successCase, newPath.Child("HealthzBindAddress")); len(errs) != 0 { + if errs := validateHostPort(successCase.ip, newPath.Child("HealthzBindAddress")); len(errs) != 0 { t.Errorf("expected success: %v", errs) } } @@ -1005,23 +1044,48 @@ func TestValidateHostPort(t *testing.T) { func TestValidateKubeProxyNodePortAddress(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - successCases := []struct { + successCases := map[string]struct { addresses []string }{ - {[]string{}}, - {[]string{"127.0.0.0/8"}}, - {[]string{"0.0.0.0/0"}}, - {[]string{"::/0"}}, - {[]string{"127.0.0.1/32", "1.2.3.0/24"}}, - {[]string{"127.0.0.0/8"}}, - {[]string{"127.0.0.1/32"}}, - {[]string{"::1/128"}}, - {[]string{"1.2.3.4/32"}}, - {[]string{"10.20.30.0/24"}}, - {[]string{"10.20.0.0/16", "100.200.0.0/16"}}, - {[]string{"10.0.0.0/8"}}, - {[]string{"2001:db8::/32"}}, - {[]string{kubeproxyconfig.NodePortAddressesPrimary}}, + "no addresses": { + addresses: []string{}, + }, + "valid 1": { + addresses: []string{"127.0.0.0/8"}, + }, + "valid 2": { + addresses: []string{"0.0.0.0/0"}, + }, + "valid 3": { + addresses: []string{"::/0"}, + }, + "valid 4": { + addresses: []string{"127.0.0.1/32", "1.2.3.0/24"}, + }, + "valid 5": { + addresses: []string{"127.0.0.1/32"}, + }, + "valid 6": { + addresses: []string{"::1/128"}, + }, + "valid 7": { + addresses: []string{"1.2.3.4/32"}, + }, + "valid 8": { + addresses: []string{"10.20.30.0/24"}, + }, + "valid 9": { + addresses: []string{"10.20.0.0/16", "100.200.0.0/16"}, + }, + "valid 10": { + addresses: []string{"10.0.0.0/8"}, + }, + "valid 11": { + addresses: []string{"2001:db8::/32"}, + }, + "primary": { + addresses: []string{kubeproxyconfig.NodePortAddressesPrimary}, + }, } for _, successCase := range successCases { @@ -1085,22 +1149,45 @@ func TestValidateKubeProxyNodePortAddress(t *testing.T) { func TestValidateKubeProxyExcludeCIDRs(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - successCases := []struct { + successCases := map[string]struct { addresses []string }{ - {[]string{}}, - {[]string{"127.0.0.0/8"}}, - {[]string{"0.0.0.0/0"}}, - {[]string{"::/0"}}, - {[]string{"127.0.0.1/32", "1.2.3.0/24"}}, - {[]string{"127.0.0.0/8"}}, - {[]string{"127.0.0.1/32"}}, - {[]string{"::1/128"}}, - {[]string{"1.2.3.4/32"}}, - {[]string{"10.20.30.0/24"}}, - {[]string{"10.20.0.0/16", "100.200.0.0/16"}}, - {[]string{"10.0.0.0/8"}}, - {[]string{"2001:db8::/32"}}, + "no cidrs": { + addresses: []string{}, + }, + "valid 1": { + addresses: []string{"127.0.0.0/8"}, + }, + "valid 2": { + addresses: []string{"0.0.0.0/0"}, + }, + "valid 3": { + addresses: []string{"::/0"}, + }, + "valid 4": { + addresses: []string{"127.0.0.1/32", "1.2.3.0/24"}, + }, + "valid 5": { + addresses: []string{"127.0.0.1/32"}, + }, + "valid 6": { + addresses: []string{"::1/128"}, + }, + "valid 7": { + addresses: []string{"1.2.3.4/32"}, + }, + "valid 8": { + addresses: []string{"10.20.30.0/24"}, + }, + "valid 9": { + addresses: []string{"10.20.0.0/16", "100.200.0.0/16"}, + }, + "valid 10": { + addresses: []string{"10.0.0.0/8"}, + }, + "valid 11": { + addresses: []string{"2001:db8::/32"}, + }, } for _, successCase := range successCases { From 7b9f7308047b59d6fa0e7070a02ac725d137810b Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 6 Mar 2024 12:28:37 -0500 Subject: [PATCH 3/5] Merge success and failure cases in KubeProxyConfiguration validation tests --- .../apis/config/validation/validation_test.go | 133 +++++------------- 1 file changed, 34 insertions(+), 99 deletions(-) diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go index 9813b38eb4a..8f400cab712 100644 --- a/pkg/proxy/apis/config/validation/validation_test.go +++ b/pkg/proxy/apis/config/validation/validation_test.go @@ -37,9 +37,11 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { } else { proxyMode = kubeproxyconfig.ProxyModeIPVS } + newPath := field.NewPath("KubeProxyConfiguration") - successCases := map[string]struct { - config kubeproxyconfig.KubeProxyConfiguration + for name, testCase := range map[string]struct { + config kubeproxyconfig.KubeProxyConfiguration + expectedErrs field.ErrorList }{ "Mode specified, extra mode-specific configs": { config: kubeproxyconfig.KubeProxyConfiguration{ @@ -261,19 +263,6 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { }, }, }, - } - - for _, successCase := range successCases { - if errs := Validate(&successCase.config); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - newPath := field.NewPath("KubeProxyConfiguration") - testCases := map[string]struct { - config kubeproxyconfig.KubeProxyConfiguration - expectedErrs field.ErrorList - }{ "invalid BindAddress": { config: kubeproxyconfig.KubeProxyConfiguration{ BindAddress: "10.10.12.11:2000", @@ -549,9 +538,7 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("logging.format"), "unsupported format", "Unsupported log format")}, }, - } - - for name, testCase := range testCases { + } { if runtime.GOOS == "windows" && testCase.config.Mode == kubeproxyconfig.ProxyModeIPVS { // IPVS is not supported on Windows. t.Log("Skipping test on Windows: ", name) @@ -574,7 +561,7 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - testCases := map[string]struct { + for _, testCase := range map[string]struct { config kubeproxyconfig.KubeProxyIPTablesConfiguration expectedErrs field.ErrorList }{ @@ -631,9 +618,7 @@ func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeIPTablesConfiguration.SyncPeriod"), metav1.Duration{Duration: 5 * time.Second}, "must be greater than or equal to KubeProxyConfiguration.KubeIPTablesConfiguration.MinSyncPeriod")}, }, - } - - for _, testCase := range testCases { + } { errs := validateKubeProxyIPTablesConfiguration(testCase.config, newPath.Child("KubeIPTablesConfiguration")) if len(testCase.expectedErrs) != len(errs) { t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -648,7 +633,7 @@ func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { func TestValidateKubeProxyIPVSConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - testCases := map[string]struct { + for _, testCase := range map[string]struct { config kubeproxyconfig.KubeProxyIPVSConfiguration expectedErrs field.ErrorList }{ @@ -732,8 +717,7 @@ func TestValidateKubeProxyIPVSConfiguration(t *testing.T) { field.Invalid(newPath.Child("KubeIPVSConfiguration.TCPFinTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0"), field.Invalid(newPath.Child("KubeIPVSConfiguration.UDPTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")}, }, - } - for _, testCase := range testCases { + } { errs := validateKubeProxyIPVSConfiguration(testCase.config, newPath.Child("KubeIPVSConfiguration")) if len(testCase.expectedErrs) != len(errs) { t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -748,7 +732,7 @@ func TestValidateKubeProxyIPVSConfiguration(t *testing.T) { func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - testCases := map[string]struct { + for _, testCase := range map[string]struct { config kubeproxyconfig.KubeProxyConntrackConfiguration expectedErrs field.ErrorList }{ @@ -840,9 +824,7 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.UDPStreamTimeout"), metav1.Duration{Duration: -5 * time.Second}, "must be greater than or equal to 0")}, }, - } - - for _, testCase := range testCases { + } { errs := validateKubeProxyConntrackConfiguration(testCase.config, newPath.Child("KubeConntrackConfiguration")) if len(testCase.expectedErrs) != len(errs) { t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -865,7 +847,7 @@ func TestValidateProxyMode(t *testing.T) { func testValidateProxyModeLinux(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - testCases := map[string]struct { + for _, testCase := range map[string]struct { mode kubeproxyconfig.ProxyMode expectedErrs field.ErrorList }{ @@ -889,8 +871,7 @@ func testValidateProxyModeLinux(t *testing.T) { mode: kubeproxyconfig.ProxyMode("non-existing"), expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", "must be iptables, ipvs, nftables or blank (blank means the best-available proxy [currently iptables])")}, }, - } - for _, testCase := range testCases { + } { errs := validateProxyMode(testCase.mode, newPath) if len(testCase.expectedErrs) != len(errs) { t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -905,7 +886,7 @@ func testValidateProxyModeLinux(t *testing.T) { func testValidateProxyModeWindows(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - testCases := map[string]struct { + for _, testCase := range map[string]struct { mode kubeproxyconfig.ProxyMode expectedErrs field.ErrorList }{ @@ -931,8 +912,7 @@ func testValidateProxyModeWindows(t *testing.T) { mode: kubeproxyconfig.ProxyMode("non-existing"), expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])")}, }, - } - for _, testCase := range testCases { + } { errs := validateProxyMode(testCase.mode, newPath) if len(testCase.expectedErrs) != len(errs) { t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -947,8 +927,7 @@ func testValidateProxyModeWindows(t *testing.T) { func TestValidateClientConnectionConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - - testCases := map[string]struct { + for _, testCase := range map[string]struct { ccc componentbaseconfig.ClientConnectionConfiguration expectedErrs field.ErrorList }{ @@ -964,9 +943,7 @@ func TestValidateClientConnectionConfiguration(t *testing.T) { ccc: componentbaseconfig.ClientConnectionConfiguration{Burst: -5}, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("Burst"), -5, "must be greater than or equal to 0")}, }, - } - - for _, testCase := range testCases { + } { errs := validateClientConnectionConfiguration(testCase.ccc, newPath) if len(testCase.expectedErrs) != len(errs) { t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -981,9 +958,9 @@ func TestValidateClientConnectionConfiguration(t *testing.T) { func TestValidateHostPort(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - - successCases := map[string]struct { - ip string + for _, testCase := range map[string]struct { + ip string + expectedErrs field.ErrorList }{ "all IPs": { ip: "0.0.0.0:10256", @@ -994,18 +971,6 @@ func TestValidateHostPort(t *testing.T) { "specific IP": { ip: "10.10.10.10:10256", }, - } - - for _, successCase := range successCases { - if errs := validateHostPort(successCase.ip, newPath.Child("HealthzBindAddress")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - errorCases := map[string]struct { - ip string - expectedErrs field.ErrorList - }{ "missing port": { ip: "10.10.10.10", expectedErrs: field.ErrorList{field.Invalid(newPath.Child("HealthzBindAddress"), "10.10.10.10", "must be IP:port")}, @@ -1026,16 +991,14 @@ func TestValidateHostPort(t *testing.T) { ip: "10.10.10.10:65536", expectedErrs: field.ErrorList{field.Invalid(newPath.Child("HealthzBindAddress"), "65536", "must be a valid port")}, }, - } - - for _, errorCase := range errorCases { - errs := validateHostPort(errorCase.ip, newPath.Child("HealthzBindAddress")) - if len(errorCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(errorCase.expectedErrs), len(errs), errs) + } { + errs := validateHostPort(testCase.ip, newPath.Child("HealthzBindAddress")) + if len(testCase.expectedErrs) != len(errs) { + t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) } for i, err := range errs { - if err.Error() != errorCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", errorCase.expectedErrs[i], err.Error()) + if err.Error() != testCase.expectedErrs[i].Error() { + t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) } } } @@ -1043,9 +1006,9 @@ func TestValidateHostPort(t *testing.T) { func TestValidateKubeProxyNodePortAddress(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - - successCases := map[string]struct { - addresses []string + for _, testCase := range map[string]struct { + addresses []string + expectedErrs field.ErrorList }{ "no addresses": { addresses: []string{}, @@ -1086,18 +1049,6 @@ func TestValidateKubeProxyNodePortAddress(t *testing.T) { "primary": { addresses: []string{kubeproxyconfig.NodePortAddressesPrimary}, }, - } - - for _, successCase := range successCases { - if errs := validateKubeProxyNodePortAddress(successCase.addresses, newPath.Child("NodePortAddresses")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - testCases := map[string]struct { - addresses []string - expectedErrs field.ErrorList - }{ "invalid foo address": { addresses: []string{"foo"}, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("NodePortAddresses[0]"), "foo", "must be a valid CIDR")}, @@ -1131,9 +1082,7 @@ func TestValidateKubeProxyNodePortAddress(t *testing.T) { addresses: []string{"127.0.0.1/32", "primary"}, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("NodePortAddresses[1]"), "primary", "can't use both 'primary' and CIDRs")}, }, - } - - for _, testCase := range testCases { + } { errs := validateKubeProxyNodePortAddress(testCase.addresses, newPath.Child("NodePortAddresses")) if len(testCase.expectedErrs) != len(errs) { t.Errorf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) @@ -1148,9 +1097,9 @@ func TestValidateKubeProxyNodePortAddress(t *testing.T) { func TestValidateKubeProxyExcludeCIDRs(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - - successCases := map[string]struct { - addresses []string + for _, testCase := range map[string]struct { + addresses []string + expectedErrs field.ErrorList }{ "no cidrs": { addresses: []string{}, @@ -1188,18 +1137,6 @@ func TestValidateKubeProxyExcludeCIDRs(t *testing.T) { "valid 11": { addresses: []string{"2001:db8::/32"}, }, - } - - for _, successCase := range successCases { - if errs := validateIPVSExcludeCIDRs(successCase.addresses, newPath.Child("ExcludeCIDRs")); len(errs) != 0 { - t.Errorf("expected success: %v", errs) - } - } - - testCases := map[string]struct { - addresses []string - expectedErrs field.ErrorList - }{ "invalid foo address": { addresses: []string{"foo"}, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ExcludeCIDRS[0]"), "foo", "must be a valid CIDR")}, @@ -1225,9 +1162,7 @@ func TestValidateKubeProxyExcludeCIDRs(t *testing.T) { addresses: []string{"::1/128", "2001:db8::/32", "2001:db8:xyz/64"}, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ExcludeCIDRS[2]"), "2001:db8:xyz/64", "must be a valid CIDR")}, }, - } - - for _, testCase := range testCases { + } { errs := validateIPVSExcludeCIDRs(testCase.addresses, newPath.Child("ExcludeCIDRS")) if len(testCase.expectedErrs) != len(errs) { t.Errorf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) From f4ecae8324fcca138b42a0f5cf503eb39597cb9e Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 6 Mar 2024 10:21:12 -0500 Subject: [PATCH 4/5] Use t.Run and assert.Equal in KubeProxyConfiguration validation tests --- .../apis/config/validation/validation_test.go | 166 +++++++----------- 1 file changed, 66 insertions(+), 100 deletions(-) diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go index 8f400cab712..d620ab4e7aa 100644 --- a/pkg/proxy/apis/config/validation/validation_test.go +++ b/pkg/proxy/apis/config/validation/validation_test.go @@ -21,6 +21,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" componentbaseconfig "k8s.io/component-base/config" @@ -546,13 +548,10 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { } t.Run(name, func(t *testing.T) { errs := Validate(&testCase.config) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Fatalf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) - } + if len(testCase.expectedErrs) == 0 { + assert.Equal(t, field.ErrorList{}, errs, "expected no validation errors") + } else { + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") } }) } @@ -561,7 +560,7 @@ func TestValidateKubeProxyConfiguration(t *testing.T) { func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { config kubeproxyconfig.KubeProxyIPTablesConfiguration expectedErrs field.ErrorList }{ @@ -607,7 +606,7 @@ func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, }, - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeIPTablesConfiguration.MasqueradeBit"), -10, "must be within the range [0, 31]")}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeIPTablesConfiguration.MasqueradeBit"), ptr.To[int32](-10), "must be within the range [0, 31]")}, }, "SyncPeriod must be >= MinSyncPeriod": { config: kubeproxyconfig.KubeProxyIPTablesConfiguration{ @@ -619,21 +618,16 @@ func TestValidateKubeProxyIPTablesConfiguration(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeIPTablesConfiguration.SyncPeriod"), metav1.Duration{Duration: 5 * time.Second}, "must be greater than or equal to KubeProxyConfiguration.KubeIPTablesConfiguration.MinSyncPeriod")}, }, } { - errs := validateKubeProxyIPTablesConfiguration(testCase.config, newPath.Child("KubeIPTablesConfiguration")) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) - } - } + t.Run(name, func(t *testing.T) { + errs := validateKubeProxyIPTablesConfiguration(testCase.config, newPath.Child("KubeIPTablesConfiguration")) + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") + }) } } func TestValidateKubeProxyIPVSConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { config kubeproxyconfig.KubeProxyIPVSConfiguration expectedErrs field.ErrorList }{ @@ -718,21 +712,16 @@ func TestValidateKubeProxyIPVSConfiguration(t *testing.T) { field.Invalid(newPath.Child("KubeIPVSConfiguration.UDPTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")}, }, } { - errs := validateKubeProxyIPVSConfiguration(testCase.config, newPath.Child("KubeIPVSConfiguration")) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) - } - } + t.Run(name, func(t *testing.T) { + errs := validateKubeProxyIPVSConfiguration(testCase.config, newPath.Child("KubeIPVSConfiguration")) + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") + }) } } func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { config kubeproxyconfig.KubeProxyConntrackConfiguration expectedErrs field.ErrorList }{ @@ -767,7 +756,7 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { UDPTimeout: metav1.Duration{Duration: 5 * time.Second}, UDPStreamTimeout: metav1.Duration{Duration: 5 * time.Second}, }, - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.MaxPerCore"), -1, "must be greater than or equal to 0")}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.MaxPerCore"), ptr.To[int32](-1), "must be greater than or equal to 0")}, }, "invalid minimum < 0": { config: kubeproxyconfig.KubeProxyConntrackConfiguration{ @@ -778,7 +767,7 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { UDPTimeout: metav1.Duration{Duration: 5 * time.Second}, UDPStreamTimeout: metav1.Duration{Duration: 5 * time.Second}, }, - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.Min"), -1, "must be greater than or equal to 0")}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.Min"), ptr.To[int32](-1), "must be greater than or equal to 0")}, }, "invalid TCPEstablishedTimeout < 0": { config: kubeproxyconfig.KubeProxyConntrackConfiguration{ @@ -789,7 +778,7 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { UDPTimeout: metav1.Duration{Duration: 5 * time.Second}, UDPStreamTimeout: metav1.Duration{Duration: 5 * time.Second}, }, - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.TCPEstablishedTimeout"), metav1.Duration{Duration: -5 * time.Second}, "must be greater than or equal to 0")}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.TCPEstablishedTimeout"), &metav1.Duration{Duration: -5 * time.Second}, "must be greater than or equal to 0")}, }, "invalid TCPCloseWaitTimeout < 0": { config: kubeproxyconfig.KubeProxyConntrackConfiguration{ @@ -800,7 +789,7 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { UDPTimeout: metav1.Duration{Duration: 5 * time.Second}, UDPStreamTimeout: metav1.Duration{Duration: 5 * time.Second}, }, - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.TCPCloseWaitTimeout"), metav1.Duration{Duration: -5 * time.Second}, "must be greater than or equal to 0")}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.TCPCloseWaitTimeout"), &metav1.Duration{Duration: -5 * time.Second}, "must be greater than or equal to 0")}, }, "invalid UDPTimeout < 0": { config: kubeproxyconfig.KubeProxyConntrackConfiguration{ @@ -825,15 +814,10 @@ func TestValidateKubeProxyConntrackConfiguration(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeConntrackConfiguration.UDPStreamTimeout"), metav1.Duration{Duration: -5 * time.Second}, "must be greater than or equal to 0")}, }, } { - errs := validateKubeProxyConntrackConfiguration(testCase.config, newPath.Child("KubeConntrackConfiguration")) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) - } - } + t.Run(name, func(t *testing.T) { + errs := validateKubeProxyConntrackConfiguration(testCase.config, newPath.Child("KubeConntrackConfiguration")) + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") + }) } } @@ -847,7 +831,7 @@ func TestValidateProxyMode(t *testing.T) { func testValidateProxyModeLinux(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { mode kubeproxyconfig.ProxyMode expectedErrs field.ErrorList }{ @@ -872,21 +856,16 @@ func testValidateProxyModeLinux(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", "must be iptables, ipvs, nftables or blank (blank means the best-available proxy [currently iptables])")}, }, } { - errs := validateProxyMode(testCase.mode, newPath) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %v", testCase.expectedErrs[i], err.Error()) - } - } + t.Run(name, func(t *testing.T) { + errs := validateProxyMode(testCase.mode, newPath) + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") + }) } } func testValidateProxyModeWindows(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { mode kubeproxyconfig.ProxyMode expectedErrs field.ErrorList }{ @@ -913,21 +892,16 @@ func testValidateProxyModeWindows(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "non-existing", "must be kernelspace or blank (blank means the most-available proxy [currently kernelspace])")}, }, } { - errs := validateProxyMode(testCase.mode, newPath) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %v", testCase.expectedErrs[i], err.Error()) - } - } + t.Run(name, func(t *testing.T) { + errs := validateProxyMode(testCase.mode, newPath) + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") + }) } } func TestValidateClientConnectionConfiguration(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { ccc componentbaseconfig.ClientConnectionConfiguration expectedErrs field.ErrorList }{ @@ -941,24 +915,19 @@ func TestValidateClientConnectionConfiguration(t *testing.T) { }, "burst < 0": { ccc: componentbaseconfig.ClientConnectionConfiguration{Burst: -5}, - expectedErrs: field.ErrorList{field.Invalid(newPath.Child("Burst"), -5, "must be greater than or equal to 0")}, + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("Burst"), int64(-5), "must be greater than or equal to 0")}, }, } { - errs := validateClientConnectionConfiguration(testCase.ccc, newPath) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) - } - } + t.Run(name, func(t *testing.T) { + errs := validateClientConnectionConfiguration(testCase.ccc, newPath) + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") + }) } } func TestValidateHostPort(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { ip string expectedErrs field.ErrorList }{ @@ -992,21 +961,20 @@ func TestValidateHostPort(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("HealthzBindAddress"), "65536", "must be a valid port")}, }, } { - errs := validateHostPort(testCase.ip, newPath.Child("HealthzBindAddress")) - if len(testCase.expectedErrs) != len(errs) { - t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) + t.Run(name, func(t *testing.T) { + errs := validateHostPort(testCase.ip, newPath.Child("HealthzBindAddress")) + if len(testCase.expectedErrs) == 0 { + assert.Equal(t, field.ErrorList{}, errs, "expected no validation errors") + } else { + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") } - } + }) } } func TestValidateKubeProxyNodePortAddress(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { addresses []string expectedErrs field.ErrorList }{ @@ -1083,21 +1051,20 @@ func TestValidateKubeProxyNodePortAddress(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("NodePortAddresses[1]"), "primary", "can't use both 'primary' and CIDRs")}, }, } { - errs := validateKubeProxyNodePortAddress(testCase.addresses, newPath.Child("NodePortAddresses")) - if len(testCase.expectedErrs) != len(errs) { - t.Errorf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) + t.Run(name, func(t *testing.T) { + errs := validateKubeProxyNodePortAddress(testCase.addresses, newPath.Child("NodePortAddresses")) + if len(testCase.expectedErrs) == 0 { + assert.Equal(t, field.ErrorList{}, errs, "expected no validation errors") + } else { + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") } - } + }) } } func TestValidateKubeProxyExcludeCIDRs(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") - for _, testCase := range map[string]struct { + for name, testCase := range map[string]struct { addresses []string expectedErrs field.ErrorList }{ @@ -1163,14 +1130,13 @@ func TestValidateKubeProxyExcludeCIDRs(t *testing.T) { expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ExcludeCIDRS[2]"), "2001:db8:xyz/64", "must be a valid CIDR")}, }, } { - errs := validateIPVSExcludeCIDRs(testCase.addresses, newPath.Child("ExcludeCIDRS")) - if len(testCase.expectedErrs) != len(errs) { - t.Errorf("Expected %d errors, got %d errors: %v", len(testCase.expectedErrs), len(errs), errs) - } - for i, err := range errs { - if err.Error() != testCase.expectedErrs[i].Error() { - t.Errorf("Expected error: %s, got %s", testCase.expectedErrs[i], err.Error()) + t.Run(name, func(t *testing.T) { + errs := validateIPVSExcludeCIDRs(testCase.addresses, newPath.Child("ExcludeCIDRS")) + if len(testCase.expectedErrs) == 0 { + assert.Equal(t, field.ErrorList{}, errs, "expected no validation errors") + } else { + assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") } - } + }) } } From a577c0b32412b0e729a5dea59b7b1c5efa14ab3e Mon Sep 17 00:00:00 2001 From: Daman Arora Date: Mon, 19 Feb 2024 14:12:26 +0530 Subject: [PATCH 5/5] kube-proxy: refactor config validation unit test Refactor the TestValidateKubeProxyConfiguration by adding a mutating function that adjusts the configuration according to each test case, thereby enhancing readability. Signed-off-by: Daman Arora --- .../apis/config/validation/validation_test.go | 522 +++--------------- 1 file changed, 86 insertions(+), 436 deletions(-) diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go index d620ab4e7aa..aae5bac74be 100644 --- a/pkg/proxy/apis/config/validation/validation_test.go +++ b/pkg/proxy/apis/config/validation/validation_test.go @@ -28,526 +28,176 @@ import ( componentbaseconfig "k8s.io/component-base/config" logsapi "k8s.io/component-base/logs/api/v1" kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config" - "k8s.io/utils/ptr" ) func TestValidateKubeProxyConfiguration(t *testing.T) { - var proxyMode kubeproxyconfig.ProxyMode - if runtime.GOOS == "windows" { - proxyMode = kubeproxyconfig.ProxyModeKernelspace - } else { - proxyMode = kubeproxyconfig.ProxyModeIPVS + baseConfig := &kubeproxyconfig.KubeProxyConfiguration{ + BindAddress: "192.168.59.103", + HealthzBindAddress: "0.0.0.0:10256", + MetricsBindAddress: "127.0.0.1:10249", + ClusterCIDR: "192.168.59.0/24", + ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, + IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ + MasqueradeAll: true, + SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, + }, + Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ + MaxPerCore: ptr.To[int32](1), + Min: ptr.To[int32](1), + TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, + TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, + }, + Logging: logsapi.LoggingConfiguration{ + Format: "text", + }, } newPath := field.NewPath("KubeProxyConfiguration") for name, testCase := range map[string]struct { - config kubeproxyconfig.KubeProxyConfiguration - expectedErrs field.ErrorList + mutateConfigFunc func(*kubeproxyconfig.KubeProxyConfiguration) + expectedErrs field.ErrorList }{ - "Mode specified, extra mode-specific configs": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "192.168.59.103", - HealthzBindAddress: "0.0.0.0:10256", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Mode: proxyMode, - IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{ - SyncPeriod: metav1.Duration{Duration: 10 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, - }, - }, "basic config, unspecified Mode": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "192.168.59.103", - HealthzBindAddress: "0.0.0.0:10256", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(_ *kubeproxyconfig.KubeProxyConfiguration) {}, + }, + "Mode specified, extra mode-specific configs": { + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + if runtime.GOOS == "windows" { + config.Mode = kubeproxyconfig.ProxyModeKernelspace + } else { + config.Mode = kubeproxyconfig.ProxyModeIPVS + config.IPVS = kubeproxyconfig.KubeProxyIPVSConfiguration{ + SyncPeriod: metav1.Duration{Duration: 10 * time.Second}, + MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second}, + } + } }, }, "empty HealthzBindAddress": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "192.168.59.103", - HealthzBindAddress: "", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.HealthzBindAddress = "" }, }, "IPv6": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "fd00:192:168:59::103", - HealthzBindAddress: "", - MetricsBindAddress: "[::1]:10249", - ClusterCIDR: "fd00:192:168:59::/64", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.BindAddress = "fd00:192:168:59::103" + config.HealthzBindAddress = "" + config.MetricsBindAddress = "[::1]:10249" + config.ClusterCIDR = "fd00:192:168:59::/64" }, }, "alternate healthz port": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.HealthzBindAddress = "0.0.0.0:12345" }, }, "ClusterCIDR is wrong IP family": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "fd00:192:168::/64", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.ClusterCIDR = "fd00:192:168::/64" }, }, "ClusterCIDR is dual-stack": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.ClusterCIDR = "192.168.59.0/24,fd00:192:168::/64" }, }, "LocalModeInterfaceNamePrefix": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: kubeproxyconfig.LocalModeInterfaceNamePrefix, - DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.DetectLocalMode = kubeproxyconfig.LocalModeInterfaceNamePrefix + config.DetectLocal = kubeproxyconfig.DetectLocalConfiguration{ InterfaceNamePrefix: "vethabcde", - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + } }, }, "LocalModeBridgeInterface": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: kubeproxyconfig.LocalModeBridgeInterface, - DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.DetectLocalMode = kubeproxyconfig.LocalModeBridgeInterface + config.DetectLocal = kubeproxyconfig.DetectLocalConfiguration{ BridgeInterface: "avz", - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + } }, }, "invalid BindAddress": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11:2000", - HealthzBindAddress: "0.0.0.0:10256", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.BindAddress = "10.10.12.11:2000" }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "10.10.12.11:2000", "not a valid textual representation of an IP address")}, }, "invalid HealthzBindAddress": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.HealthzBindAddress = "0.0.0.0" }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("HealthzBindAddress"), "0.0.0.0", "must be IP:port")}, }, "invalid MetricsBindAddress": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.MetricsBindAddress = "127.0.0.1" }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("MetricsBindAddress"), "127.0.0.1", "must be IP:port")}, }, "ClusterCIDR missing subset range": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.ClusterCIDR = "192.168.59.0" }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ClusterCIDR"), "192.168.59.0", "must be a valid CIDR block (e.g. 10.100.0.0/16 or fde4:8dba:82e1::/48)")}, }, "Invalid number of ClusterCIDRs": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64,10.0.0.0/16", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.ClusterCIDR = "192.168.59.0/24,fd00:192:168::/64,10.0.0.0/16" }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ClusterCIDR"), "192.168.59.0/24,fd00:192:168::/64,10.0.0.0/16", "only one CIDR allowed or a valid DualStack CIDR (e.g. 10.100.0.0/16,fde4:8dba:82e1::/48)")}, }, "ConfigSyncPeriod must be > 0": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: -1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.ConfigSyncPeriod = metav1.Duration{Duration: -1 * time.Second} }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ConfigSyncPeriod"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than 0")}, }, "IPVS mode selected without providing required SyncPeriod": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "192.168.59.103", - HealthzBindAddress: "0.0.0.0:10256", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - // not specifying valid period in IPVS mode. - Mode: kubeproxyconfig.ProxyModeIPVS, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.Mode = kubeproxyconfig.ProxyModeIPVS }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("KubeProxyIPVSConfiguration.SyncPeriod"), metav1.Duration{Duration: 0}, "must be greater than 0")}, }, "interfacePrefix is empty": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: kubeproxyconfig.LocalModeInterfaceNamePrefix, - DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.DetectLocalMode = kubeproxyconfig.LocalModeInterfaceNamePrefix + config.DetectLocal = kubeproxyconfig.DetectLocalConfiguration{ InterfaceNamePrefix: "", - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + } }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("InterfacePrefix"), "", "must not be empty")}, }, "bridgeInterfaceName is empty": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: kubeproxyconfig.LocalModeBridgeInterface, - DetectLocal: kubeproxyconfig.DetectLocalConfiguration{ + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.DetectLocalMode = kubeproxyconfig.LocalModeBridgeInterface + config.DetectLocal = kubeproxyconfig.DetectLocalConfiguration{ InterfaceNamePrefix: "eth0", // we won't care about prefix since mode is not prefix - }, - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + } }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("InterfaceName"), "", "must not be empty")}, }, "invalid DetectLocalMode": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - DetectLocalMode: "Guess", - Logging: logsapi.LoggingConfiguration{ - Format: "text", - }, + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.DetectLocalMode = "Guess" }, expectedErrs: field.ErrorList{field.NotSupported(newPath.Child("DetectLocalMode"), "Guess", []string{"ClusterCIDR", "NodeCIDR", "BridgeInterface", "InterfaceNamePrefix", ""})}, }, "invalid logging format": { - config: kubeproxyconfig.KubeProxyConfiguration{ - BindAddress: "10.10.12.11", - HealthzBindAddress: "0.0.0.0:12345", - MetricsBindAddress: "127.0.0.1:10249", - ClusterCIDR: "192.168.59.0/24", - ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second}, - IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{ - MasqueradeAll: true, - SyncPeriod: metav1.Duration{Duration: 5 * time.Second}, - MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second}, - }, - Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{ - MaxPerCore: ptr.To[int32](1), - Min: ptr.To[int32](1), - TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, - TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, - }, - Logging: logsapi.LoggingConfiguration{ + mutateConfigFunc: func(config *kubeproxyconfig.KubeProxyConfiguration) { + config.Logging = logsapi.LoggingConfiguration{ Format: "unsupported format", - }, + } }, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("logging.format"), "unsupported format", "Unsupported log format")}, }, } { - if runtime.GOOS == "windows" && testCase.config.Mode == kubeproxyconfig.ProxyModeIPVS { - // IPVS is not supported on Windows. - t.Log("Skipping test on Windows: ", name) - continue - } t.Run(name, func(t *testing.T) { - errs := Validate(&testCase.config) + config := baseConfig.DeepCopy() + testCase.mutateConfigFunc(config) + errs := Validate(config) if len(testCase.expectedErrs) == 0 { assert.Equal(t, field.ErrorList{}, errs, "expected no validation errors") } else {