diff --git a/cmd/kube-proxy/app/server_others_test.go b/cmd/kube-proxy/app/server_others_test.go index 50eb6ff3817..a7b7f216bbf 100644 --- a/cmd/kube-proxy/app/server_others_test.go +++ b/cmd/kube-proxy/app/server_others_test.go @@ -32,6 +32,7 @@ func Test_getProxyMode(t *testing.T) { iptablesVersion string ipsetVersion string kmods []string + kernelVersion string kernelCompat bool iptablesError error ipsetError error @@ -85,15 +86,24 @@ func Test_getProxyMode(t *testing.T) { kernelCompat: true, expected: proxyModeIPTables, }, - { // flag says ipvs, ipset version ok, kernel modules installed - flag: "ipvs", - kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, - ipsetVersion: ipvs.MinIPSetCheckVersion, - expected: proxyModeIPVS, + { // flag says ipvs, ipset version ok, kernel modules installed for linux kernel before 4.19 + flag: "ipvs", + kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, + kernelVersion: "4.18", + ipsetVersion: ipvs.MinIPSetCheckVersion, + expected: proxyModeIPVS, + }, + { // flag says ipvs, ipset version ok, kernel modules installed for linux kernel 4.19 + flag: "ipvs", + kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, + kernelVersion: "4.19", + ipsetVersion: ipvs.MinIPSetCheckVersion, + expected: proxyModeIPVS, }, { // flag says ipvs, ipset version too low, fallback on iptables mode flag: "ipvs", - kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, + kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, + kernelVersion: "4.19", ipsetVersion: "0.0", iptablesVersion: iptables.MinCheckVersion, kernelCompat: true, @@ -101,7 +111,8 @@ func Test_getProxyMode(t *testing.T) { }, { // flag says ipvs, bad ipset version, fallback on iptables mode flag: "ipvs", - kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, + kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, + kernelVersion: "4.19", ipsetVersion: "a.b.c", iptablesVersion: iptables.MinCheckVersion, kernelCompat: true, @@ -110,6 +121,7 @@ func Test_getProxyMode(t *testing.T) { { // flag says ipvs, required kernel modules are not installed, fallback on iptables mode flag: "ipvs", kmods: []string{"foo", "bar", "baz"}, + kernelVersion: "4.19", ipsetVersion: ipvs.MinIPSetCheckVersion, iptablesVersion: iptables.MinCheckVersion, kernelCompat: true, @@ -118,6 +130,16 @@ func Test_getProxyMode(t *testing.T) { { // flag says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode flag: "ipvs", kmods: []string{"foo", "bar", "baz"}, + kernelVersion: "4.19", + ipsetVersion: ipvs.MinIPSetCheckVersion, + iptablesVersion: "0.0.0", + kernelCompat: true, + expected: proxyModeUserspace, + }, + { // flag says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode + flag: "ipvs", + kmods: []string{"foo", "bar", "baz"}, + kernelVersion: "4.19", ipsetVersion: ipvs.MinIPSetCheckVersion, iptablesVersion: "0.0.0", kernelCompat: true, @@ -125,7 +147,8 @@ func Test_getProxyMode(t *testing.T) { }, { // flag says ipvs, ipset version too low, iptables version too old, kernel not compatible, fallback on userspace mode flag: "ipvs", - kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, + kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, + kernelVersion: "4.19", ipsetVersion: "0.0", iptablesVersion: iptables.MinCheckVersion, kernelCompat: false, @@ -136,7 +159,10 @@ func Test_getProxyMode(t *testing.T) { versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError} kcompater := &fakeKernelCompatTester{c.kernelCompat} ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError} - khandler := &fakeKernelHandler{c.kmods} + khandler := &fakeKernelHandler{ + modules: c.kmods, + kernelVersion: c.kernelVersion, + } r := getProxyMode(c.flag, versioner, khandler, ipsetver, kcompater) if r != c.expected { t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r) diff --git a/cmd/kube-proxy/app/server_test.go b/cmd/kube-proxy/app/server_test.go index fec5f90a2e9..5b7671c3e13 100644 --- a/cmd/kube-proxy/app/server_test.go +++ b/cmd/kube-proxy/app/server_test.go @@ -73,13 +73,18 @@ func (fake *fakeKernelCompatTester) IsCompatible() error { // fakeKernelHandler implements KernelHandler. type fakeKernelHandler struct { - modules []string + modules []string + kernelVersion string } func (fake *fakeKernelHandler) GetModules() ([]string, error) { return fake.modules, nil } +func (fake *fakeKernelHandler) GetKernelVersion() (string, error) { + return fake.kernelVersion, nil +} + // This test verifies that NewProxyServer does not crash when CleanupAndExit is true. func TestProxyServerWithCleanupAndExit(t *testing.T) { // Each bind address below is a separate test case diff --git a/pkg/proxy/ipvs/proxier.go b/pkg/proxy/ipvs/proxier.go index 0c90ade252f..ba215ca18f7 100644 --- a/pkg/proxy/ipvs/proxier.go +++ b/pkg/proxy/ipvs/proxier.go @@ -19,8 +19,10 @@ package ipvs import ( "bytes" "fmt" + "io" "io/ioutil" "net" + "os" "regexp" "strconv" "strings" @@ -28,13 +30,13 @@ import ( "sync/atomic" "time" - "k8s.io/klog" - v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/record" + "k8s.io/klog" "k8s.io/kubernetes/pkg/proxy" "k8s.io/kubernetes/pkg/proxy/healthcheck" "k8s.io/kubernetes/pkg/proxy/metrics" @@ -473,6 +475,7 @@ func newServiceInfo(port *v1.ServicePort, service *v1.Service, baseInfo *proxy.B // KernelHandler can handle the current installed kernel modules. type KernelHandler interface { GetModules() ([]string, error) + GetKernelVersion() (string, error) } // LinuxKernelHandler implements KernelHandler interface. @@ -490,11 +493,17 @@ func NewLinuxKernelHandler() *LinuxKernelHandler { // GetModules returns all installed kernel modules. func (handle *LinuxKernelHandler) GetModules() ([]string, error) { // Check whether IPVS required kernel modules are built-in - kernelVersion, ipvsModules, err := utilipvs.GetKernelVersionAndIPVSMods(handle.executor) + kernelVersionStr, err := handle.GetKernelVersion() if err != nil { return nil, err } - builtinModsFilePath := fmt.Sprintf("/lib/modules/%s/modules.builtin", kernelVersion) + kernelVersion, err := version.ParseGeneric(kernelVersionStr) + if err != nil { + return nil, fmt.Errorf("error parseing kernel version %q: %v", kernelVersionStr, err) + } + ipvsModules := utilipvs.GetRequiredIPVSModules(kernelVersion) + + builtinModsFilePath := fmt.Sprintf("/lib/modules/%s/modules.builtin", kernelVersionStr) b, err := ioutil.ReadFile(builtinModsFilePath) if err != nil { klog.Warningf("Failed to read file %s with error %v. You can ignore this message when kube-proxy is running inside container without mounting /lib/modules", builtinModsFilePath, err) @@ -516,15 +525,49 @@ func (handle *LinuxKernelHandler) GetModules() ([]string, error) { } // Find out loaded kernel modules - out, err := handle.executor.Command("cut", "-f1", "-d", " ", "/proc/modules").CombinedOutput() + modulesFile, err := os.Open("/proc/modules") if err != nil { return nil, err } - mods := strings.Split(string(out), "\n") + mods, err := getFirstColumn(modulesFile) + if err != nil { + return nil, fmt.Errorf("failed to find loaded kernel modules: %v", err) + } + return append(mods, bmods...), nil } +// getFirstColumn reads all the content from r into memory and return a +// slice which consists of the first word from each line. +func getFirstColumn(r io.Reader) ([]string, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + lines := strings.Split(string(b), "\n") + words := make([]string, 0, len(lines)) + for i := range lines { + fields := strings.Fields(lines[i]) + if len(fields) > 0 { + words = append(words, fields[0]) + } + } + return words, nil +} + +// GetKernelVersion returns currently running kernel version. +func (handle *LinuxKernelHandler) GetKernelVersion() (string, error) { + kernelVersionFile := "/proc/sys/kernel/osrelease" + fileContent, err := ioutil.ReadFile(kernelVersionFile) + if err != nil { + return "", fmt.Errorf("error reading osrelease file %q: %v", kernelVersionFile, err) + } + + return strings.TrimSpace(string(fileContent)), nil +} + // CanUseIPVSProxier returns true if we can use the ipvs Proxier. // This is determined by checking if all the required kernel modules can be loaded. It may // return an error if it fails to get the kernel modules information without error, in which @@ -534,12 +577,21 @@ func CanUseIPVSProxier(handle KernelHandler, ipsetver IPSetVersioner) (bool, err if err != nil { return false, fmt.Errorf("error getting installed ipvs required kernel modules: %v", err) } - wantModules := sets.NewString() loadModules := sets.NewString() - linuxKernelHandler := NewLinuxKernelHandler() - _, ipvsModules, _ := utilipvs.GetKernelVersionAndIPVSMods(linuxKernelHandler.executor) - wantModules.Insert(ipvsModules...) loadModules.Insert(mods...) + + kernelVersionStr, err := handle.GetKernelVersion() + if err != nil { + return false, fmt.Errorf("error determining kernel version to find required kernel modules for ipvs support: %v", err) + } + kernelVersion, err := version.ParseGeneric(kernelVersionStr) + if err != nil { + return false, fmt.Errorf("error parseing kernel version %q: %v", kernelVersionStr, err) + } + mods = utilipvs.GetRequiredIPVSModules(kernelVersion) + wantModules := sets.NewString() + wantModules.Insert(mods...) + modules := wantModules.Difference(loadModules).UnsortedList() var missingMods []string ConntrackiMissingCounter := 0 diff --git a/pkg/proxy/ipvs/proxier_test.go b/pkg/proxy/ipvs/proxier_test.go index 2e46f18435e..46b4dafccd9 100644 --- a/pkg/proxy/ipvs/proxier_test.go +++ b/pkg/proxy/ipvs/proxier_test.go @@ -24,15 +24,12 @@ import ( "strings" "testing" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/kubernetes/pkg/proxy" - "k8s.io/utils/exec" - fakeexec "k8s.io/utils/exec/testing" - "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/kubernetes/pkg/proxy" netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing" utilproxy "k8s.io/kubernetes/pkg/proxy/util" proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing" @@ -42,6 +39,8 @@ import ( iptablestest "k8s.io/kubernetes/pkg/util/iptables/testing" utilipvs "k8s.io/kubernetes/pkg/util/ipvs" ipvstest "k8s.io/kubernetes/pkg/util/ipvs/testing" + "k8s.io/utils/exec" + fakeexec "k8s.io/utils/exec/testing" ) const testHostname = "test-hostname" @@ -90,13 +89,18 @@ func (fake *fakeHealthChecker) SyncEndpoints(newEndpoints map[types.NamespacedNa // fakeKernelHandler implements KernelHandler. type fakeKernelHandler struct { - modules []string + modules []string + kernelVersion string } func (fake *fakeKernelHandler) GetModules() ([]string, error) { return fake.modules, nil } +func (fake *fakeKernelHandler) GetKernelVersion() (string, error) { + return fake.kernelVersion, nil +} + // fakeKernelHandler implements KernelHandler. type fakeIPSetVersioner struct { version string @@ -256,64 +260,79 @@ func TestCleanupLeftovers(t *testing.T) { func TestCanUseIPVSProxier(t *testing.T) { testCases := []struct { - mods []string - kernelErr error - ipsetVersion string - ipsetErr error - ok bool + mods []string + kernelVersion string + kernelErr error + ipsetVersion string + ipsetErr error + ok bool }{ // case 0, kernel error { - mods: []string{"foo", "bar", "baz"}, - kernelErr: fmt.Errorf("oops"), - ipsetVersion: "0.0", - ok: false, + mods: []string{"foo", "bar", "baz"}, + kernelVersion: "4.19", + kernelErr: fmt.Errorf("oops"), + ipsetVersion: "0.0", + ok: false, }, // case 1, ipset error { - mods: []string{"foo", "bar", "baz"}, - ipsetVersion: MinIPSetCheckVersion, - ipsetErr: fmt.Errorf("oops"), - ok: false, + mods: []string{"foo", "bar", "baz"}, + kernelVersion: "4.19", + ipsetVersion: MinIPSetCheckVersion, + ipsetErr: fmt.Errorf("oops"), + ok: false, }, // case 2, missing required kernel modules and ipset version too low { - mods: []string{"foo", "bar", "baz"}, - ipsetVersion: "1.1", - ok: false, + mods: []string{"foo", "bar", "baz"}, + kernelVersion: "4.19", + ipsetVersion: "1.1", + ok: false, }, // case 3, missing required ip_vs_* kernel modules { - mods: []string{"ip_vs", "a", "bc", "def"}, - ipsetVersion: MinIPSetCheckVersion, - ok: false, + mods: []string{"ip_vs", "a", "bc", "def"}, + kernelVersion: "4.19", + ipsetVersion: MinIPSetCheckVersion, + ok: false, }, // case 4, ipset version too low { - mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, - ipsetVersion: "4.3.0", - ok: false, + mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, + kernelVersion: "4.19", + ipsetVersion: "4.3.0", + ok: false, }, - // case 5 + // case 5, ok for linux kernel 4.19 { - mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, - ipsetVersion: MinIPSetCheckVersion, - ok: true, + mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"}, + kernelVersion: "4.19", + ipsetVersion: MinIPSetCheckVersion, + ok: true, }, - // case 6 + // case 6, ok for linux kernel 4.18 { - mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4", "foo", "bar"}, - ipsetVersion: "6.19", - ok: true, + mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"}, + kernelVersion: "4.18", + ipsetVersion: MinIPSetCheckVersion, + ok: true, + }, + // case 7. ok when module list has extra modules + { + mods: []string{"foo", "ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack", "bar"}, + kernelVersion: "4.19", + ipsetVersion: "6.19", + ok: true, }, } for i := range testCases { - handle := &fakeKernelHandler{modules: testCases[i].mods} + handle := &fakeKernelHandler{modules: testCases[i].mods, kernelVersion: testCases[i].kernelVersion} versioner := &fakeIPSetVersioner{version: testCases[i].ipsetVersion, err: testCases[i].ipsetErr} - ok, _ := CanUseIPVSProxier(handle, versioner) + ok, err := CanUseIPVSProxier(handle, versioner) if ok != testCases[i].ok { - t.Errorf("Case [%d], expect %v, got %v", i, testCases[i].ok, ok) + t.Errorf("Case [%d], expect %v, got %v: err: %v", i, testCases[i].ok, ok, err) } } } @@ -3140,3 +3159,33 @@ func TestMultiPortServiceBindAddr(t *testing.T) { t.Errorf("Expected number of remaining bound addrs after cleanup to be %v. Got %v", 0, len(remainingAddrs)) } } + +func Test_getFirstColumn(t *testing.T) { + testCases := []struct { + name string + fileContent string + want []string + wantErr bool + }{ + { + name: "valid content", + fileContent: `libiscsi_tcp 28672 1 iscsi_tcp, Live 0xffffffffc07ae000 +libiscsi 57344 3 ib_iser,iscsi_tcp,libiscsi_tcp, Live 0xffffffffc079a000 +raid10 57344 0 - Live 0xffffffffc0597000`, + want: []string{"libiscsi_tcp", "libiscsi", "raid10"}, + wantErr: false, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + got, err := getFirstColumn(strings.NewReader(test.fileContent)) + if (err != nil) != test.wantErr { + t.Errorf("getFirstColumn() error = %v, wantErr %v", err, test.wantErr) + return + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("getFirstColumn() = %v, want %v", got, test.want) + } + }) + } +} diff --git a/pkg/util/ipvs/BUILD b/pkg/util/ipvs/BUILD index 12f84bd5363..70a51121552 100644 --- a/pkg/util/ipvs/BUILD +++ b/pkg/util/ipvs/BUILD @@ -13,7 +13,9 @@ go_test( "ipvs_test.go", ], embed = [":go_default_library"], - deps = select({ + deps = [ + "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", + ] + select({ "@io_bazel_rules_go//go/platform:linux": [ "//vendor/github.com/docker/libnetwork/ipvs:go_default_library", ], @@ -32,11 +34,41 @@ go_library( importpath = "k8s.io/kubernetes/pkg/util/ipvs", deps = [ "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", - "//vendor/k8s.io/utils/exec:go_default_library", ] + select({ + "@io_bazel_rules_go//go/platform:android": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], "@io_bazel_rules_go//go/platform:linux": [ "//vendor/github.com/docker/libnetwork/ipvs:go_default_library", "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:nacl": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/k8s.io/utils/exec:go_default_library", ], "//conditions:default": [], }), diff --git a/pkg/util/ipvs/ipvs.go b/pkg/util/ipvs/ipvs.go index 6cd081851ce..5e7bacc8c6b 100644 --- a/pkg/util/ipvs/ipvs.go +++ b/pkg/util/ipvs/ipvs.go @@ -19,11 +19,8 @@ package ipvs import ( "net" "strconv" - "strings" - "fmt" "k8s.io/apimachinery/pkg/util/version" - "k8s.io/utils/exec" ) // Interface is an injectable interface for running ipvs commands. Implementations must be goroutine-safe. @@ -74,18 +71,18 @@ const ( // IPVS required kernel modules. const ( - // ModIPVS is the kernel module "ip_vs" - ModIPVS string = "ip_vs" - // ModIPVSRR is the kernel module "ip_vs_rr" - ModIPVSRR string = "ip_vs_rr" - // ModIPVSWRR is the kernel module "ip_vs_wrr" - ModIPVSWRR string = "ip_vs_wrr" - // ModIPVSSH is the kernel module "ip_vs_sh" - ModIPVSSH string = "ip_vs_sh" - // ModNfConntrackIPV4 is the module "nf_conntrack_ipv4" - ModNfConntrackIPV4 string = "nf_conntrack_ipv4" - // ModNfConntrack is the kernel module "nf_conntrack" - ModNfConntrack string = "nf_conntrack" + // KernelModuleIPVS is the kernel module "ip_vs" + KernelModuleIPVS string = "ip_vs" + // KernelModuleIPVSRR is the kernel module "ip_vs_rr" + KernelModuleIPVSRR string = "ip_vs_rr" + // KernelModuleIPVSWRR is the kernel module "ip_vs_wrr" + KernelModuleIPVSWRR string = "ip_vs_wrr" + // KernelModuleIPVSSH is the kernel module "ip_vs_sh" + KernelModuleIPVSSH string = "ip_vs_sh" + // KernelModuleNfConntrackIPV4 is the module "nf_conntrack_ipv4" + KernelModuleNfConntrackIPV4 string = "nf_conntrack_ipv4" + // KernelModuleNfConntrack is the kernel module "nf_conntrack" + KernelModuleNfConntrack string = "nf_conntrack" ) // Equal check the equality of virtual server. @@ -123,28 +120,13 @@ func (rs *RealServer) Equal(other *RealServer) bool { rs.Port == other.Port } -// GetKernelVersionAndIPVSMods returns the linux kernel version and the required ipvs modules -func GetKernelVersionAndIPVSMods(Executor exec.Interface) (kernelVersion string, ipvsModules []string, err error) { - kernelVersionFile := "/proc/sys/kernel/osrelease" - out, err := Executor.Command("cut", "-f1", "-d", " ", kernelVersionFile).CombinedOutput() - if err != nil { - return "", nil, fmt.Errorf("error getting os release kernel version: %v(%s)", err, out) - } - kernelVersion = strings.TrimSpace(string(out)) - // parse kernel version - ver1, err := version.ParseGeneric(kernelVersion) - if err != nil { - return kernelVersion, nil, fmt.Errorf("error parsing kernel version: %v(%s)", err, kernelVersion) - } +// GetRequiredIPVSModules returns the required ipvs modules for the given linux kernel version. +func GetRequiredIPVSModules(kernelVersion *version.Version) []string { // "nf_conntrack_ipv4" has been removed since v4.19 // see https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f - ver2, _ := version.ParseGeneric("4.19") - // get required ipvs modules - if ver1.LessThan(ver2) { - ipvsModules = append(ipvsModules, ModIPVS, ModIPVSRR, ModIPVSWRR, ModIPVSSH, ModNfConntrackIPV4) - } else { - ipvsModules = append(ipvsModules, ModIPVS, ModIPVSRR, ModIPVSWRR, ModIPVSSH, ModNfConntrack) + if kernelVersion.LessThan(version.MustParseGeneric("4.19")) { + return []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrackIPV4} } + return []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrack} - return kernelVersion, ipvsModules, nil } diff --git a/pkg/util/ipvs/ipvs_test.go b/pkg/util/ipvs/ipvs_test.go index 63db2591cca..408966818f3 100644 --- a/pkg/util/ipvs/ipvs_test.go +++ b/pkg/util/ipvs/ipvs_test.go @@ -18,7 +18,11 @@ package ipvs import ( "net" + "reflect" + "sort" "testing" + + "k8s.io/apimachinery/pkg/util/version" ) func TestVirtualServerEqual(t *testing.T) { @@ -380,3 +384,32 @@ func TestFrontendDestinationString(t *testing.T) { } } } + +func TestGetRequiredIPVSModules(t *testing.T) { + Tests := []struct { + name string + kernelVersion *version.Version + want []string + }{ + { + name: "kernel version < 4.19", + kernelVersion: version.MustParseGeneric("4.18"), + want: []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrackIPV4}, + }, + { + name: "kernel version 4.19", + kernelVersion: version.MustParseGeneric("4.19"), + want: []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrack}, + }, + } + for _, test := range Tests { + t.Run(test.name, func(t *testing.T) { + got := GetRequiredIPVSModules(test.kernelVersion) + sort.Strings(got) + sort.Strings(test.want) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("GetRequiredIPVSMods() = %v for kenel version: %s, want %v", got, test.kernelVersion, test.want) + } + }) + } +}