From 4cd6d34a0b1fe40d59bd8b2ef38350a982107d53 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 23 Mar 2017 11:54:34 -0500 Subject: [PATCH] util/iptables: check for and use new iptables-restore 'wait' argument iptables-restore did not previously perform any locking, meaning that when callers (like kube-proxy) asked iptables-restore to write large numbers of rules, the iptables-restore process might run in parallel with other 'iptables' invocations in kubelet (hostports), docker, and other software. This causes errors like: "CNI request failed with status 400: 'Failed to ensure that nat chain POSTROUTING jumps to MASQUERADE: error checking rule: exit status 4: iptables: Resource temporarily unavailable." or from Docker "Failed to allocate and map port 1095-1095: iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 1095 -j DNAT --to-destination 10.1.0.2:1095 ! -i lbr0: iptables: Resource temporarily unavailable.\n (exit status 4)" iptables-restore "wait" functionality was added in iptables git commit 999eaa241212d3952ddff39a99d0d55a74e3639e but is NOT YET in a released version of iptables. See also https://bugzilla.redhat.com/show_bug.cgi?id=1417234 --- pkg/util/iptables/iptables.go | 70 ++++-- pkg/util/iptables/iptables_test.go | 329 +++++++++++++++++++++-------- 2 files changed, 301 insertions(+), 98 deletions(-) diff --git a/pkg/util/iptables/iptables.go b/pkg/util/iptables/iptables.go index 1b09ff77203..c7166bc7ea3 100644 --- a/pkg/util/iptables/iptables.go +++ b/pkg/util/iptables/iptables.go @@ -124,12 +124,13 @@ const MinWait2Version = "1.4.22" // runner implements Interface in terms of exec("iptables"). type runner struct { - mu sync.Mutex - exec utilexec.Interface - dbus utildbus.Interface - protocol Protocol - hasCheck bool - waitFlag []string + mu sync.Mutex + exec utilexec.Interface + dbus utildbus.Interface + protocol Protocol + hasCheck bool + waitFlag []string + restoreWaitFlag []string reloadFuncs []func() signal chan *godbus.Signal @@ -142,12 +143,14 @@ func New(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol) In glog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err) vstring = MinCheckVersion } + runner := &runner{ - exec: exec, - dbus: dbus, - protocol: protocol, - hasCheck: getIPTablesHasCheckCommand(vstring), - waitFlag: getIPTablesWaitFlag(vstring), + exec: exec, + dbus: dbus, + protocol: protocol, + hasCheck: getIPTablesHasCheckCommand(vstring), + waitFlag: getIPTablesWaitFlag(vstring), + restoreWaitFlag: getIPTablesRestoreWaitFlag(exec), } runner.connectToFirewallD() return runner @@ -335,8 +338,9 @@ func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFla } // run the command and return the output or an error including the output and error - glog.V(4).Infof("running iptables-restore %v", args) - cmd := runner.exec.Command(cmdIPTablesRestore, args...) + fullArgs := append(runner.restoreWaitFlag, args...) + glog.V(4).Infof("running iptables-restore %v", fullArgs) + cmd := runner.exec.Command(cmdIPTablesRestore, fullArgs...) cmd.SetStdin(bytes.NewBuffer(data)) b, err := cmd.CombinedOutput() if err != nil { @@ -519,6 +523,46 @@ func getIPTablesVersionString(exec utilexec.Interface) (string, error) { return match[1], nil } +// Checks if iptables-restore has a "wait" flag +// --wait support landed in v1.6.1+ right before --version support, so +// any version of iptables-restore that supports --version will also +// support --wait +func getIPTablesRestoreWaitFlag(exec utilexec.Interface) []string { + vstring, err := getIPTablesRestoreVersionString(exec) + if err != nil || vstring == "" { + glog.V(3).Infof("couldn't get iptables-restore version; assuming it doesn't support --wait") + return nil + } + if _, err := utilversion.ParseGeneric(vstring); err != nil { + glog.V(3).Infof("couldn't parse iptables-restore version; assuming it doesn't support --wait") + return nil + } + + return []string{"--wait=2"} +} + +// getIPTablesRestoreVersionString runs "iptables-restore --version" to get the version string +// in the form "X.X.X" +func getIPTablesRestoreVersionString(exec utilexec.Interface) (string, error) { + // this doesn't access mutable state so we don't need to use the interface / runner + + // iptables-restore hasn't always had --version, and worse complains + // about unrecognized commands but doesn't exit when it gets them. + // Work around that by setting stdin to nothing so it exits immediately. + cmd := exec.Command(cmdIPTablesRestore, "--version") + cmd.SetStdin(bytes.NewReader([]byte{})) + bytes, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)") + match := versionMatcher.FindStringSubmatch(string(bytes)) + if match == nil { + return "", fmt.Errorf("no iptables version found in string: %s", bytes) + } + return match[1], nil +} + // goroutine to listen for D-Bus signals func (runner *runner) dbusSignalHandler(bus utildbus.Connection) { firewalld := bus.Object(firewalldName, firewalldPath) diff --git a/pkg/util/iptables/iptables_test.go b/pkg/util/iptables/iptables_test.go index 14ed4dedcb2..f65f14ddc70 100644 --- a/pkg/util/iptables/iptables_test.go +++ b/pkg/util/iptables/iptables_test.go @@ -41,6 +41,8 @@ func testEnsureChain(t *testing.T, protocol Protocol) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, // Exists. @@ -55,6 +57,7 @@ func testEnsureChain(t *testing.T, protocol Protocol) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), protocol) @@ -67,12 +70,12 @@ func testEnsureChain(t *testing.T, protocol Protocol) { if exists { t.Errorf("expected exists = false") } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } cmd := getIPTablesCommand(protocol) - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll(cmd, "-t", "nat", "-N", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll(cmd, "-t", "nat", "-N", "FOOBAR") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } // Exists. exists, err = runner.EnsureChain(TableNAT, Chain("FOOBAR")) @@ -102,6 +105,8 @@ func TestFlushChain(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, // Failure. @@ -113,6 +118,7 @@ func TestFlushChain(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -122,11 +128,11 @@ func TestFlushChain(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } // Failure. err = runner.FlushChain(TableNAT, Chain("FOOBAR")) @@ -140,6 +146,8 @@ func TestDeleteChain(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, // Failure. @@ -151,6 +159,7 @@ func TestDeleteChain(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -160,11 +169,11 @@ func TestDeleteChain(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-X", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-X", "FOOBAR") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } // Failure. err = runner.DeleteChain(TableNAT, Chain("FOOBAR")) @@ -178,6 +187,8 @@ func TestEnsureRuleAlreadyExists(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, }, @@ -186,6 +197,8 @@ func TestEnsureRuleAlreadyExists(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Success of that exec means "done". func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, @@ -199,11 +212,11 @@ func TestEnsureRuleAlreadyExists(t *testing.T) { if !exists { t.Errorf("expected exists = true") } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } } @@ -212,6 +225,8 @@ func TestEnsureRuleNew(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Status 1 on the first call. func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, // Success on the second call. @@ -222,6 +237,8 @@ func TestEnsureRuleNew(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Failure of that means create it. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, @@ -236,11 +253,11 @@ func TestEnsureRuleNew(t *testing.T) { if exists { t.Errorf("expected exists = false") } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 4 { + t.Errorf("expected 4 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) + if !sets.NewString(fcmd.CombinedOutputLog[3]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3]) } } @@ -249,6 +266,8 @@ func TestEnsureRuleErrorChecking(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Status 2 on the first call. func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} }, }, @@ -257,6 +276,8 @@ func TestEnsureRuleErrorChecking(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Failure of that means create it. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, @@ -267,8 +288,8 @@ func TestEnsureRuleErrorChecking(t *testing.T) { if err == nil { t.Errorf("expected failure") } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } } @@ -277,6 +298,8 @@ func TestEnsureRuleErrorCreating(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Status 1 on the first call. func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, // Status 1 on the second call. @@ -287,6 +310,8 @@ func TestEnsureRuleErrorCreating(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Failure of that means create it. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, @@ -298,8 +323,8 @@ func TestEnsureRuleErrorCreating(t *testing.T) { if err == nil { t.Errorf("expected failure") } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 4 { + t.Errorf("expected 4 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } } @@ -308,6 +333,8 @@ func TestDeleteRuleAlreadyExists(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Status 1 on the first call. func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, }, @@ -316,6 +343,8 @@ func TestDeleteRuleAlreadyExists(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Failure of that exec means "does not exist". func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, @@ -326,11 +355,11 @@ func TestDeleteRuleAlreadyExists(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } } @@ -339,6 +368,8 @@ func TestDeleteRuleNew(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success on the first call. func() ([]byte, error) { return []byte{}, nil }, // Success on the second call. @@ -349,6 +380,8 @@ func TestDeleteRuleNew(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Success of that means delete it. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, @@ -360,11 +393,11 @@ func TestDeleteRuleNew(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 4 { + t.Errorf("expected 4 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) + if !sets.NewString(fcmd.CombinedOutputLog[3]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3]) } } @@ -373,6 +406,8 @@ func TestDeleteRuleErrorChecking(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Status 2 on the first call. func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} }, }, @@ -381,6 +416,8 @@ func TestDeleteRuleErrorChecking(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Failure of that means create it. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, @@ -391,8 +428,8 @@ func TestDeleteRuleErrorChecking(t *testing.T) { if err == nil { t.Errorf("expected failure") } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } } @@ -401,6 +438,8 @@ func TestDeleteRuleErrorCreating(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success on the first call. func() ([]byte, error) { return []byte{}, nil }, // Status 1 on the second call. @@ -411,6 +450,8 @@ func TestDeleteRuleErrorCreating(t *testing.T) { CommandScript: []exec.FakeCommandAction{ // iptables version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, // The second Command() call is checking the rule. Success of that means delete it. func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, @@ -422,8 +463,8 @@ func TestDeleteRuleErrorCreating(t *testing.T) { if err == nil { t.Errorf("expected failure") } - if fcmd.CombinedOutputCalls != 3 { - t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 4 { + t.Errorf("expected 4 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } } @@ -573,12 +614,17 @@ func TestWaitFlagUnavailable(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.4.19"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, }, } fexec := exec.FakeExec{ CommandScript: []exec.FakeCommandAction{ + // iptables version check + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + // iptables-restore version check func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, @@ -589,11 +635,11 @@ func TestWaitFlagUnavailable(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if sets.NewString(fcmd.CombinedOutputLog[1]...).HasAny("-w", "-w2") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if sets.NewString(fcmd.CombinedOutputLog[2]...).HasAny("-w", "-w2") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } } @@ -602,6 +648,8 @@ func TestWaitFlagOld(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.4.20"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, }, @@ -610,6 +658,7 @@ func TestWaitFlagOld(t *testing.T) { CommandScript: []exec.FakeCommandAction{ func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -618,14 +667,14 @@ func TestWaitFlagOld(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-w") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-w") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } - if sets.NewString(fcmd.CombinedOutputLog[1]...).HasAny("-w2") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if sets.NewString(fcmd.CombinedOutputLog[2]...).HasAny("-w2") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } } @@ -634,6 +683,8 @@ func TestWaitFlagNew(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.4.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // Success. func() ([]byte, error) { return []byte{}, nil }, }, @@ -642,6 +693,7 @@ func TestWaitFlagNew(t *testing.T) { CommandScript: []exec.FakeCommandAction{ func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -650,14 +702,14 @@ func TestWaitFlagNew(t *testing.T) { if err != nil { t.Errorf("expected success, got %v", err) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-w2") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-w2") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } - if sets.NewString(fcmd.CombinedOutputLog[1]...).HasAny("-w") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if sets.NewString(fcmd.CombinedOutputLog[2]...).HasAny("-w") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } } @@ -673,6 +725,8 @@ func TestReload(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.4.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, // first reload // EnsureChain @@ -700,6 +754,7 @@ func TestReload(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } @@ -732,25 +787,25 @@ func TestReload(t *testing.T) { <-reloaded <-reloaded - if fcmd.CombinedOutputCalls != 4 { - t.Errorf("expected 4 CombinedOutput() calls total, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 5 { + t.Errorf("expected 5 CombinedOutput() calls total, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) - } - if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } - if !sets.NewString(fcmd.CombinedOutputLog[3]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { + if !sets.NewString(fcmd.CombinedOutputLog[3]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3]) } + if !sets.NewString(fcmd.CombinedOutputLog[4]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[4]) + } go func() { time.Sleep(time.Second / 100); reloaded <- true }() dbusConn.EmitSignal(firewalldName, firewalldPath, firewalldInterface, "DefaultZoneChanged", "public") dbusConn.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", "io.k8s.Something", "", ":1.1") <-reloaded - if fcmd.CombinedOutputCalls != 4 { + if fcmd.CombinedOutputCalls != 5 { t.Errorf("Incorrect signal caused a reload") } @@ -758,18 +813,18 @@ func TestReload(t *testing.T) { <-reloaded <-reloaded - if fcmd.CombinedOutputCalls != 7 { - t.Errorf("expected 7 CombinedOutput() calls total, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 8 { + t.Errorf("expected 8 CombinedOutput() calls total, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[4]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[4]) - } - if !sets.NewString(fcmd.CombinedOutputLog[5]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { + if !sets.NewString(fcmd.CombinedOutputLog[5]...).HasAll("iptables", "-t", "nat", "-N", "FOOBAR") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[5]) } - if !sets.NewString(fcmd.CombinedOutputLog[6]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { + if !sets.NewString(fcmd.CombinedOutputLog[6]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[6]) } + if !sets.NewString(fcmd.CombinedOutputLog[7]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[7]) + } } func TestSave(t *testing.T) { @@ -785,6 +840,8 @@ COMMIT CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, func() ([]byte, error) { return []byte(output), nil }, func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, }, @@ -794,6 +851,7 @@ COMMIT func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -808,11 +866,11 @@ COMMIT t.Errorf("expected output to be equal to mocked one, got %v", o) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables-save", "-t", "nat") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables-save", "-t", "nat") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } // Failure. @@ -835,6 +893,8 @@ COMMIT CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, func() ([]byte, error) { return []byte(output), nil }, func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, }, @@ -844,6 +904,7 @@ COMMIT func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -858,11 +919,11 @@ COMMIT t.Errorf("expected output to be equal to mocked one, got %v", o) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } - if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables-save") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables-save") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } // Failure. @@ -877,6 +938,8 @@ func TestRestore(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, func() ([]byte, error) { return []byte{}, nil }, func() ([]byte, error) { return []byte{}, nil }, func() ([]byte, error) { return []byte{}, nil }, @@ -892,6 +955,7 @@ func TestRestore(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -903,7 +967,7 @@ func TestRestore(t *testing.T) { t.Errorf("expected success, got %v", err) } - commandSet := sets.NewString(fcmd.CombinedOutputLog[1]...) + commandSet := sets.NewString(fcmd.CombinedOutputLog[2]...) if !commandSet.HasAll("iptables-restore", "-T", string(TableNAT), "--counters") || commandSet.HasAny("--noflush") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) } @@ -914,7 +978,7 @@ func TestRestore(t *testing.T) { t.Errorf("expected success, got %v", err) } - commandSet = sets.NewString(fcmd.CombinedOutputLog[2]...) + commandSet = sets.NewString(fcmd.CombinedOutputLog[3]...) if !commandSet.HasAll("iptables-restore", "-T", string(TableNAT)) || commandSet.HasAny("--noflush", "--counters") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } @@ -925,7 +989,7 @@ func TestRestore(t *testing.T) { t.Errorf("expected success, got %v", err) } - commandSet = sets.NewString(fcmd.CombinedOutputLog[3]...) + commandSet = sets.NewString(fcmd.CombinedOutputLog[4]...) if !commandSet.HasAll("iptables-restore", "-T", string(TableNAT), "--noflush", "--counters") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3]) } @@ -936,13 +1000,13 @@ func TestRestore(t *testing.T) { t.Errorf("expected success, got %v", err) } - commandSet = sets.NewString(fcmd.CombinedOutputLog[4]...) + commandSet = sets.NewString(fcmd.CombinedOutputLog[5]...) if !commandSet.HasAll("iptables-restore", "-T", string(TableNAT), "--noflush") || commandSet.HasAny("--counters") { t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[4]) } - if fcmd.CombinedOutputCalls != 5 { - t.Errorf("expected 5 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 6 { + t.Errorf("expected 6 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } // Failure. @@ -958,6 +1022,8 @@ func TestRestoreAll(t *testing.T) { CombinedOutputScript: []exec.FakeCombinedOutputAction{ // iptables version check func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, func() ([]byte, error) { return []byte{}, nil }, func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, }, @@ -967,6 +1033,7 @@ func TestRestoreAll(t *testing.T) { func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, }, } runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) @@ -977,13 +1044,105 @@ func TestRestoreAll(t *testing.T) { t.Errorf("expected success, got %v", err) } - commandSet := sets.NewString(fcmd.CombinedOutputLog[1]...) + commandSet := sets.NewString(fcmd.CombinedOutputLog[2]...) if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") { - t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1]) + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) } - if fcmd.CombinedOutputCalls != 2 { - t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + } + + // Failure. + err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters) + if err == nil { + t.Errorf("expected failure") + } +} + +// TestRestoreAllWait tests that the "wait" flag is passed to a compatible iptables-restore +func TestRestoreAllWait(t *testing.T) { + fcmd := exec.FakeCmd{ + CombinedOutputScript: []exec.FakeCombinedOutputAction{ + // iptables version check + func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("iptables-restore v1.9.22"), nil }, + func() ([]byte, error) { return []byte{}, nil }, + func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, + }, + } + fexec := exec.FakeExec{ + CommandScript: []exec.FakeCommandAction{ + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + }, + } + runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) + defer runner.Destroy() + + err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters) + if err != nil { + t.Errorf("expected success, got %v", err) + } + + commandSet := sets.NewString(fcmd.CombinedOutputLog[2]...) + if !commandSet.HasAll("iptables-restore", "--wait=2", "--counters", "--noflush") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) + } + + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) + } + + // Failure. + err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters) + if err == nil { + t.Errorf("expected failure") + } +} + +// TestRestoreAllWaitOldIptablesRestore tests that the "wait" flag is not passed +// to a in-compatible iptables-restore +func TestRestoreAllWaitOldIptablesRestore(t *testing.T) { + fcmd := exec.FakeCmd{ + CombinedOutputScript: []exec.FakeCombinedOutputAction{ + // iptables version check + func() ([]byte, error) { return []byte("iptables v1.9.22"), nil }, + // iptables-restore version check + func() ([]byte, error) { return []byte("unrecognized option: --version"), nil }, + func() ([]byte, error) { return []byte{}, nil }, + func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} }, + }, + } + fexec := exec.FakeExec{ + CommandScript: []exec.FakeCommandAction{ + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) }, + }, + } + runner := New(&fexec, dbus.NewFake(nil, nil), ProtocolIpv4) + defer runner.Destroy() + + err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters) + if err != nil { + t.Errorf("expected success, got %v", err) + } + + commandSet := sets.NewString(fcmd.CombinedOutputLog[2]...) + if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") { + t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) + } + if commandSet.HasAny("--wait=2") { + t.Errorf("wrong CombinedOutput() log (unexpected --wait=2 option), got %s", fcmd.CombinedOutputLog[2]) + } + + if fcmd.CombinedOutputCalls != 3 { + t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls) } // Failure.