Skip to content

Commit 90ce2d5

Browse files
committed
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.
1 parent a9a68c5 commit 90ce2d5

File tree

7 files changed

+275
-96
lines changed

7 files changed

+275
-96
lines changed

cmd/kube-proxy/app/server_others_test.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func Test_getProxyMode(t *testing.T) {
3232
iptablesVersion string
3333
ipsetVersion string
3434
kmods []string
35+
kernelVersion string
3536
kernelCompat bool
3637
iptablesError error
3738
ipsetError error
@@ -85,23 +86,33 @@ func Test_getProxyMode(t *testing.T) {
8586
kernelCompat: true,
8687
expected: proxyModeIPTables,
8788
},
88-
{ // flag says ipvs, ipset version ok, kernel modules installed
89-
flag: "ipvs",
90-
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
91-
ipsetVersion: ipvs.MinIPSetCheckVersion,
92-
expected: proxyModeIPVS,
89+
{ // flag says ipvs, ipset version ok, kernel modules installed for linux kernel before 4.19
90+
flag: "ipvs",
91+
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
92+
kernelVersion: "4.18",
93+
ipsetVersion: ipvs.MinIPSetCheckVersion,
94+
expected: proxyModeIPVS,
95+
},
96+
{ // flag says ipvs, ipset version ok, kernel modules installed for linux kernel 4.19
97+
flag: "ipvs",
98+
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
99+
kernelVersion: "4.19",
100+
ipsetVersion: ipvs.MinIPSetCheckVersion,
101+
expected: proxyModeIPVS,
93102
},
94103
{ // flag says ipvs, ipset version too low, fallback on iptables mode
95104
flag: "ipvs",
96-
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
105+
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
106+
kernelVersion: "4.19",
97107
ipsetVersion: "0.0",
98108
iptablesVersion: iptables.MinCheckVersion,
99109
kernelCompat: true,
100110
expected: proxyModeIPTables,
101111
},
102112
{ // flag says ipvs, bad ipset version, fallback on iptables mode
103113
flag: "ipvs",
104-
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
114+
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
115+
kernelVersion: "4.19",
105116
ipsetVersion: "a.b.c",
106117
iptablesVersion: iptables.MinCheckVersion,
107118
kernelCompat: true,
@@ -110,6 +121,7 @@ func Test_getProxyMode(t *testing.T) {
110121
{ // flag says ipvs, required kernel modules are not installed, fallback on iptables mode
111122
flag: "ipvs",
112123
kmods: []string{"foo", "bar", "baz"},
124+
kernelVersion: "4.19",
113125
ipsetVersion: ipvs.MinIPSetCheckVersion,
114126
iptablesVersion: iptables.MinCheckVersion,
115127
kernelCompat: true,
@@ -118,14 +130,25 @@ func Test_getProxyMode(t *testing.T) {
118130
{ // flag says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode
119131
flag: "ipvs",
120132
kmods: []string{"foo", "bar", "baz"},
133+
kernelVersion: "4.19",
134+
ipsetVersion: ipvs.MinIPSetCheckVersion,
135+
iptablesVersion: "0.0.0",
136+
kernelCompat: true,
137+
expected: proxyModeUserspace,
138+
},
139+
{ // flag says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode
140+
flag: "ipvs",
141+
kmods: []string{"foo", "bar", "baz"},
142+
kernelVersion: "4.19",
121143
ipsetVersion: ipvs.MinIPSetCheckVersion,
122144
iptablesVersion: "0.0.0",
123145
kernelCompat: true,
124146
expected: proxyModeUserspace,
125147
},
126148
{ // flag says ipvs, ipset version too low, iptables version too old, kernel not compatible, fallback on userspace mode
127149
flag: "ipvs",
128-
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
150+
kmods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack"},
151+
kernelVersion: "4.19",
129152
ipsetVersion: "0.0",
130153
iptablesVersion: iptables.MinCheckVersion,
131154
kernelCompat: false,
@@ -136,7 +159,10 @@ func Test_getProxyMode(t *testing.T) {
136159
versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError}
137160
kcompater := &fakeKernelCompatTester{c.kernelCompat}
138161
ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError}
139-
khandler := &fakeKernelHandler{c.kmods}
162+
khandler := &fakeKernelHandler{
163+
modules: c.kmods,
164+
kernelVersion: c.kernelVersion,
165+
}
140166
r := getProxyMode(c.flag, versioner, khandler, ipsetver, kcompater)
141167
if r != c.expected {
142168
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)

cmd/kube-proxy/app/server_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,18 @@ func (fake *fakeKernelCompatTester) IsCompatible() error {
7373

7474
// fakeKernelHandler implements KernelHandler.
7575
type fakeKernelHandler struct {
76-
modules []string
76+
modules []string
77+
kernelVersion string
7778
}
7879

7980
func (fake *fakeKernelHandler) GetModules() ([]string, error) {
8081
return fake.modules, nil
8182
}
8283

84+
func (fake *fakeKernelHandler) GetKernelVersion() (string, error) {
85+
return fake.kernelVersion, nil
86+
}
87+
8388
// This test verifies that NewProxyServer does not crash when CleanupAndExit is true.
8489
func TestProxyServerWithCleanupAndExit(t *testing.T) {
8590
// Each bind address below is a separate test case

pkg/proxy/ipvs/proxier.go

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,24 @@ package ipvs
1919
import (
2020
"bytes"
2121
"fmt"
22+
"io"
2223
"io/ioutil"
2324
"net"
25+
"os"
2426
"regexp"
2527
"strconv"
2628
"strings"
2729
"sync"
2830
"sync/atomic"
2931
"time"
3032

31-
"k8s.io/klog"
32-
3333
v1 "k8s.io/api/core/v1"
3434
"k8s.io/apimachinery/pkg/types"
3535
"k8s.io/apimachinery/pkg/util/sets"
36+
"k8s.io/apimachinery/pkg/util/version"
3637
"k8s.io/apimachinery/pkg/util/wait"
3738
"k8s.io/client-go/tools/record"
39+
"k8s.io/klog"
3840
"k8s.io/kubernetes/pkg/proxy"
3941
"k8s.io/kubernetes/pkg/proxy/healthcheck"
4042
"k8s.io/kubernetes/pkg/proxy/metrics"
@@ -473,6 +475,7 @@ func newServiceInfo(port *v1.ServicePort, service *v1.Service, baseInfo *proxy.B
473475
// KernelHandler can handle the current installed kernel modules.
474476
type KernelHandler interface {
475477
GetModules() ([]string, error)
478+
GetKernelVersion() (string, error)
476479
}
477480

478481
// LinuxKernelHandler implements KernelHandler interface.
@@ -490,11 +493,17 @@ func NewLinuxKernelHandler() *LinuxKernelHandler {
490493
// GetModules returns all installed kernel modules.
491494
func (handle *LinuxKernelHandler) GetModules() ([]string, error) {
492495
// Check whether IPVS required kernel modules are built-in
493-
kernelVersion, ipvsModules, err := utilipvs.GetKernelVersionAndIPVSMods(handle.executor)
496+
kernelVersionStr, err := handle.GetKernelVersion()
494497
if err != nil {
495498
return nil, err
496499
}
497-
builtinModsFilePath := fmt.Sprintf("/lib/modules/%s/modules.builtin", kernelVersion)
500+
kernelVersion, err := version.ParseGeneric(kernelVersionStr)
501+
if err != nil {
502+
return nil, fmt.Errorf("error parseing kernel version %q: %v", kernelVersionStr, err)
503+
}
504+
ipvsModules := utilipvs.GetRequiredIPVSModules(kernelVersion)
505+
506+
builtinModsFilePath := fmt.Sprintf("/lib/modules/%s/modules.builtin", kernelVersionStr)
498507
b, err := ioutil.ReadFile(builtinModsFilePath)
499508
if err != nil {
500509
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) {
516525
}
517526

518527
// Find out loaded kernel modules
519-
out, err := handle.executor.Command("cut", "-f1", "-d", " ", "/proc/modules").CombinedOutput()
528+
modulesFile, err := os.Open("/proc/modules")
520529
if err != nil {
521530
return nil, err
522531
}
523532

524-
mods := strings.Split(string(out), "\n")
533+
mods, err := getFirstColumn(modulesFile)
534+
if err != nil {
535+
return nil, fmt.Errorf("failed to find loaded kernel modules: %v", err)
536+
}
537+
525538
return append(mods, bmods...), nil
526539
}
527540

541+
// getFirstColumn reads all the content from r into memory and return a
542+
// slice which consists of the first word from each line.
543+
func getFirstColumn(r io.Reader) ([]string, error) {
544+
b, err := ioutil.ReadAll(r)
545+
if err != nil {
546+
return nil, err
547+
}
548+
549+
lines := strings.Split(string(b), "\n")
550+
words := make([]string, 0, len(lines))
551+
for i := range lines {
552+
fields := strings.Fields(lines[i])
553+
if len(fields) > 0 {
554+
words = append(words, fields[0])
555+
}
556+
}
557+
return words, nil
558+
}
559+
560+
// GetKernelVersion returns currently running kernel version.
561+
func (handle *LinuxKernelHandler) GetKernelVersion() (string, error) {
562+
kernelVersionFile := "/proc/sys/kernel/osrelease"
563+
fileContent, err := ioutil.ReadFile(kernelVersionFile)
564+
if err != nil {
565+
return "", fmt.Errorf("error reading osrelease file %q: %v", kernelVersionFile, err)
566+
}
567+
568+
return strings.TrimSpace(string(fileContent)), nil
569+
}
570+
528571
// CanUseIPVSProxier returns true if we can use the ipvs Proxier.
529572
// This is determined by checking if all the required kernel modules can be loaded. It may
530573
// 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
534577
if err != nil {
535578
return false, fmt.Errorf("error getting installed ipvs required kernel modules: %v", err)
536579
}
537-
wantModules := sets.NewString()
538580
loadModules := sets.NewString()
539-
linuxKernelHandler := NewLinuxKernelHandler()
540-
_, ipvsModules, _ := utilipvs.GetKernelVersionAndIPVSMods(linuxKernelHandler.executor)
541-
wantModules.Insert(ipvsModules...)
542581
loadModules.Insert(mods...)
582+
583+
kernelVersionStr, err := handle.GetKernelVersion()
584+
if err != nil {
585+
return false, fmt.Errorf("error determining kernel version to find required kernel modules for ipvs support: %v", err)
586+
}
587+
kernelVersion, err := version.ParseGeneric(kernelVersionStr)
588+
if err != nil {
589+
return false, fmt.Errorf("error parseing kernel version %q: %v", kernelVersionStr, err)
590+
}
591+
mods = utilipvs.GetRequiredIPVSModules(kernelVersion)
592+
wantModules := sets.NewString()
593+
wantModules.Insert(mods...)
594+
543595
modules := wantModules.Difference(loadModules).UnsortedList()
544596
var missingMods []string
545597
ConntrackiMissingCounter := 0

0 commit comments

Comments
 (0)