iptables: simplify version handling

This commit is contained in:
Dan Winship 2019-07-16 17:11:02 -04:00
parent a735c97356
commit 81cd27a51e
5 changed files with 48 additions and 116 deletions

View File

@ -52,10 +52,6 @@ func NewFakeIPTables() *fakeIPTables {
} }
} }
func (f *fakeIPTables) GetVersion() (string, error) {
return "1.4.21", nil
}
func (f *fakeIPTables) getTable(tableName utiliptables.Table) (*fakeTable, error) { func (f *fakeIPTables) getTable(tableName utiliptables.Table) (*fakeTable, error) {
table, ok := f.tables[string(tableName)] table, ok := f.tables[string(tableName)]
if !ok { if !ok {

View File

@ -45,6 +45,7 @@ go_test(
"@io_bazel_rules_go//go/platform:linux": [ "@io_bazel_rules_go//go/platform:linux": [
"//pkg/util/dbus:go_default_library", "//pkg/util/dbus:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library", "//vendor/k8s.io/utils/exec/testing:go_default_library",
], ],

View File

@ -43,8 +43,6 @@ const (
// An injectable interface for running iptables commands. Implementations must be goroutine-safe. // An injectable interface for running iptables commands. Implementations must be goroutine-safe.
type Interface interface { type Interface interface {
// GetVersion returns the "X.Y.Z" version string for iptables.
GetVersion() (string, error)
// EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true. // EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true.
EnsureChain(table Table, chain Chain) (bool, error) EnsureChain(table Table, chain Chain) (bool, error)
// FlushChain clears the specified chain. If the chain did not exist, return error. // FlushChain clears the specified chain. If the chain did not exist, return error.
@ -121,12 +119,13 @@ const NoFlushTables FlushFlag = false
// Versions of iptables less than this do not support the -C / --check flag // Versions of iptables less than this do not support the -C / --check flag
// (test whether a rule exists). // (test whether a rule exists).
const MinCheckVersion = "1.4.11" var MinCheckVersion = utilversion.MustParseGeneric("1.4.11")
// Minimum iptables versions supporting the -w and -w<seconds> flags // Minimum iptables versions supporting the -w and -w<seconds> flags
const WaitMinVersion = "1.4.20" var WaitMinVersion = utilversion.MustParseGeneric("1.4.20")
const WaitSecondsMinVersion = "1.4.22" var WaitSecondsMinVersion = utilversion.MustParseGeneric("1.4.22")
const WaitRestoreMinVersion = "1.6.2" var WaitRestoreMinVersion = utilversion.MustParseGeneric("1.6.2")
const WaitString = "-w" const WaitString = "-w"
const WaitSecondsValue = "5" const WaitSecondsValue = "5"
@ -151,10 +150,10 @@ type runner struct {
// newInternal returns a new Interface which will exec iptables, and allows the // newInternal returns a new Interface which will exec iptables, and allows the
// caller to change the iptables-restore lockfile path // caller to change the iptables-restore lockfile path
func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol, lockfilePath string) Interface { func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol, lockfilePath string) Interface {
vstring, err := getIPTablesVersionString(exec, protocol) version, err := getIPTablesVersion(exec, protocol)
if err != nil { if err != nil {
klog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err) klog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err)
vstring = MinCheckVersion version = MinCheckVersion
} }
if lockfilePath == "" { if lockfilePath == "" {
@ -165,10 +164,10 @@ func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Prot
exec: exec, exec: exec,
dbus: dbus, dbus: dbus,
protocol: protocol, protocol: protocol,
hasCheck: getIPTablesHasCheckCommand(vstring), hasCheck: version.AtLeast(MinCheckVersion),
hasListener: false, hasListener: false,
waitFlag: getIPTablesWaitFlag(vstring), waitFlag: getIPTablesWaitFlag(version),
restoreWaitFlag: getIPTablesRestoreWaitFlag(vstring), restoreWaitFlag: getIPTablesRestoreWaitFlag(version),
lockfilePath: lockfilePath, lockfilePath: lockfilePath,
} }
return runner return runner
@ -215,11 +214,6 @@ func (runner *runner) connectToFirewallD() {
go runner.dbusSignalHandler(bus) go runner.dbusSignalHandler(bus)
} }
// GetVersion returns the version string.
func (runner *runner) GetVersion() (string, error) {
return getIPTablesVersionString(runner.exec, runner.protocol)
}
// EnsureChain is part of Interface. // EnsureChain is part of Interface.
func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) { func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) {
fullArgs := makeFullArgs(table, chain) fullArgs := makeFullArgs(table, chain)
@ -540,83 +534,46 @@ func makeFullArgs(table Table, chain Chain, args ...string) []string {
return append([]string{string(chain), "-t", string(table)}, args...) return append([]string{string(chain), "-t", string(table)}, args...)
} }
// Checks if iptables has the "-C" flag // getIPTablesVersion runs "iptables --version" and parses the returned version
func getIPTablesHasCheckCommand(vstring string) bool { func getIPTablesVersion(exec utilexec.Interface, protocol Protocol) (*utilversion.Version, error) {
minVersion, err := utilversion.ParseGeneric(MinCheckVersion)
if err != nil {
klog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err)
return true
}
version, err := utilversion.ParseGeneric(vstring)
if err != nil {
klog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
return true
}
return version.AtLeast(minVersion)
}
// Checks if iptables version has a "wait" flag
func getIPTablesWaitFlag(vstring string) []string {
version, err := utilversion.ParseGeneric(vstring)
if err != nil {
klog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
return nil
}
minVersion, err := utilversion.ParseGeneric(WaitMinVersion)
if err != nil {
klog.Errorf("WaitMinVersion (%s) is not a valid version string: %v", WaitMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return nil
}
minVersion, err = utilversion.ParseGeneric(WaitSecondsMinVersion)
if err != nil {
klog.Errorf("WaitSecondsMinVersion (%s) is not a valid version string: %v", WaitSecondsMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return []string{WaitString}
}
return []string{WaitString, WaitSecondsValue}
}
// getIPTablesVersionString runs "iptables --version" to get the version string
// in the form "X.X.X"
func getIPTablesVersionString(exec utilexec.Interface, protocol Protocol) (string, error) {
// this doesn't access mutable state so we don't need to use the interface / runner // this doesn't access mutable state so we don't need to use the interface / runner
iptablesCmd := iptablesCommand(protocol) iptablesCmd := iptablesCommand(protocol)
bytes, err := exec.Command(iptablesCmd, "--version").CombinedOutput() bytes, err := exec.Command(iptablesCmd, "--version").CombinedOutput()
if err != nil { if err != nil {
return "", err return nil, err
} }
versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)") versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)")
match := versionMatcher.FindStringSubmatch(string(bytes)) match := versionMatcher.FindStringSubmatch(string(bytes))
if match == nil { if match == nil {
return "", fmt.Errorf("no iptables version found in string: %s", bytes) return nil, fmt.Errorf("no iptables version found in string: %s", bytes)
}
version, err := utilversion.ParseGeneric(match[1])
if err != nil {
return nil, fmt.Errorf("iptables version %q is not a valid version string: %v", match[1], err)
}
return version, nil
}
// Checks if iptables version has a "wait" flag
func getIPTablesWaitFlag(version *utilversion.Version) []string {
switch {
case version.AtLeast(WaitSecondsMinVersion):
return []string{WaitString, WaitSecondsValue}
case version.AtLeast(WaitMinVersion):
return []string{WaitString}
default:
return nil
} }
return match[1], nil
} }
// Checks if iptables-restore has a "wait" flag // Checks if iptables-restore has a "wait" flag
func getIPTablesRestoreWaitFlag(vstring string) []string { func getIPTablesRestoreWaitFlag(version *utilversion.Version) []string {
version, err := utilversion.ParseGeneric(vstring) if version.AtLeast(WaitRestoreMinVersion) {
if err != nil { return []string{WaitString, WaitSecondsValue}
klog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) } else {
return nil return nil
} }
minVersion, err := utilversion.ParseGeneric(WaitRestoreMinVersion)
if err != nil {
klog.Errorf("WaitRestoreMinVersion (%s) is not a valid version string: %v", WaitRestoreMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return nil
}
return []string{WaitString, WaitSecondsValue}
} }
// goroutine to listen for D-Bus signals // goroutine to listen for D-Bus signals

View File

@ -29,6 +29,7 @@ import (
"time" "time"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilversion "k8s.io/apimachinery/pkg/util/version"
"k8s.io/kubernetes/pkg/util/dbus" "k8s.io/kubernetes/pkg/util/dbus"
"k8s.io/utils/exec" "k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing" fakeexec "k8s.io/utils/exec/testing"
@ -52,14 +53,11 @@ func testIPTablesVersionCmds(t *testing.T, protocol Protocol) {
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{ CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// iptables version response (for runner instantiation) // iptables version response (for runner instantiation)
func() ([]byte, error) { return []byte(iptablesCmd + version), nil }, func() ([]byte, error) { return []byte(iptablesCmd + version), nil },
// iptables version response (for call to runner.GetVersion())
func() ([]byte, error) { return []byte(iptablesCmd + version), nil },
}, },
} }
fexec := fakeexec.FakeExec{ fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{ CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
}, },
} }
runner := New(&fexec, dbus.NewFake(nil, nil), protocol) runner := New(&fexec, dbus.NewFake(nil, nil), protocol)
@ -69,16 +67,6 @@ func testIPTablesVersionCmds(t *testing.T, protocol Protocol) {
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll(iptablesCmd, "--version") { if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll(iptablesCmd, "--version") {
t.Errorf("%s runner instantiate: Expected cmd '%s --version', Got '%s'", protoStr, iptablesCmd, fcmd.CombinedOutputLog[0]) t.Errorf("%s runner instantiate: Expected cmd '%s --version', Got '%s'", protoStr, iptablesCmd, fcmd.CombinedOutputLog[0])
} }
_, err := runner.GetVersion()
if err != nil {
t.Errorf("%s GetVersion: Expected success, got %v", protoStr, err)
}
// Check that proper iptables version command was used for runner.GetVersion
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll(iptablesCmd, "--version") {
t.Errorf("%s GetVersion: Expected cmd '%s --version', Got '%s'", protoStr, iptablesCmd, fcmd.CombinedOutputLog[2])
}
} }
func TestIPTablesVersionCmdsIPv4(t *testing.T) { func TestIPTablesVersionCmdsIPv4(t *testing.T) {
@ -485,14 +473,13 @@ func TestDeleteRuleErrorDeleting(t *testing.T) {
func TestGetIPTablesHasCheckCommand(t *testing.T) { func TestGetIPTablesHasCheckCommand(t *testing.T) {
testCases := []struct { testCases := []struct {
Version string Version string
Err bool
Expected bool Expected bool
}{ }{
{"iptables v1.4.7", false, false}, {"iptables v1.4.7", false},
{"iptables v1.4.11", false, true}, {"iptables v1.4.11", true},
{"iptables v1.4.19.1", false, true}, {"iptables v1.4.19.1", true},
{"iptables v2.0.0", false, true}, {"iptables v2.0.0", true},
{"total junk", true, false}, {"total junk", true},
} }
for _, testCase := range testCases { for _, testCase := range testCases {
@ -506,15 +493,10 @@ func TestGetIPTablesHasCheckCommand(t *testing.T) {
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
}, },
} }
version, err := getIPTablesVersionString(&fexec, ProtocolIpv4) ipt := New(&fexec, nil, ProtocolIpv4)
if (err != nil) != testCase.Err { runner := ipt.(*runner)
t.Errorf("Expected error: %v, Got error: %v", testCase.Err, err) if testCase.Expected != runner.hasCheck {
} t.Errorf("Expected result: %v, Got result: %v", testCase.Expected, runner.hasCheck)
if err == nil {
check := getIPTablesHasCheckCommand(version)
if testCase.Expected != check {
t.Errorf("Expected result: %v, Got result: %v", testCase.Expected, check)
}
} }
} }
} }
@ -645,7 +627,7 @@ func TestIPTablesWaitFlag(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
result := getIPTablesWaitFlag(testCase.Version) result := getIPTablesWaitFlag(utilversion.MustParseGeneric(testCase.Version))
if !reflect.DeepEqual(result, testCase.Result) { if !reflect.DeepEqual(result, testCase.Result) {
t.Errorf("For %s expected %v got %v", testCase.Version, testCase.Result, result) t.Errorf("For %s expected %v got %v", testCase.Version, testCase.Result, result)
} }

View File

@ -48,10 +48,6 @@ func NewFake() *FakeIPTables {
return &FakeIPTables{} return &FakeIPTables{}
} }
func (*FakeIPTables) GetVersion() (string, error) {
return "0.0.0", nil
}
func (*FakeIPTables) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) { func (*FakeIPTables) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) {
return true, nil return true, nil
} }