diff --git a/cmd/kube-proxy/app/conntrack.go b/cmd/kube-proxy/app/conntrack.go index 001d9911193..cbd0f6297b0 100644 --- a/cmd/kube-proxy/app/conntrack.go +++ b/cmd/kube-proxy/app/conntrack.go @@ -38,7 +38,7 @@ var readOnlySysFSError = errors.New("ReadOnlySysFS") func (realConntracker) SetMax(max int) error { glog.Infof("Setting nf_conntrack_max to %d", max) - if err := sysctl.SetSysctl("net/netfilter/nf_conntrack_max", max); err != nil { + if err := sysctl.New().SetSysctl("net/netfilter/nf_conntrack_max", max); err != nil { return err } // sysfs is expected to be mounted as 'rw'. However, it may be unexpectedly mounted as @@ -60,7 +60,7 @@ func (realConntracker) SetMax(max int) error { func (realConntracker) SetTCPEstablishedTimeout(seconds int) error { glog.Infof("Setting nf_conntrack_tcp_timeout_established to %d", seconds) - return sysctl.SetSysctl("net/netfilter/nf_conntrack_tcp_timeout_established", seconds) + return sysctl.New().SetSysctl("net/netfilter/nf_conntrack_tcp_timeout_established", seconds) } // isSysFSWritable checks /proc/mounts to see whether sysfs is 'rw' or not. diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index d10d38905b4..965df692607 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -47,6 +47,7 @@ import ( nodeutil "k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/pkg/util/oom" "k8s.io/kubernetes/pkg/util/resourcecontainer" + utilsysctl "k8s.io/kubernetes/pkg/util/sysctl" "k8s.io/kubernetes/pkg/util/wait" "github.com/golang/glog" @@ -204,7 +205,7 @@ func NewProxyServerDefault(config *options.ProxyServerConfig) (*ProxyServer, err // IPTablesMasqueradeBit must be specified or defaulted. return nil, fmt.Errorf("Unable to read IPTablesMasqueradeBit from config") } - proxierIptables, err := iptables.NewProxier(iptInterface, execer, config.IPTablesSyncPeriod.Duration, config.MasqueradeAll, int(*config.IPTablesMasqueradeBit), config.ClusterCIDR, hostname, getNodeIP(client, hostname)) + proxierIptables, err := iptables.NewProxier(iptInterface, utilsysctl.New(), execer, config.IPTablesSyncPeriod.Duration, config.MasqueradeAll, int(*config.IPTablesMasqueradeBit), config.ClusterCIDR, hostname, getNodeIP(client, hostname)) if err != nil { glog.Fatalf("Unable to create proxier: %v", err) } diff --git a/pkg/kubelet/cm/container_manager_linux.go b/pkg/kubelet/cm/container_manager_linux.go index b5b6205fbdf..140433d1357 100644 --- a/pkg/kubelet/cm/container_manager_linux.go +++ b/pkg/kubelet/cm/container_manager_linux.go @@ -270,9 +270,11 @@ func setupKernelTunables(option KernelTunableBehavior) error { utilsysctl.KernelPanicOnOops: utilsysctl.KernelPanicOnOopsAlways, } + sysctl := utilsysctl.New() + errList := []error{} for flag, expectedValue := range desiredState { - val, err := utilsysctl.GetSysctl(flag) + val, err := sysctl.GetSysctl(flag) if err != nil { errList = append(errList, err) continue @@ -288,7 +290,7 @@ func setupKernelTunables(option KernelTunableBehavior) error { glog.V(2).Infof("Invalid kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val) case KernelTunableModify: glog.V(2).Infof("Updating kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val) - err = utilsysctl.SetSysctl(flag, expectedValue) + err = sysctl.SetSysctl(flag, expectedValue) if err != nil { errList = append(errList, err) } diff --git a/pkg/kubelet/network/kubenet/kubenet_linux.go b/pkg/kubelet/network/kubenet/kubenet_linux.go index 4a3ff9cdae8..c2a3a65e1df 100644 --- a/pkg/kubelet/network/kubenet/kubenet_linux.go +++ b/pkg/kubelet/network/kubenet/kubenet_linux.go @@ -71,6 +71,7 @@ type kubenetNetworkPlugin struct { hairpinMode componentconfig.HairpinMode hostportHandler hostport.HostportHandler iptables utiliptables.Interface + sysctl utilsysctl.Interface // vendorDir is passed by kubelet network-plugin-dir parameter. // kubenet will search for cni binaries in DefaultCNIDir first, then continue to vendorDir. vendorDir string @@ -115,7 +116,7 @@ func (plugin *kubenetNetworkPlugin) Init(host network.Host, hairpinMode componen // was built-in, we simply ignore the error here. A better thing to do is // to check the kernel version in the future. plugin.execer.Command("modprobe", "br-netfilter").CombinedOutput() - err := utilsysctl.SetSysctl(sysctlBridgeCallIptables, 1) + err := plugin.sysctl.SetSysctl(sysctlBridgeCallIptables, 1) if err != nil { glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err) } diff --git a/pkg/kubelet/network/plugins.go b/pkg/kubelet/network/plugins.go index e5045824cbe..39a78e8aa3b 100644 --- a/pkg/kubelet/network/plugins.go +++ b/pkg/kubelet/network/plugins.go @@ -166,7 +166,7 @@ func (plugin *NoopNetworkPlugin) Init(host Host, hairpinMode componentconfig.Hai // Ensure the netfilter module is loaded on kernel >= 3.18; previously // it was built-in. utilexec.New().Command("modprobe", "br-netfilter").CombinedOutput() - if err := utilsysctl.SetSysctl(sysctlBridgeCallIptables, 1); err != nil { + if err := utilsysctl.New().SetSysctl(sysctlBridgeCallIptables, 1); err != nil { glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err) } diff --git a/pkg/proxy/iptables/proxier.go b/pkg/proxy/iptables/proxier.go index 9fb1de57eee..e5465ce3281 100644 --- a/pkg/proxy/iptables/proxier.go +++ b/pkg/proxy/iptables/proxier.go @@ -123,7 +123,7 @@ func (lkct LinuxKernelCompatTester) IsCompatible() error { // Check for the required sysctls. We don't care about the value, just // that it exists. If this Proxier is chosen, we'll initialize it as we // need. - _, err := utilsysctl.GetSysctl(sysctlRouteLocalnet) + _, err := utilsysctl.New().GetSysctl(sysctlRouteLocalnet) return err } @@ -195,16 +195,16 @@ var _ proxy.ProxyProvider = &Proxier{} // An error will be returned if iptables fails to update or acquire the initial lock. // Once a proxier is created, it will keep iptables up to date in the background and // will not terminate if a particular iptables call fails. -func NewProxier(ipt utiliptables.Interface, exec utilexec.Interface, syncPeriod time.Duration, masqueradeAll bool, masqueradeBit int, clusterCIDR string, hostname string, nodeIP net.IP) (*Proxier, error) { +func NewProxier(ipt utiliptables.Interface, sysctl utilsysctl.Interface, exec utilexec.Interface, syncPeriod time.Duration, masqueradeAll bool, masqueradeBit int, clusterCIDR string, hostname string, nodeIP net.IP) (*Proxier, error) { // Set the route_localnet sysctl we need for - if err := utilsysctl.SetSysctl(sysctlRouteLocalnet, 1); err != nil { + if err := sysctl.SetSysctl(sysctlRouteLocalnet, 1); err != nil { return nil, fmt.Errorf("can't set sysctl %s: %v", sysctlRouteLocalnet, err) } // Proxy needs br_netfilter and bridge-nf-call-iptables=1 when containers // are connected to a Linux bridge (but not SDN bridges). Until most // plugins handle this, log when config is missing - if val, err := utilsysctl.GetSysctl(sysctlBridgeCallIptables); err == nil && val != 1 { + if val, err := sysctl.GetSysctl(sysctlBridgeCallIptables); err == nil && val != 1 { glog.Infof("missing br-netfilter module or unset sysctl br-nf-call-iptables; proxy may not work as intended") } diff --git a/pkg/util/sysctl/sysctl.go b/pkg/util/sysctl/sysctl.go index bbf52736c19..e58b99d3013 100644 --- a/pkg/util/sysctl/sysctl.go +++ b/pkg/util/sysctl/sysctl.go @@ -37,8 +37,25 @@ const ( KernelPanicRebootTimeout = 10 // seconds after a panic for the kernel to reboot ) +// An injectable interface for running sysctl commands. +type Interface interface { + // GetSysctl returns the value for the specified sysctl setting + GetSysctl(sysctl string) (int, error) + // SetSysctl modifies the specified sysctl flag to the new value + SetSysctl(sysctl string, newVal int) error +} + +// New returns a new Interface for accessing sysctl +func New() Interface { + return &procSysctl{} +} + +// procSysctl implements Interface by reading and writing files under /proc/sys +type procSysctl struct { +} + // GetSysctl returns the value for the specified sysctl setting -func GetSysctl(sysctl string) (int, error) { +func (_ *procSysctl) GetSysctl(sysctl string) (int, error) { data, err := ioutil.ReadFile(path.Join(sysctlBase, sysctl)) if err != nil { return -1, err @@ -51,6 +68,6 @@ func GetSysctl(sysctl string) (int, error) { } // SetSysctl modifies the specified sysctl flag to the new value -func SetSysctl(sysctl string, newVal int) error { +func (_ *procSysctl) SetSysctl(sysctl string, newVal int) error { return ioutil.WriteFile(path.Join(sysctlBase, sysctl), []byte(strconv.Itoa(newVal)), 0640) } diff --git a/pkg/util/sysctl/testing/fake.go b/pkg/util/sysctl/testing/fake.go new file mode 100644 index 00000000000..17b805ce4c8 --- /dev/null +++ b/pkg/util/sysctl/testing/fake.go @@ -0,0 +1,50 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "k8s.io/kubernetes/pkg/util/sysctl" + "os" +) + +// fake is a map-backed implementation of sysctl.Interface, for testing/mocking +type fake struct { + Settings map[string]int +} + +func NewFake() *fake { + return &fake{ + Settings: make(map[string]int), + } +} + +// GetSysctl returns the value for the specified sysctl setting +func (m *fake) GetSysctl(sysctl string) (int, error) { + v, found := m.Settings[sysctl] + if !found { + return -1, os.ErrNotExist + } + return v, nil +} + +// SetSysctl modifies the specified sysctl flag to the new value +func (m *fake) SetSysctl(sysctl string, newVal int) error { + m.Settings[sysctl] = newVal + return nil +} + +var _ = sysctl.Interface(&fake{})