Merge pull request #19611 from thockin/proxy-sysctl-decouple

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-02-06 23:01:48 -08:00
commit c26087db45
3 changed files with 85 additions and 23 deletions

View File

@ -188,7 +188,7 @@ func NewProxyServerDefault(config *options.ProxyServerConfig) (*ProxyServer, err
var proxier proxy.ProxyProvider var proxier proxy.ProxyProvider
var endpointsHandler proxyconfig.EndpointsConfigHandler var endpointsHandler proxyconfig.EndpointsConfigHandler
proxyMode := getProxyMode(string(config.Mode), client.Nodes(), hostname, iptInterface) proxyMode := getProxyMode(string(config.Mode), client.Nodes(), hostname, iptInterface, iptables.LinuxKernelCompatTester{})
if proxyMode == proxyModeIptables { if proxyMode == proxyModeIptables {
glog.V(2).Info("Using iptables Proxier.") glog.V(2).Info("Using iptables Proxier.")
proxierIptables, err := iptables.NewProxier(iptInterface, execer, config.IPTablesSyncPeriod.Duration, config.MasqueradeAll) proxierIptables, err := iptables.NewProxier(iptInterface, execer, config.IPTablesSyncPeriod.Duration, config.MasqueradeAll)
@ -308,28 +308,28 @@ type nodeGetter interface {
Get(hostname string) (*api.Node, error) Get(hostname string) (*api.Node, error)
} }
func getProxyMode(proxyMode string, client nodeGetter, hostname string, iptver iptables.IptablesVersioner) string { func getProxyMode(proxyMode string, client nodeGetter, hostname string, iptver iptables.IptablesVersioner, kcompat iptables.KernelCompatTester) string {
if proxyMode == proxyModeUserspace { if proxyMode == proxyModeUserspace {
return proxyModeUserspace return proxyModeUserspace
} else if proxyMode == proxyModeIptables { } else if proxyMode == proxyModeIptables {
return tryIptablesProxy(iptver) return tryIptablesProxy(iptver, kcompat)
} else if proxyMode != "" { } else if proxyMode != "" {
glog.V(1).Infof("Flag proxy-mode=%q unknown, assuming iptables proxy", proxyMode) glog.V(1).Infof("Flag proxy-mode=%q unknown, assuming iptables proxy", proxyMode)
return tryIptablesProxy(iptver) return tryIptablesProxy(iptver, kcompat)
} }
// proxyMode == "" - choose the best option. // proxyMode == "" - choose the best option.
if client == nil { if client == nil {
glog.Errorf("nodeGetter is nil: assuming iptables proxy") glog.Errorf("nodeGetter is nil: assuming iptables proxy")
return tryIptablesProxy(iptver) return tryIptablesProxy(iptver, kcompat)
} }
node, err := client.Get(hostname) node, err := client.Get(hostname)
if err != nil { if err != nil {
glog.Errorf("Can't get Node %q, assuming iptables proxy: %v", hostname, err) glog.Errorf("Can't get Node %q, assuming iptables proxy: %v", hostname, err)
return tryIptablesProxy(iptver) return tryIptablesProxy(iptver, kcompat)
} }
if node == nil { if node == nil {
glog.Errorf("Got nil Node %q, assuming iptables proxy: %v", hostname) glog.Errorf("Got nil Node %q, assuming iptables proxy: %v", hostname)
return tryIptablesProxy(iptver) return tryIptablesProxy(iptver, kcompat)
} }
proxyMode, found := node.Annotations[betaProxyModeAnnotation] proxyMode, found := node.Annotations[betaProxyModeAnnotation]
if found { if found {
@ -345,13 +345,13 @@ func getProxyMode(proxyMode string, client nodeGetter, hostname string, iptver i
glog.V(1).Infof("Annotation demands userspace proxy") glog.V(1).Infof("Annotation demands userspace proxy")
return proxyModeUserspace return proxyModeUserspace
} }
return tryIptablesProxy(iptver) return tryIptablesProxy(iptver, kcompat)
} }
func tryIptablesProxy(iptver iptables.IptablesVersioner) string { func tryIptablesProxy(iptver iptables.IptablesVersioner, kcompat iptables.KernelCompatTester) string {
var err error var err error
// guaranteed false on error, error only necessary for debugging // guaranteed false on error, error only necessary for debugging
useIptablesProxy, err := iptables.CanUseIptablesProxier(iptver) useIptablesProxy, err := iptables.CanUseIptablesProxier(iptver, kcompat)
if err != nil { if err != nil {
glog.Errorf("Can't determine whether to use iptables proxy, using userspace proxier: %v", err) glog.Errorf("Can't determine whether to use iptables proxy, using userspace proxier: %v", err)
return proxyModeUserspace return proxyModeUserspace

View File

@ -45,6 +45,17 @@ func (fake *fakeIptablesVersioner) GetVersion() (string, error) {
return fake.version, fake.err return fake.version, fake.err
} }
type fakeKernelCompatTester struct {
ok bool
}
func (fake *fakeKernelCompatTester) IsCompatible() error {
if !fake.ok {
return fmt.Errorf("error")
}
return nil
}
func Test_getProxyMode(t *testing.T) { func Test_getProxyMode(t *testing.T) {
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
t.Skip("skipping on non-Linux") t.Skip("skipping on non-Linux")
@ -54,6 +65,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey string annotationKey string
annotationVal string annotationVal string
iptablesVersion string iptablesVersion string
kernelCompat bool
iptablesError error iptablesError error
expected string expected string
}{ }{
@ -71,9 +83,16 @@ func Test_getProxyMode(t *testing.T) {
iptablesVersion: "0.0.0", iptablesVersion: "0.0.0",
expected: proxyModeUserspace, expected: proxyModeUserspace,
}, },
{ // flag says iptables, version ok { // flag says iptables, version ok, kernel not compatible
flag: "iptables", flag: "iptables",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel is compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // detect, error { // detect, error
@ -86,9 +105,16 @@ func Test_getProxyMode(t *testing.T) {
iptablesVersion: "0.0.0", iptablesVersion: "0.0.0",
expected: proxyModeUserspace, expected: proxyModeUserspace,
}, },
{ // detect, version ok { // detect, version ok, kernel not compatible
flag: "", flag: "",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // annotation says userspace { // annotation says userspace
@ -111,11 +137,20 @@ func Test_getProxyMode(t *testing.T) {
iptablesVersion: "0.0.0", iptablesVersion: "0.0.0",
expected: proxyModeUserspace, expected: proxyModeUserspace,
}, },
{ // annotation says iptables, version ok { // annotation says iptables, version ok, kernel not compatible
flag: "", flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode", annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "iptables", annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // annotation says iptables, version ok, kernel is compatible
flag: "",
annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // annotation says something else, version ok { // annotation says something else, version ok
@ -123,6 +158,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey: "net.experimental.kubernetes.io/proxy-mode", annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "other", annotationVal: "other",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // annotation says nothing, version ok { // annotation says nothing, version ok
@ -130,6 +166,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey: "net.experimental.kubernetes.io/proxy-mode", annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "", annotationVal: "",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // annotation says userspace { // annotation says userspace
@ -152,11 +189,20 @@ func Test_getProxyMode(t *testing.T) {
iptablesVersion: "0.0.0", iptablesVersion: "0.0.0",
expected: proxyModeUserspace, expected: proxyModeUserspace,
}, },
{ // annotation says iptables, version ok { // annotation says iptables, version ok, kernel not compatible
flag: "", flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode", annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "iptables", annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // annotation says iptables, version ok, kernel is compatible
flag: "",
annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // annotation says something else, version ok { // annotation says something else, version ok
@ -164,6 +210,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey: "net.beta.kubernetes.io/proxy-mode", annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "other", annotationVal: "other",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // annotation says nothing, version ok { // annotation says nothing, version ok
@ -171,6 +218,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey: "net.beta.kubernetes.io/proxy-mode", annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "", annotationVal: "",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // flag says userspace, annotation disagrees { // flag says userspace, annotation disagrees
@ -185,6 +233,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey: "net.experimental.kubernetes.io/proxy-mode", annotationKey: "net.experimental.kubernetes.io/proxy-mode",
annotationVal: "userspace", annotationVal: "userspace",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
{ // flag says userspace, annotation disagrees { // flag says userspace, annotation disagrees
@ -199,6 +248,7 @@ func Test_getProxyMode(t *testing.T) {
annotationKey: "net.beta.kubernetes.io/proxy-mode", annotationKey: "net.beta.kubernetes.io/proxy-mode",
annotationVal: "userspace", annotationVal: "userspace",
iptablesVersion: iptables.MinCheckVersion, iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIptables, expected: proxyModeIptables,
}, },
} }
@ -206,7 +256,8 @@ func Test_getProxyMode(t *testing.T) {
getter := &fakeNodeInterface{} getter := &fakeNodeInterface{}
getter.node.Annotations = map[string]string{c.annotationKey: c.annotationVal} getter.node.Annotations = map[string]string{c.annotationKey: c.annotationVal}
versioner := &fakeIptablesVersioner{c.iptablesVersion, c.iptablesError} versioner := &fakeIptablesVersioner{c.iptablesVersion, c.iptablesError}
r := getProxyMode(c.flag, getter, "host", versioner) kcompater := &fakeKernelCompatTester{c.kernelCompat}
r := getProxyMode(c.flag, getter, "host", versioner, 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

@ -68,12 +68,18 @@ type IptablesVersioner interface {
GetVersion() (string, error) GetVersion() (string, error)
} }
// KernelCompatTester tests whether the required kernel capabilities are
// present to run the iptables proxier.
type KernelCompatTester interface {
IsCompatible() error
}
// CanUseIptablesProxier returns true if we should use the iptables Proxier // CanUseIptablesProxier returns true if we should use the iptables Proxier
// instead of the "classic" userspace Proxier. This is determined by checking // instead of the "classic" userspace Proxier. This is determined by checking
// the iptables version and for the existence of kernel features. It may return // the iptables version and for the existence of kernel features. It may return
// an error if it fails to get the iptables version without error, in which // an error if it fails to get the iptables version without error, in which
// case it will also return false. // case it will also return false.
func CanUseIptablesProxier(iptver IptablesVersioner) (bool, error) { func CanUseIptablesProxier(iptver IptablesVersioner, kcompat KernelCompatTester) (bool, error) {
minVersion, err := semver.NewVersion(iptablesMinVersion) minVersion, err := semver.NewVersion(iptablesMinVersion)
if err != nil { if err != nil {
return false, err return false, err
@ -91,18 +97,23 @@ func CanUseIptablesProxier(iptver IptablesVersioner) (bool, error) {
return false, nil return false, nil
} }
// Check for the required sysctls. We don't care about the value, just // Check that the kernel supports what we need.
// that it exists. If this Proxier is chosen, we'll iniialize it as we if err := kcompat.IsCompatible(); err != nil {
// need.
// TODO: we should inject a sysctl.Interface like we do for iptables
_, err = utilsysctl.GetSysctl(sysctlRouteLocalnet)
if err != nil {
return false, err return false, err
} }
return true, nil return true, nil
} }
type LinuxKernelCompatTester struct{}
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)
return err
}
const sysctlRouteLocalnet = "net/ipv4/conf/all/route_localnet" const sysctlRouteLocalnet = "net/ipv4/conf/all/route_localnet"
const sysctlBridgeCallIptables = "net/bridge/bridge-nf-call-iptables" const sysctlBridgeCallIptables = "net/bridge/bridge-nf-call-iptables"