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.
This commit is contained in:
Emrecan BATI 2019-07-26 15:47:40 +03:00
parent a9a68c5e44
commit 90ce2d50d3
7 changed files with 275 additions and 96 deletions

View File

@ -32,6 +32,7 @@ func Test_getProxyMode(t *testing.T) {
iptablesVersion string iptablesVersion string
ipsetVersion string ipsetVersion string
kmods []string kmods []string
kernelVersion string
kernelCompat bool kernelCompat bool
iptablesError error iptablesError error
ipsetError error ipsetError error
@ -85,15 +86,24 @@ func Test_getProxyMode(t *testing.T) {
kernelCompat: true, kernelCompat: true,
expected: proxyModeIPTables, 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", 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_ipv4"},
ipsetVersion: ipvs.MinIPSetCheckVersion, kernelVersion: "4.18",
expected: proxyModeIPVS, 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 says ipvs, ipset version too low, fallback on iptables mode
flag: "ipvs", 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", ipsetVersion: "0.0",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true, kernelCompat: true,
@ -101,7 +111,8 @@ func Test_getProxyMode(t *testing.T) {
}, },
{ // flag says ipvs, bad ipset version, fallback on iptables mode { // flag says ipvs, bad ipset version, fallback on iptables mode
flag: "ipvs", 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", ipsetVersion: "a.b.c",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true, 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 says ipvs, required kernel modules are not installed, fallback on iptables mode
flag: "ipvs", flag: "ipvs",
kmods: []string{"foo", "bar", "baz"}, kmods: []string{"foo", "bar", "baz"},
kernelVersion: "4.19",
ipsetVersion: ipvs.MinIPSetCheckVersion, ipsetVersion: ipvs.MinIPSetCheckVersion,
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true, 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 says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode
flag: "ipvs", flag: "ipvs",
kmods: []string{"foo", "bar", "baz"}, 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, ipsetVersion: ipvs.MinIPSetCheckVersion,
iptablesVersion: "0.0.0", iptablesVersion: "0.0.0",
kernelCompat: true, 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 says ipvs, ipset version too low, iptables version too old, kernel not compatible, fallback on userspace mode
flag: "ipvs", 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", ipsetVersion: "0.0",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false, kernelCompat: false,
@ -136,7 +159,10 @@ func Test_getProxyMode(t *testing.T) {
versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError} versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError}
kcompater := &fakeKernelCompatTester{c.kernelCompat} kcompater := &fakeKernelCompatTester{c.kernelCompat}
ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError} 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) r := getProxyMode(c.flag, versioner, khandler, ipsetver, kcompater)
if r != c.expected { if r != c.expected {
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r) t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)

View File

