Create testable implementation of sysctl

This is so we can test kubenet Init, which calls sysctl
This commit is contained in:
Justin Santa Barbara 2016-08-10 11:38:44 -04:00
parent 5d25bffffe
commit 2c103af2b6
8 changed files with 84 additions and 13 deletions

View File

@ -38,7 +38,7 @@ var readOnlySysFSError = errors.New("ReadOnlySysFS")
func (realConntracker) SetMax(max int) error { func (realConntracker) SetMax(max int) error {
glog.Infof("Setting nf_conntrack_max to %d", max) 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 return err
} }
// sysfs is expected to be mounted as 'rw'. However, it may be unexpectedly mounted as // 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 { func (realConntracker) SetTCPEstablishedTimeout(seconds int) error {
glog.Infof("Setting nf_conntrack_tcp_timeout_established to %d", seconds) 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. // isSysFSWritable checks /proc/mounts to see whether sysfs is 'rw' or not.

View File

@ -47,6 +47,7 @@ import (
nodeutil "k8s.io/kubernetes/pkg/util/node" nodeutil "k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/pkg/util/oom" "k8s.io/kubernetes/pkg/util/oom"
"k8s.io/kubernetes/pkg/util/resourcecontainer" "k8s.io/kubernetes/pkg/util/resourcecontainer"
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
"k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/pkg/util/wait"
"github.com/golang/glog" "github.com/golang/glog"
@ -204,7 +205,7 @@ func NewProxyServerDefault(config *options.ProxyServerConfig) (*ProxyServer, err
// IPTablesMasqueradeBit must be specified or defaulted. // IPTablesMasqueradeBit must be specified or defaulted.
return nil, fmt.Errorf("Unable to read IPTablesMasqueradeBit from config") 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 { if err != nil {
glog.Fatalf("Unable to create proxier: %v", err) glog.Fatalf("Unable to create proxier: %v", err)
} }

View File

@ -270,9 +270,11 @@ func setupKernelTunables(option KernelTunableBehavior) error {
utilsysctl.KernelPanicOnOops: utilsysctl.KernelPanicOnOopsAlways, utilsysctl.KernelPanicOnOops: utilsysctl.KernelPanicOnOopsAlways,
} }
sysctl := utilsysctl.New()
errList := []error{} errList := []error{}
for flag, expectedValue := range desiredState { for flag, expectedValue := range desiredState {
val, err := utilsysctl.GetSysctl(flag) val, err := sysctl.GetSysctl(flag)
if err != nil { if err != nil {
errList = append(errList, err) errList = append(errList, err)
continue 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) glog.V(2).Infof("Invalid kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val)
case KernelTunableModify: case KernelTunableModify:
glog.V(2).Infof("Updating kernel flag: %v, expected value: %v, actual value: %v", flag, expectedValue, val) 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 { if err != nil {
errList = append(errList, err) errList = append(errList, err)
} }

View File

@ -71,6 +71,7 @@ type kubenetNetworkPlugin struct {
hairpinMode componentconfig.HairpinMode hairpinMode componentconfig.HairpinMode
hostportHandler hostport.HostportHandler hostportHandler hostport.HostportHandler
iptables utiliptables.Interface iptables utiliptables.Interface
sysctl utilsysctl.Interface
// vendorDir is passed by kubelet network-plugin-dir parameter. // vendorDir is passed by kubelet network-plugin-dir parameter.
// kubenet will search for cni binaries in DefaultCNIDir first, then continue to vendorDir. // kubenet will search for cni binaries in DefaultCNIDir first, then continue to vendorDir.
vendorDir string 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 // was built-in, we simply ignore the error here. A better thing to do is
// to check the kernel version in the future. // to check the kernel version in the future.
plugin.execer.Command("modprobe", "br-netfilter").CombinedOutput() plugin.execer.Command("modprobe", "br-netfilter").CombinedOutput()
err := utilsysctl.SetSysctl(sysctlBridgeCallIptables, 1) err := plugin.sysctl.SetSysctl(sysctlBridgeCallIptables, 1)
if err != nil { if err != nil {
glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err) glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err)
} }

View File

@ -166,7 +166,7 @@ func (plugin *NoopNetworkPlugin) Init(host Host, hairpinMode componentconfig.Hai
// Ensure the netfilter module is loaded on kernel >= 3.18; previously // Ensure the netfilter module is loaded on kernel >= 3.18; previously
// it was built-in. // it was built-in.
utilexec.New().Command("modprobe", "br-netfilter").CombinedOutput() 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) glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err)
} }

View File

@ -123,7 +123,7 @@ func (lkct LinuxKernelCompatTester) IsCompatible() error {
// Check for the required sysctls. We don't care about the value, just // 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 // that it exists. If this Proxier is chosen, we'll initialize it as we
// need. // need.
_, err := utilsysctl.GetSysctl(sysctlRouteLocalnet) _, err := utilsysctl.New().GetSysctl(sysctlRouteLocalnet)
return err 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. // 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 // 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. // 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 // 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) return nil, fmt.Errorf("can't set sysctl %s: %v", sysctlRouteLocalnet, err)
} }
// Proxy needs br_netfilter and bridge-nf-call-iptables=1 when containers // Proxy needs br_netfilter and bridge-nf-call-iptables=1 when containers
// are connected to a Linux bridge (but not SDN bridges). Until most // are connected to a Linux bridge (but not SDN bridges). Until most
// plugins handle this, log when config is missing // 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") glog.Infof("missing br-netfilter module or unset sysctl br-nf-call-iptables; proxy may not work as intended")
} }

View File

@ -37,8 +37,25 @@ const (
KernelPanicRebootTimeout = 10 // seconds after a panic for the kernel to reboot 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 // 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)) data, err := ioutil.ReadFile(path.Join(sysctlBase, sysctl))
if err != nil { if err != nil {
return -1, err return -1, err
@ -51,6 +68,6 @@ func GetSysctl(sysctl string) (int, error) {
} }
// SetSysctl modifies the specified sysctl flag to the new value // 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) return ioutil.WriteFile(path.Join(sysctlBase, sysctl), []byte(strconv.Itoa(newVal)), 0640)
} }

View File

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