Merge pull request #80636 from ebati/issue-80504-test-kubeproxy-mac

Add GetKernelVersion to ipvs.KernelHandler interface
This commit is contained in:
Kubernetes Prow Robot 2019-07-31 16:56:13 -07:00 committed by GitHub
commit 59142b231c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 275 additions and 96 deletions

View File

@ -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 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)

View File

@ -74,12 +74,17 @@ func (fake *fakeKernelCompatTester) IsCompatible() error {
// fakeKernelHandler implements KernelHandler.
type fakeKernelHandler struct {
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

View File

@ -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

View File

@ -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"
@ -91,12 +90,17 @@ func (fake *fakeHealthChecker) SyncEndpoints(newEndpoints map[types.NamespacedNa
// fakeKernelHandler implements KernelHandler.
type fakeKernelHandler struct {
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
@ -257,6 +261,7 @@ func TestCleanupLeftovers(t *testing.T) {
func TestCanUseIPVSProxier(t *testing.T) {
testCases := []struct {
mods []string
kernelVersion string
kernelErr error
ipsetVersion string
ipsetErr error
@ -265,6 +270,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 0, kernel error
{
mods: []string{"foo", "bar", "baz"},
kernelVersion: "4.19",
kernelErr: fmt.Errorf("oops"),
ipsetVersion: "0.0",
ok: false,
@ -272,6 +278,7 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 1, ipset error
{
mods: []string{"foo", "bar", "baz"},
kernelVersion: "4.19",
ipsetVersion: MinIPSetCheckVersion,
ipsetErr: fmt.Errorf("oops"),
ok: false,
@ -279,41 +286,53 @@ func TestCanUseIPVSProxier(t *testing.T) {
// case 2, missing required kernel modules and ipset version too low
{
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"},
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"},
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"},
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"},
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)
}
})
}
}

View File

@ -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": [],
}),

View File

@ -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
}

View File

@ -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)
}
})
}
}