@ -73,13 +73,18 @@ func (fake *fakeKernelCompatTester) IsCompatible() error {
// fakeKernelHandler implements KernelHandler. // fakeKernelHandler implements KernelHandler.
type fakeKernelHandler struct { type fakeKernelHandler struct {
modules []string modules []string
kernelVersion string
} }
func (fake *fakeKernelHandler) GetModules() ([]string, error) { func (fake *fakeKernelHandler) GetModules() ([]string, error) {
return fake.modules, nil 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. // This test verifies that NewProxyServer does not crash when CleanupAndExit is true.
func TestProxyServerWithCleanupAndExit(t *testing.T) { func TestProxyServerWithCleanupAndExit(t *testing.T) {
// Each bind address below is a separate test case // Each bind address below is a separate test case

View File

@ -19,8 +19,10 @@ package ipvs
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -28,13 +30,13 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"k8s.io/klog"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/proxy" "k8s.io/kubernetes/pkg/proxy"
"k8s.io/kubernetes/pkg/proxy/healthcheck" "k8s.io/kubernetes/pkg/proxy/healthcheck"
"k8s.io/kubernetes/pkg/proxy/metrics" "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. // KernelHandler can handle the current installed kernel modules.
type KernelHandler interface { type KernelHandler interface {
GetModules() ([]string, error) GetModules() ([]string, error)
GetKernelVersion() (string, error)
} }
// LinuxKernelHandler implements KernelHandler interface. // LinuxKernelHandler implements KernelHandler interface.
@ -490,11 +493,17 @@ func NewLinuxKernelHandler() *LinuxKernelHandler {
// GetModules returns all installed kernel modules. // GetModules returns all installed kernel modules.
func (handle *LinuxKernelHandler) GetModules() ([]string, error) { func (handle *LinuxKernelHandler) GetModules() ([]string, error) {
// Check whether IPVS required kernel modules are built-in // Check whether IPVS required kernel modules are built-in
kernelVersion, ipvsModules, err := utilipvs.GetKernelVersionAndIPVSMods(handle.executor) kernelVersionStr, err := handle.GetKernelVersion()
if err != nil { if err != nil {
return nil, err 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) b, err := ioutil.ReadFile(builtinModsFilePath)
if err != nil { 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) 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 // 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 { if err != nil {
return nil, err 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 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. // 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 // 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 // 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 { if err != nil {
return false, fmt.Errorf("error getting installed ipvs required kernel modules: %v", err) return false, fmt.Errorf("error getting installed ipvs required kernel modules: %v", err)
} }
wantModules := sets.NewString()
loadModules := sets.NewString() loadModules := sets.NewString()
linuxKernelHandler := NewLinuxKernelHandler()
_, ipvsModules, _ := utilipvs.GetKernelVersionAndIPVSMods(linuxKernelHandler.executor)
wantModules.Insert(ipvsModules...)
loadModules.Insert(mods...) 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() modules := wantModules.Difference(loadModules).UnsortedList()
var missingMods []string var missingMods []string
ConntrackiMissingCounter := 0 ConntrackiMissingCounter := 0

View File

@ -24,15 +24,12 @@ import (
"strings" "strings"
"testing" "testing"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr" "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/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/proxy"
netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing" netlinktest "k8s.io/kubernetes/pkg/proxy/ipvs/testing"
utilproxy "k8s.io/kubernetes/pkg/proxy/util" utilproxy "k8s.io/kubernetes/pkg/proxy/util"
proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing" proxyutiltest "k8s.io/kubernetes/pkg/proxy/util/testing"
@ -42,6 +39,8 @@ import (
iptablestest "k8s.io/kubernetes/pkg/util/iptables/testing" iptablestest "k8s.io/kubernetes/pkg/util/iptables/testing"
utilipvs "k8s.io/kubernetes/pkg/util/ipvs" utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
ipvstest "k8s.io/kubernetes/pkg/util/ipvs/testing" ipvstest "k8s.io/kubernetes/pkg/util/ipvs/testing"
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
) )
const testHostname = "test-hostname" const testHostname = "test-hostname"
@ -90,13 +89,18 @@ func (fake *fakeHealthChecker) SyncEndpoints(newEndpoints map[types.NamespacedNa
// fakeKernelHandler implements KernelHandler. // fakeKernelHandler implements KernelHandler.
type fakeKernelHandler struct { type fakeKernelHandler struct {
modules []string modules []string
kernelVersion string
} }
func (fake *fakeKernelHandler) GetModules() ([]string, error) { func (fake *fakeKernelHandler) GetModules() ([]string, error) {
return fake.modules, nil return fake.modules, nil
} }
func (fake *fakeKernelHandler) GetKernelVersion() (string, error) {
return fake.kernelVersion, nil
}
// fakeKernelHandler implements KernelHandler. // fakeKernelHandler implements KernelHandler.
type fakeIPSetVersioner struct { type fakeIPSetVersioner struct {
version string version string
@ -256,64 +260,79 @@ func TestCleanupLeftovers(t *testing.T) {
func TestCanUseIPVSProxier(t *testing.T) { func TestCanUseIPVSProxier(t *testing.T) {
testCases := []struct { testCases := []struct {
mods []string mods []string
kernelErr error kernelVersion string
ipsetVersion string kernelErr error
ipsetErr error ipsetVersion string
ok bool ipsetErr error
ok bool
}{ }{
// case 0, kernel error // case 0, kernel error
{ {
mods: []string{"foo", "bar", "baz"}, mods: []string{"foo", "bar", "baz"},
kernelErr: fmt.Errorf("oops"), kernelVersion: "4.19",
ipsetVersion: "0.0", kernelErr: fmt.Errorf("oops"),
ok: false, ipsetVersion: "0.0",
ok: false,
}, },
// case 1, ipset error // case 1, ipset error
{ {
mods: []string{"foo", "bar", "baz"}, mods: []string{"foo", "bar", "baz"},
ipsetVersion: MinIPSetCheckVersion, kernelVersion: "4.19",
ipsetErr: fmt.Errorf("oops"), ipsetVersion: MinIPSetCheckVersion,
ok: false, ipsetErr: fmt.Errorf("oops"),
ok: false,
}, },
// case 2, missing required kernel modules and ipset version too low // case 2, missing required kernel modules and ipset version too low
{ {
mods: []string{"foo", "bar", "baz"}, mods: []string{"foo", "bar", "baz"},
ipsetVersion: "1.1", kernelVersion: "4.19",
ok: false, ipsetVersion: "1.1",
ok: false,
}, },
// case 3, missing required ip_vs_* kernel modules // case 3, missing required ip_vs_* kernel modules
{ {
mods: []string{"ip_vs", "a", "bc", "def"}, mods: []string{"ip_vs", "a", "bc", "def"},
ipsetVersion: MinIPSetCheckVersion, kernelVersion: "4.19",
ok: false, ipsetVersion: MinIPSetCheckVersion,
ok: false,
}, },
// case 4, ipset version too low // 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"},
ipsetVersion: "4.3.0", kernelVersion: "4.19",
ok: false, 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"},
ipsetVersion: MinIPSetCheckVersion, kernelVersion: "4.19",
ok: true, 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"},
ipsetVersion: "6.19", kernelVersion: "4.18",
ok: true, 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 { 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} versioner := &fakeIPSetVersioner{version: testCases[i].ipsetVersion, err: testCases[i].ipsetErr}
ok, _ := CanUseIPVSProxier(handle, versioner) ok, err := CanUseIPVSProxier(handle, versioner)
if ok != testCases[i].ok { 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)) 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", "ipvs_test.go",
], ],
embed = [":go_default_library"], 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": [ "@io_bazel_rules_go//go/platform:linux": [
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library", "//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
], ],
@ -32,11 +34,41 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/util/ipvs", importpath = "k8s.io/kubernetes/pkg/util/ipvs",
deps = [ deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
] + select({ ] + 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": [ "@io_bazel_rules_go//go/platform:linux": [
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library", "//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
"//vendor/k8s.io/klog: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": [], "//conditions:default": [],
}), }),

View File

@ -19,11 +19,8 @@ package ipvs
import ( import (
"net" "net"
"strconv" "strconv"
"strings"
"fmt"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
"k8s.io/utils/exec"
) )
// Interface is an injectable interface for running ipvs commands. Implementations must be goroutine-safe. // Interface is an injectable interface for running ipvs commands. Implementations must be goroutine-safe.
@ -74,18 +71,18 @@ const (
// IPVS required kernel modules. // IPVS required kernel modules.
const ( const (
// ModIPVS is the kernel module "ip_vs" // KernelModuleIPVS is the kernel module "ip_vs"
ModIPVS string = "ip_vs" KernelModuleIPVS string = "ip_vs"
// ModIPVSRR is the kernel module "ip_vs_rr" // KernelModuleIPVSRR is the kernel module "ip_vs_rr"
ModIPVSRR string = "ip_vs_rr" KernelModuleIPVSRR string = "ip_vs_rr"
// ModIPVSWRR is the kernel module "ip_vs_wrr" // KernelModuleIPVSWRR is the kernel module "ip_vs_wrr"
ModIPVSWRR string = "ip_vs_wrr" KernelModuleIPVSWRR string = "ip_vs_wrr"
// ModIPVSSH is the kernel module "ip_vs_sh" // KernelModuleIPVSSH is the kernel module "ip_vs_sh"
ModIPVSSH string = "ip_vs_sh" KernelModuleIPVSSH string = "ip_vs_sh"
// ModNfConntrackIPV4 is the module "nf_conntrack_ipv4" // KernelModuleNfConntrackIPV4 is the module "nf_conntrack_ipv4"
ModNfConntrackIPV4 string = "nf_conntrack_ipv4" KernelModuleNfConntrackIPV4 string = "nf_conntrack_ipv4"
// ModNfConntrack is the kernel module "nf_conntrack" // KernelModuleNfConntrack is the kernel module "nf_conntrack"
ModNfConntrack string = "nf_conntrack" KernelModuleNfConntrack string = "nf_conntrack"
) )
// Equal check the equality of virtual server. // Equal check the equality of virtual server.
@ -123,28 +120,13 @@ func (rs *RealServer) Equal(other *RealServer) bool {
rs.Port == other.Port rs.Port == other.Port
} }
// GetKernelVersionAndIPVSMods returns the linux kernel version and the required ipvs modules // GetRequiredIPVSModules returns the required ipvs modules for the given linux kernel version.
func GetKernelVersionAndIPVSMods(Executor exec.Interface) (kernelVersion string, ipvsModules []string, err error) { func GetRequiredIPVSModules(kernelVersion *version.Version) []string {
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)
}
// "nf_conntrack_ipv4" has been removed since v4.19 // "nf_conntrack_ipv4" has been removed since v4.19
// see https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f // see https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
ver2, _ := version.ParseGeneric("4.19") if kernelVersion.LessThan(version.MustParseGeneric("4.19")) {
// get required ipvs modules return []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrackIPV4}
if ver1.LessThan(ver2) {
ipvsModules = append(ipvsModules, ModIPVS, ModIPVSRR, ModIPVSWRR, ModIPVSSH, ModNfConntrackIPV4)
} else {
ipvsModules = append(ipvsModules, ModIPVS, ModIPVSRR, ModIPVSWRR, ModIPVSSH, ModNfConntrack)
} }
return []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrack}
return kernelVersion, ipvsModules, nil
} }

View File

@ -18,7 +18,11 @@ package ipvs
import ( import (
"net" "net"
"reflect"
"sort"
"testing" "testing"
"k8s.io/apimachinery/pkg/util/version"
) )
func TestVirtualServerEqual(t *testing.T) { 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)
}
})
}
}