From 90ce2d50d3ae654ff6f4c0c17b4f522240e8ae1b Mon Sep 17 00:00:00 2001 From: Emrecan BATI Date: Fri, 26 Jul 2019 15:47:40 +0300 Subject: [PATCH] Add GetKernelVersion to ipvs.KernelHandler interface ipvs `getProxyMode` test fails on mac as `utilipvs.GetRequiredIPVSMods` try to reach `/proc/sys/kernel/osrelease` to find version of the running linux kernel. Linux kernel version is used to determine the list of required kernel modules for ipvs. Logic to determine kernel version is moved to GetKernelVersion method in LinuxKernelHandler which implements ipvs.KernelHandler. Mock KernelHandler is used in the test cases. Read and parse file is converted to go function instead of execing cut. --- cmd/kube-proxy/app/server_others_test.go | 44 ++++++-- cmd/kube-proxy/app/server_test.go | 7 +- pkg/proxy/ipvs/proxier.go | 72 +++++++++++-- pkg/proxy/ipvs/proxier_test.go | 127 ++++++++++++++++------- pkg/util/ipvs/BUILD | 36 ++++++- pkg/util/ipvs/ipvs.go | 52 +++------- pkg/util/ipvs/ipvs_test.go | 33 ++++++ 7 files changed, 275 insertions(+), 96 deletions(-) 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) + } + }) + } +}