...

Source file src/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go

Documentation: k8s.io/kubernetes/pkg/util/iptables

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2014 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package iptables
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"net"
    26  	"os"
    27  	"reflect"
    28  	"strings"
    29  	"testing"
    30  	"time"
    31  
    32  	"k8s.io/apimachinery/pkg/util/sets"
    33  	utilversion "k8s.io/apimachinery/pkg/util/version"
    34  	"k8s.io/apimachinery/pkg/util/wait"
    35  	"k8s.io/utils/exec"
    36  	fakeexec "k8s.io/utils/exec/testing"
    37  )
    38  
    39  func getLockPaths() (string, string) {
    40  	lock14x := fmt.Sprintf("@xtables-%d", time.Now().Nanosecond())
    41  	lock16x := fmt.Sprintf("xtables-%d.lock", time.Now().Nanosecond())
    42  	return lock14x, lock16x
    43  }
    44  
    45  func testIPTablesVersionCmds(t *testing.T, protocol Protocol) {
    46  	version := " v1.4.22"
    47  	iptablesCmd := iptablesCommand(protocol)
    48  	iptablesRestoreCmd := iptablesRestoreCommand(protocol)
    49  
    50  	fcmd := fakeexec.FakeCmd{
    51  		CombinedOutputScript: []fakeexec.FakeAction{
    52  			// iptables version response (for runner instantiation)
    53  			func() ([]byte, []byte, error) { return []byte(iptablesCmd + version), nil, nil },
    54  			// iptables-restore version response (for runner instantiation)
    55  			func() ([]byte, []byte, error) { return []byte(iptablesRestoreCmd + version), nil, nil },
    56  		},
    57  	}
    58  	fexec := &fakeexec.FakeExec{
    59  		CommandScript: []fakeexec.FakeCommandAction{
    60  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
    61  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
    62  		},
    63  	}
    64  	_ = New(fexec, protocol)
    65  
    66  	// Check that proper iptables version command was used during runner instantiation
    67  	if !sets.New(fcmd.CombinedOutputLog[0]...).HasAll(iptablesCmd, "--version") {
    68  		t.Errorf("%s runner instantiate: Expected cmd '%s --version', Got '%s'", protocol, iptablesCmd, fcmd.CombinedOutputLog[0])
    69  	}
    70  
    71  	// Check that proper iptables restore version command was used during runner instantiation
    72  	if !sets.New(fcmd.CombinedOutputLog[1]...).HasAll(iptablesRestoreCmd, "--version") {
    73  		t.Errorf("%s runner instantiate: Expected cmd '%s --version', Got '%s'", protocol, iptablesRestoreCmd, fcmd.CombinedOutputLog[1])
    74  	}
    75  }
    76  
    77  func TestIPTablesVersionCmdsIPv4(t *testing.T) {
    78  	testIPTablesVersionCmds(t, ProtocolIPv4)
    79  }
    80  
    81  func TestIPTablesVersionCmdsIPv6(t *testing.T) {
    82  	testIPTablesVersionCmds(t, ProtocolIPv6)
    83  }
    84  
    85  func testEnsureChain(t *testing.T, protocol Protocol) {
    86  	fcmd := fakeexec.FakeCmd{
    87  		CombinedOutputScript: []fakeexec.FakeAction{
    88  			// iptables version check
    89  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
    90  			// Success.
    91  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
    92  			// Exists.
    93  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
    94  			// Failure.
    95  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} },
    96  		},
    97  	}
    98  	fexec := &fakeexec.FakeExec{
    99  		CommandScript: []fakeexec.FakeCommandAction{
   100  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   101  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   102  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   103  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   104  		},
   105  	}
   106  	runner := New(fexec, protocol)
   107  	// Success.
   108  	exists, err := runner.EnsureChain(TableNAT, Chain("FOOBAR"))
   109  	if err != nil {
   110  		t.Errorf("%s new chain: Expected success, got %v", protocol, err)
   111  	}
   112  	if exists {
   113  		t.Errorf("%s new chain: Expected exists = false", protocol)
   114  	}
   115  	if fcmd.CombinedOutputCalls != 2 {
   116  		t.Errorf("%s new chain: Expected 2 CombinedOutput() calls, got %d", protocol, fcmd.CombinedOutputCalls)
   117  	}
   118  	cmd := iptablesCommand(protocol)
   119  	if !sets.New(fcmd.CombinedOutputLog[1]...).HasAll(cmd, "-t", "nat", "-N", "FOOBAR") {
   120  		t.Errorf("%s new chain: Expected cmd containing '%s -t nat -N FOOBAR', got %s", protocol, cmd, fcmd.CombinedOutputLog[2])
   121  	}
   122  	// Exists.
   123  	exists, err = runner.EnsureChain(TableNAT, Chain("FOOBAR"))
   124  	if err != nil {
   125  		t.Errorf("%s existing chain: Expected success, got %v", protocol, err)
   126  	}
   127  	if !exists {
   128  		t.Errorf("%s existing chain: Expected exists = true", protocol)
   129  	}
   130  	// Simulate failure.
   131  	_, err = runner.EnsureChain(TableNAT, Chain("FOOBAR"))
   132  	if err == nil {
   133  		t.Errorf("%s: Expected failure", protocol)
   134  	}
   135  }
   136  
   137  func TestEnsureChainIPv4(t *testing.T) {
   138  	testEnsureChain(t, ProtocolIPv4)
   139  }
   140  
   141  func TestEnsureChainIPv6(t *testing.T) {
   142  	testEnsureChain(t, ProtocolIPv6)
   143  }
   144  
   145  func TestFlushChain(t *testing.T) {
   146  	fcmd := fakeexec.FakeCmd{
   147  		CombinedOutputScript: []fakeexec.FakeAction{
   148  			// iptables version check
   149  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   150  			// Success.
   151  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   152  			// Failure.
   153  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   154  		},
   155  	}
   156  	fexec := &fakeexec.FakeExec{
   157  		CommandScript: []fakeexec.FakeCommandAction{
   158  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   159  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   160  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   161  		},
   162  	}
   163  	runner := New(fexec, ProtocolIPv4)
   164  	// Success.
   165  	err := runner.FlushChain(TableNAT, Chain("FOOBAR"))
   166  	if err != nil {
   167  		t.Errorf("expected success, got %v", err)
   168  	}
   169  	if fcmd.CombinedOutputCalls != 2 {
   170  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   171  	}
   172  	if !sets.New(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") {
   173  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   174  	}
   175  	// Failure.
   176  	err = runner.FlushChain(TableNAT, Chain("FOOBAR"))
   177  	if err == nil {
   178  		t.Errorf("expected failure")
   179  	}
   180  }
   181  
   182  func TestDeleteChain(t *testing.T) {
   183  	fcmd := fakeexec.FakeCmd{
   184  		CombinedOutputScript: []fakeexec.FakeAction{
   185  			// iptables version check
   186  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   187  			// Success.
   188  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   189  			// Failure.
   190  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   191  		},
   192  	}
   193  	fexec := &fakeexec.FakeExec{
   194  		CommandScript: []fakeexec.FakeCommandAction{
   195  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   196  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   197  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   198  		},
   199  	}
   200  	runner := New(fexec, ProtocolIPv4)
   201  	// Success.
   202  	err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
   203  	if err != nil {
   204  		t.Errorf("expected success, got %v", err)
   205  	}
   206  	if fcmd.CombinedOutputCalls != 2 {
   207  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   208  	}
   209  	if !sets.New(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-X", "FOOBAR") {
   210  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   211  	}
   212  	// Failure.
   213  	err = runner.DeleteChain(TableNAT, Chain("FOOBAR"))
   214  	if err == nil {
   215  		t.Errorf("expected failure")
   216  	}
   217  }
   218  
   219  func TestEnsureRuleAlreadyExists(t *testing.T) {
   220  	fcmd := fakeexec.FakeCmd{
   221  		CombinedOutputScript: []fakeexec.FakeAction{
   222  			// iptables version check
   223  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   224  			// Success.
   225  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   226  		},
   227  	}
   228  	fexec := &fakeexec.FakeExec{
   229  		CommandScript: []fakeexec.FakeCommandAction{
   230  			// iptables version check
   231  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   232  			// The second Command() call is checking the rule.  Success of that exec means "done".
   233  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   234  		},
   235  	}
   236  	runner := New(fexec, ProtocolIPv4)
   237  	exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
   238  	if err != nil {
   239  		t.Errorf("expected success, got %v", err)
   240  	}
   241  	if !exists {
   242  		t.Errorf("expected exists = true")
   243  	}
   244  	if fcmd.CombinedOutputCalls != 2 {
   245  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   246  	}
   247  	if !sets.New(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
   248  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   249  	}
   250  }
   251  
   252  func TestEnsureRuleNew(t *testing.T) {
   253  	fcmd := fakeexec.FakeCmd{
   254  		CombinedOutputScript: []fakeexec.FakeAction{
   255  			// iptables version check
   256  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   257  			// Status 1 on the first call.
   258  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   259  			// Success on the second call.
   260  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   261  		},
   262  	}
   263  	fexec := &fakeexec.FakeExec{
   264  		CommandScript: []fakeexec.FakeCommandAction{
   265  			// iptables version check
   266  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   267  			// The second Command() call is checking the rule.  Failure of that means create it.
   268  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   269  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   270  		},
   271  	}
   272  	runner := New(fexec, ProtocolIPv4)
   273  	exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
   274  	if err != nil {
   275  		t.Errorf("expected success, got %v", err)
   276  	}
   277  	if exists {
   278  		t.Errorf("expected exists = false")
   279  	}
   280  	if fcmd.CombinedOutputCalls != 3 {
   281  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   282  	}
   283  	if !sets.New(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") {
   284  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3])
   285  	}
   286  }
   287  
   288  func TestEnsureRuleErrorChecking(t *testing.T) {
   289  	fcmd := fakeexec.FakeCmd{
   290  		CombinedOutputScript: []fakeexec.FakeAction{
   291  			// iptables version check
   292  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   293  			// Status 2 on the first call.
   294  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} },
   295  		},
   296  	}
   297  	fexec := &fakeexec.FakeExec{
   298  		CommandScript: []fakeexec.FakeCommandAction{
   299  			// iptables version check
   300  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   301  			// The second Command() call is checking the rule.  Failure of that means create it.
   302  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   303  		},
   304  	}
   305  	runner := New(fexec, ProtocolIPv4)
   306  	_, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
   307  	if err == nil {
   308  		t.Errorf("expected failure")
   309  	}
   310  	if fcmd.CombinedOutputCalls != 2 {
   311  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   312  	}
   313  }
   314  
   315  func TestEnsureRuleErrorCreating(t *testing.T) {
   316  	fcmd := fakeexec.FakeCmd{
   317  		CombinedOutputScript: []fakeexec.FakeAction{
   318  			// iptables version check
   319  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   320  			// Status 1 on the first call.
   321  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   322  			// Status 1 on the second call.
   323  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   324  		},
   325  	}
   326  	fexec := &fakeexec.FakeExec{
   327  		CommandScript: []fakeexec.FakeCommandAction{
   328  			// iptables version check
   329  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   330  			// The second Command() call is checking the rule.  Failure of that means create it.
   331  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   332  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   333  		},
   334  	}
   335  	runner := New(fexec, ProtocolIPv4)
   336  	_, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
   337  	if err == nil {
   338  		t.Errorf("expected failure")
   339  	}
   340  	if fcmd.CombinedOutputCalls != 3 {
   341  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   342  	}
   343  }
   344  
   345  func TestDeleteRuleDoesNotExist(t *testing.T) {
   346  	fcmd := fakeexec.FakeCmd{
   347  		CombinedOutputScript: []fakeexec.FakeAction{
   348  			// iptables version check
   349  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   350  			// Status 1 on the first call.
   351  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   352  		},
   353  	}
   354  	fexec := &fakeexec.FakeExec{
   355  		CommandScript: []fakeexec.FakeCommandAction{
   356  			// iptables version check
   357  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   358  			// The second Command() call is checking the rule.  Failure of that exec means "does not exist".
   359  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   360  		},
   361  	}
   362  	runner := New(fexec, ProtocolIPv4)
   363  	err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
   364  	if err != nil {
   365  		t.Errorf("expected success, got %v", err)
   366  	}
   367  	if fcmd.CombinedOutputCalls != 2 {
   368  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   369  	}
   370  	if !sets.New(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
   371  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   372  	}
   373  }
   374  
   375  func TestDeleteRuleExists(t *testing.T) {
   376  	fcmd := fakeexec.FakeCmd{
   377  		CombinedOutputScript: []fakeexec.FakeAction{
   378  			// iptables version check
   379  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   380  			// Success on the first call.
   381  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   382  			// Success on the second call.
   383  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   384  		},
   385  	}
   386  	fexec := &fakeexec.FakeExec{
   387  		CommandScript: []fakeexec.FakeCommandAction{
   388  			// iptables version check
   389  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   390  			// The second Command() call is checking the rule.  Success of that means delete it.
   391  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   392  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   393  		},
   394  	}
   395  	runner := New(fexec, ProtocolIPv4)
   396  	err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
   397  	if err != nil {
   398  		t.Errorf("expected success, got %v", err)
   399  	}
   400  	if fcmd.CombinedOutputCalls != 3 {
   401  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   402  	}
   403  	if !sets.New(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") {
   404  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3])
   405  	}
   406  }
   407  
   408  func TestDeleteRuleErrorChecking(t *testing.T) {
   409  	fcmd := fakeexec.FakeCmd{
   410  		CombinedOutputScript: []fakeexec.FakeAction{
   411  			// iptables version check
   412  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   413  			// Status 2 on the first call.
   414  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} },
   415  		},
   416  	}
   417  	fexec := &fakeexec.FakeExec{
   418  		CommandScript: []fakeexec.FakeCommandAction{
   419  			// iptables version check
   420  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   421  			// The second Command() call is checking the rule.  Failure of that means create it.
   422  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   423  		},
   424  	}
   425  	runner := New(fexec, ProtocolIPv4)
   426  	err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
   427  	if err == nil {
   428  		t.Errorf("expected failure")
   429  	}
   430  	if fcmd.CombinedOutputCalls != 2 {
   431  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   432  	}
   433  }
   434  
   435  func TestDeleteRuleErrorDeleting(t *testing.T) {
   436  	fcmd := fakeexec.FakeCmd{
   437  		CombinedOutputScript: []fakeexec.FakeAction{
   438  			// iptables version check
   439  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   440  			// Success on the first call.
   441  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   442  			// Status 1 on the second call.
   443  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   444  		},
   445  	}
   446  	fexec := &fakeexec.FakeExec{
   447  		CommandScript: []fakeexec.FakeCommandAction{
   448  			// iptables version check
   449  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   450  			// The second Command() call is checking the rule.  Success of that means delete it.
   451  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   452  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   453  		},
   454  	}
   455  	runner := New(fexec, ProtocolIPv4)
   456  	err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
   457  	if err == nil {
   458  		t.Errorf("expected failure")
   459  	}
   460  	if fcmd.CombinedOutputCalls != 3 {
   461  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   462  	}
   463  }
   464  
   465  func TestGetIPTablesHasCheckCommand(t *testing.T) {
   466  	testCases := []struct {
   467  		Version  string
   468  		Expected bool
   469  	}{
   470  		{"iptables v1.4.7", false},
   471  		{"iptables v1.4.11", true},
   472  		{"iptables v1.4.19.1", true},
   473  		{"iptables v2.0.0", true},
   474  		{"total junk", true},
   475  	}
   476  
   477  	for _, testCase := range testCases {
   478  		fcmd := fakeexec.FakeCmd{
   479  			CombinedOutputScript: []fakeexec.FakeAction{
   480  				func() ([]byte, []byte, error) { return []byte(testCase.Version), nil, nil },
   481  				func() ([]byte, []byte, error) { return []byte(testCase.Version), nil, nil },
   482  			},
   483  		}
   484  		fexec := &fakeexec.FakeExec{
   485  			CommandScript: []fakeexec.FakeCommandAction{
   486  				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   487  				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   488  			},
   489  		}
   490  		ipt := New(fexec, ProtocolIPv4)
   491  		runner := ipt.(*runner)
   492  		if testCase.Expected != runner.hasCheck {
   493  			t.Errorf("Expected result: %v, Got result: %v", testCase.Expected, runner.hasCheck)
   494  		}
   495  	}
   496  }
   497  
   498  func TestIPTablesCommands(t *testing.T) {
   499  	testCases := []struct {
   500  		funcName    string
   501  		protocol    Protocol
   502  		expectedCmd string
   503  	}{
   504  		{"iptablesCommand", ProtocolIPv4, cmdIPTables},
   505  		{"iptablesCommand", ProtocolIPv6, cmdIP6Tables},
   506  		{"iptablesSaveCommand", ProtocolIPv4, cmdIPTablesSave},
   507  		{"iptablesSaveCommand", ProtocolIPv6, cmdIP6TablesSave},
   508  		{"iptablesRestoreCommand", ProtocolIPv4, cmdIPTablesRestore},
   509  		{"iptablesRestoreCommand", ProtocolIPv6, cmdIP6TablesRestore},
   510  	}
   511  	for _, testCase := range testCases {
   512  		var cmd string
   513  		switch testCase.funcName {
   514  		case "iptablesCommand":
   515  			cmd = iptablesCommand(testCase.protocol)
   516  		case "iptablesSaveCommand":
   517  			cmd = iptablesSaveCommand(testCase.protocol)
   518  		case "iptablesRestoreCommand":
   519  			cmd = iptablesRestoreCommand(testCase.protocol)
   520  		}
   521  		if cmd != testCase.expectedCmd {
   522  			t.Errorf("Function: %s, Expected result: %s, Actual result: %s", testCase.funcName, testCase.expectedCmd, cmd)
   523  		}
   524  	}
   525  }
   526  
   527  func TestCheckRuleWithoutCheckPresent(t *testing.T) {
   528  	iptablesSaveOutput := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
   529  *nat
   530  :PREROUTING ACCEPT [2136997:197881818]
   531  :POSTROUTING ACCEPT [4284525:258542680]
   532  :OUTPUT ACCEPT [5901660:357267963]
   533  -A PREROUTING -m addrtype --dst-type LOCAL -m mark --mark 0x00004000/0x00004000 -j DOCKER
   534  COMMIT
   535  # Completed on Wed Oct 29 14:56:01 2014`
   536  
   537  	fcmd := fakeexec.FakeCmd{
   538  		CombinedOutputScript: []fakeexec.FakeAction{
   539  			// Success.
   540  			func() ([]byte, []byte, error) { return []byte(iptablesSaveOutput), nil, nil },
   541  		},
   542  	}
   543  	fexec := &fakeexec.FakeExec{
   544  		CommandScript: []fakeexec.FakeCommandAction{
   545  			// The first Command() call is checking the rule.  Success of that exec means "done".
   546  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   547  		},
   548  	}
   549  	runner := &runner{exec: fexec}
   550  	exists, err := runner.checkRuleWithoutCheck(
   551  		TableNAT, ChainPrerouting,
   552  		"-m", "addrtype",
   553  		"-m", "mark", "--mark", "0x4000/0x4000",
   554  		"-j", "DOCKER",
   555  		"--dst-type", "LOCAL")
   556  	if err != nil {
   557  		t.Errorf("expected success, got %v", err)
   558  	}
   559  	if !exists {
   560  		t.Errorf("expected exists = true")
   561  	}
   562  	if fcmd.CombinedOutputCalls != 1 {
   563  		t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
   564  	}
   565  	if !sets.New(fcmd.CombinedOutputLog[0]...).HasAll("iptables-save", "-t", "nat") {
   566  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
   567  	}
   568  }
   569  
   570  func TestCheckRuleWithoutCheckAbsent(t *testing.T) {
   571  	iptablesSaveOutput := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
   572  *nat
   573  :PREROUTING ACCEPT [2136997:197881818]
   574  :POSTROUTING ACCEPT [4284525:258542680]
   575  :OUTPUT ACCEPT [5901660:357267963]
   576  -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
   577  COMMIT
   578  # Completed on Wed Oct 29 14:56:01 2014`
   579  
   580  	fcmd := fakeexec.FakeCmd{
   581  		CombinedOutputScript: []fakeexec.FakeAction{
   582  			// Success.
   583  			func() ([]byte, []byte, error) { return []byte(iptablesSaveOutput), nil, nil },
   584  		},
   585  	}
   586  	fexec := &fakeexec.FakeExec{
   587  		CommandScript: []fakeexec.FakeCommandAction{
   588  			// The first Command() call is checking the rule.  Success of that exec means "done".
   589  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   590  		},
   591  	}
   592  	runner := &runner{exec: fexec}
   593  	exists, err := runner.checkRuleWithoutCheck(TableNAT, ChainPrerouting, "-m", "addrtype", "-j", "DOCKER")
   594  	if err != nil {
   595  		t.Errorf("expected success, got %v", err)
   596  	}
   597  	if exists {
   598  		t.Errorf("expected exists = false")
   599  	}
   600  	if fcmd.CombinedOutputCalls != 1 {
   601  		t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
   602  	}
   603  	if !sets.New(fcmd.CombinedOutputLog[0]...).HasAll("iptables-save", "-t", "nat") {
   604  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
   605  	}
   606  }
   607  
   608  func TestIPTablesWaitFlag(t *testing.T) {
   609  	testCases := []struct {
   610  		Version string
   611  		Result  []string
   612  	}{
   613  		{"0.55.55", nil},
   614  		{"1.0.55", nil},
   615  		{"1.4.19", nil},
   616  		{"1.4.20", []string{WaitString}},
   617  		{"1.4.21", []string{WaitString}},
   618  		{"1.4.22", []string{WaitString, WaitSecondsValue}},
   619  		{"1.5.0", []string{WaitString, WaitSecondsValue}},
   620  		{"2.0.0", []string{WaitString, WaitSecondsValue, WaitIntervalString, WaitIntervalUsecondsValue}},
   621  	}
   622  
   623  	for _, testCase := range testCases {
   624  		result := getIPTablesWaitFlag(utilversion.MustParseGeneric(testCase.Version))
   625  		if !reflect.DeepEqual(result, testCase.Result) {
   626  			t.Errorf("For %s expected %v got %v", testCase.Version, testCase.Result, result)
   627  		}
   628  	}
   629  }
   630  
   631  func TestWaitFlagUnavailable(t *testing.T) {
   632  	fcmd := fakeexec.FakeCmd{
   633  		CombinedOutputScript: []fakeexec.FakeAction{
   634  			// iptables version check
   635  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.19"), nil, nil },
   636  			// iptables-restore version check
   637  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   638  			// Success.
   639  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   640  		},
   641  	}
   642  	fexec := &fakeexec.FakeExec{
   643  		CommandScript: []fakeexec.FakeCommandAction{
   644  			// iptables version check
   645  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   646  			// iptables-restore version check
   647  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   648  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   649  		},
   650  	}
   651  	runner := New(fexec, ProtocolIPv4)
   652  	err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
   653  	if err != nil {
   654  		t.Errorf("expected success, got %v", err)
   655  	}
   656  	if fcmd.CombinedOutputCalls != 3 {
   657  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   658  	}
   659  	if sets.New(fcmd.CombinedOutputLog[2]...).Has(WaitString) {
   660  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   661  	}
   662  }
   663  
   664  func TestWaitFlagOld(t *testing.T) {
   665  	fcmd := fakeexec.FakeCmd{
   666  		CombinedOutputScript: []fakeexec.FakeAction{
   667  			// iptables version check
   668  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.20"), nil, nil },
   669  			// iptables-restore version check
   670  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   671  			// Success.
   672  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   673  		},
   674  	}
   675  	fexec := &fakeexec.FakeExec{
   676  		CommandScript: []fakeexec.FakeCommandAction{
   677  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   678  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   679  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   680  		},
   681  	}
   682  	runner := New(fexec, ProtocolIPv4)
   683  	err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
   684  	if err != nil {
   685  		t.Errorf("expected success, got %v", err)
   686  	}
   687  	if fcmd.CombinedOutputCalls != 3 {
   688  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   689  	}
   690  	if !sets.New(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString) {
   691  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   692  	}
   693  	if sets.New(fcmd.CombinedOutputLog[2]...).Has(WaitSecondsValue) {
   694  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   695  	}
   696  }
   697  
   698  func TestWaitFlagNew(t *testing.T) {
   699  	fcmd := fakeexec.FakeCmd{
   700  		CombinedOutputScript: []fakeexec.FakeAction{
   701  			// iptables version check
   702  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
   703  			// iptables-restore version check
   704  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   705  			// Success.
   706  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   707  		},
   708  	}
   709  	fexec := &fakeexec.FakeExec{
   710  		CommandScript: []fakeexec.FakeCommandAction{
   711  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   712  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   713  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   714  		},
   715  	}
   716  	runner := New(fexec, ProtocolIPv4)
   717  	err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
   718  	if err != nil {
   719  		t.Errorf("expected success, got %v", err)
   720  	}
   721  	if fcmd.CombinedOutputCalls != 3 {
   722  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   723  	}
   724  	if !sets.New(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString, WaitSecondsValue) {
   725  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   726  	}
   727  }
   728  
   729  func TestWaitIntervalFlagNew(t *testing.T) {
   730  	fcmd := fakeexec.FakeCmd{
   731  		CombinedOutputScript: []fakeexec.FakeAction{
   732  			// iptables version check
   733  			func() ([]byte, []byte, error) { return []byte("iptables v1.6.1"), nil, nil },
   734  			// iptables-restore version check
   735  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   736  			// Success.
   737  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   738  		},
   739  	}
   740  	fexec := &fakeexec.FakeExec{
   741  		CommandScript: []fakeexec.FakeCommandAction{
   742  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   743  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   744  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   745  		},
   746  	}
   747  	runner := New(fexec, ProtocolIPv4)
   748  	err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
   749  	if err != nil {
   750  		t.Errorf("expected success, got %v", err)
   751  	}
   752  	if fcmd.CombinedOutputCalls != 3 {
   753  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   754  	}
   755  	if !sets.New(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString, WaitSecondsValue, WaitIntervalString, WaitIntervalUsecondsValue) {
   756  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   757  	}
   758  }
   759  
   760  func testSaveInto(t *testing.T, protocol Protocol) {
   761  	version := " v1.9.22"
   762  	iptablesCmd := iptablesCommand(protocol)
   763  	iptablesSaveCmd := iptablesSaveCommand(protocol)
   764  
   765  	output := fmt.Sprintf(`# Generated by %s on Thu Jan 19 11:38:09 2017
   766  *filter
   767  :INPUT ACCEPT [15079:38410730]
   768  :FORWARD ACCEPT [0:0]
   769  :OUTPUT ACCEPT [11045:521562]
   770  COMMIT
   771  # Completed on Thu Jan 19 11:38:09 2017`, iptablesSaveCmd+version)
   772  
   773  	stderrOutput := "#STDERR OUTPUT" // SaveInto() should should NOT capture stderr into the buffer
   774  
   775  	fcmd := fakeexec.FakeCmd{
   776  		CombinedOutputScript: []fakeexec.FakeAction{
   777  			// iptables version check
   778  			func() ([]byte, []byte, error) { return []byte(iptablesCmd + version), nil, nil },
   779  		},
   780  		RunScript: []fakeexec.FakeAction{
   781  			func() ([]byte, []byte, error) { return []byte(output), []byte(stderrOutput), nil },
   782  			func() ([]byte, []byte, error) { return nil, []byte(stderrOutput), &fakeexec.FakeExitError{Status: 1} },
   783  		},
   784  	}
   785  	fexec := &fakeexec.FakeExec{
   786  		CommandScript: []fakeexec.FakeCommandAction{
   787  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   788  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   789  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   790  		},
   791  	}
   792  	runner := New(fexec, protocol)
   793  	buffer := bytes.NewBuffer(nil)
   794  
   795  	// Success.
   796  	err := runner.SaveInto(TableNAT, buffer)
   797  	if err != nil {
   798  		t.Fatalf("%s: Expected success, got %v", protocol, err)
   799  	}
   800  
   801  	if buffer.String() != output {
   802  		t.Errorf("%s: Expected output '%s', got '%v'", protocol, output, buffer.String())
   803  	}
   804  
   805  	if fcmd.CombinedOutputCalls != 1 {
   806  		t.Errorf("%s: Expected 1 CombinedOutput() calls, got %d", protocol, fcmd.CombinedOutputCalls)
   807  	}
   808  	if fcmd.RunCalls != 1 {
   809  		t.Errorf("%s: Expected 1 Run() call, got %d", protocol, fcmd.RunCalls)
   810  	}
   811  	if !sets.New(fcmd.RunLog[0]...).HasAll(iptablesSaveCmd, "-t", "nat") {
   812  		t.Errorf("%s: Expected cmd containing '%s -t nat', got '%s'", protocol, iptablesSaveCmd, fcmd.RunLog[0])
   813  	}
   814  
   815  	// Failure.
   816  	buffer.Reset()
   817  	err = runner.SaveInto(TableNAT, buffer)
   818  	if err == nil {
   819  		t.Errorf("%s: Expected failure", protocol)
   820  	}
   821  	if buffer.String() != stderrOutput {
   822  		t.Errorf("%s: Expected output '%s', got '%v'", protocol, stderrOutput, buffer.String())
   823  	}
   824  }
   825  
   826  func TestSaveIntoIPv4(t *testing.T) {
   827  	testSaveInto(t, ProtocolIPv4)
   828  }
   829  
   830  func TestSaveIntoIPv6(t *testing.T) {
   831  	testSaveInto(t, ProtocolIPv6)
   832  }
   833  
   834  func testRestore(t *testing.T, protocol Protocol) {
   835  	version := " v1.9.22"
   836  	iptablesCmd := iptablesCommand(protocol)
   837  	iptablesRestoreCmd := iptablesRestoreCommand(protocol)
   838  
   839  	fcmd := fakeexec.FakeCmd{
   840  		CombinedOutputScript: []fakeexec.FakeAction{
   841  			// iptables version check
   842  			func() ([]byte, []byte, error) { return []byte(iptablesCmd + version), nil, nil },
   843  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   844  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   845  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   846  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   847  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   848  		},
   849  	}
   850  	fexec := &fakeexec.FakeExec{
   851  		CommandScript: []fakeexec.FakeCommandAction{
   852  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   853  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   854  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   855  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   856  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   857  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   858  		},
   859  	}
   860  	runner := New(fexec, protocol)
   861  
   862  	// both flags true
   863  	err := runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
   864  	if err != nil {
   865  		t.Errorf("%s flush,restore: Expected success, got %v", protocol, err)
   866  	}
   867  
   868  	commandSet := sets.New(fcmd.CombinedOutputLog[1]...)
   869  	if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT), "--counters") || commandSet.HasAny("--noflush") {
   870  		t.Errorf("%s flush, restore: Expected cmd containing '%s -T %s --counters', got '%s'", protocol, iptablesRestoreCmd, string(TableNAT), fcmd.CombinedOutputLog[1])
   871  	}
   872  
   873  	// FlushTables, NoRestoreCounters
   874  	err = runner.Restore(TableNAT, []byte{}, FlushTables, NoRestoreCounters)
   875  	if err != nil {
   876  		t.Errorf("%s flush, no restore: Expected success, got %v", protocol, err)
   877  	}
   878  
   879  	commandSet = sets.New(fcmd.CombinedOutputLog[2]...)
   880  	if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT)) || commandSet.HasAny("--noflush", "--counters") {
   881  		t.Errorf("%s flush, no restore: Expected cmd containing '--noflush' or '--counters', got '%s'", protocol, fcmd.CombinedOutputLog[2])
   882  	}
   883  
   884  	// NoFlushTables, RestoreCounters
   885  	err = runner.Restore(TableNAT, []byte{}, NoFlushTables, RestoreCounters)
   886  	if err != nil {
   887  		t.Errorf("%s no flush, restore: Expected success, got %v", protocol, err)
   888  	}
   889  
   890  	commandSet = sets.New(fcmd.CombinedOutputLog[3]...)
   891  	if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT), "--noflush", "--counters") {
   892  		t.Errorf("%s no flush, restore: Expected cmd containing '--noflush' and '--counters', got '%s'", protocol, fcmd.CombinedOutputLog[3])
   893  	}
   894  
   895  	// NoFlushTables, NoRestoreCounters
   896  	err = runner.Restore(TableNAT, []byte{}, NoFlushTables, NoRestoreCounters)
   897  	if err != nil {
   898  		t.Errorf("%s no flush, no restore: Expected success, got %v", protocol, err)
   899  	}
   900  
   901  	commandSet = sets.New(fcmd.CombinedOutputLog[4]...)
   902  	if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT), "--noflush") || commandSet.HasAny("--counters") {
   903  		t.Errorf("%s no flush, no restore: Expected cmd containing '%s -T %s --noflush', got '%s'", protocol, iptablesRestoreCmd, string(TableNAT), fcmd.CombinedOutputLog[4])
   904  	}
   905  
   906  	if fcmd.CombinedOutputCalls != 5 {
   907  		t.Errorf("%s: Expected 5 total CombinedOutput() calls, got %d", protocol, fcmd.CombinedOutputCalls)
   908  	}
   909  
   910  	// Failure.
   911  	err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
   912  	if err == nil {
   913  		t.Errorf("%s Expected a failure", protocol)
   914  	}
   915  }
   916  
   917  func TestRestoreIPv4(t *testing.T) {
   918  	testRestore(t, ProtocolIPv4)
   919  }
   920  
   921  func TestRestoreIPv6(t *testing.T) {
   922  	testRestore(t, ProtocolIPv6)
   923  }
   924  
   925  // TestRestoreAll tests only the simplest use case, as flag handling code is already tested in TestRestore
   926  func TestRestoreAll(t *testing.T) {
   927  	fcmd := fakeexec.FakeCmd{
   928  		CombinedOutputScript: []fakeexec.FakeAction{
   929  			// iptables version check
   930  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   931  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   932  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   933  		},
   934  	}
   935  	fexec := &fakeexec.FakeExec{
   936  		CommandScript: []fakeexec.FakeCommandAction{
   937  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   938  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   939  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   940  		},
   941  	}
   942  	lockPath14x, lockPath16x := getLockPaths()
   943  	runner := newInternal(fexec, ProtocolIPv4, lockPath14x, lockPath16x)
   944  
   945  	err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
   946  	if err != nil {
   947  		t.Fatalf("expected success, got %v", err)
   948  	}
   949  
   950  	commandSet := sets.New(fcmd.CombinedOutputLog[1]...)
   951  	if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") {
   952  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
   953  	}
   954  
   955  	if fcmd.CombinedOutputCalls != 2 {
   956  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   957  	}
   958  
   959  	// Failure.
   960  	err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
   961  	if err == nil {
   962  		t.Errorf("expected failure")
   963  	}
   964  }
   965  
   966  // TestRestoreAllWait tests that the "wait" flag is passed to a compatible iptables-restore
   967  func TestRestoreAllWait(t *testing.T) {
   968  	fcmd := fakeexec.FakeCmd{
   969  		CombinedOutputScript: []fakeexec.FakeAction{
   970  			// iptables version check
   971  			func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
   972  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
   973  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
   974  		},
   975  	}
   976  	fexec := &fakeexec.FakeExec{
   977  		CommandScript: []fakeexec.FakeCommandAction{
   978  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   979  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   980  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
   981  		},
   982  	}
   983  	lockPath14x, lockPath16x := getLockPaths()
   984  	runner := newInternal(fexec, ProtocolIPv4, lockPath14x, lockPath16x)
   985  
   986  	err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
   987  	if err != nil {
   988  		t.Fatalf("expected success, got %v", err)
   989  	}
   990  
   991  	commandSet := sets.New(fcmd.CombinedOutputLog[1]...)
   992  	if !commandSet.HasAll("iptables-restore", WaitString, WaitSecondsValue, WaitIntervalString, WaitIntervalUsecondsValue, "--counters", "--noflush") {
   993  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
   994  	}
   995  
   996  	if fcmd.CombinedOutputCalls != 2 {
   997  		t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
   998  	}
   999  
  1000  	// Failure.
  1001  	err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  1002  	if err == nil {
  1003  		t.Errorf("expected failure")
  1004  	}
  1005  }
  1006  
  1007  // TestRestoreAllWaitOldIptablesRestore tests that the "wait" flag is not passed
  1008  // to an old iptables-restore
  1009  func TestRestoreAllWaitOldIptablesRestore(t *testing.T) {
  1010  	fcmd := fakeexec.FakeCmd{
  1011  		CombinedOutputScript: []fakeexec.FakeAction{
  1012  			// iptables version check
  1013  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1014  			// iptables-restore version check
  1015  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1016  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1017  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  1018  		},
  1019  	}
  1020  	fexec := &fakeexec.FakeExec{
  1021  		CommandScript: []fakeexec.FakeCommandAction{
  1022  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1023  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1024  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1025  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1026  		},
  1027  	}
  1028  	lockPath14x, lockPath16x := getLockPaths()
  1029  	// the lockPath14x is a UNIX socket which is cleaned up automatically on close, but the
  1030  	// lockPath16x is a plain file which is not cleaned up.
  1031  	defer os.Remove(lockPath16x)
  1032  	runner := newInternal(fexec, ProtocolIPv4, lockPath14x, lockPath16x)
  1033  
  1034  	err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1035  	if err != nil {
  1036  		t.Fatalf("expected success, got %v", err)
  1037  	}
  1038  
  1039  	commandSet := sets.New(fcmd.CombinedOutputLog[2]...)
  1040  	if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") {
  1041  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  1042  	}
  1043  	if commandSet.HasAll(WaitString) {
  1044  		t.Errorf("wrong CombinedOutput() log (unexpected %s option), got %s", WaitString, fcmd.CombinedOutputLog[1])
  1045  	}
  1046  
  1047  	if fcmd.CombinedOutputCalls != 3 {
  1048  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  1049  	}
  1050  
  1051  	// Failure.
  1052  	err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  1053  	if err == nil {
  1054  		t.Errorf("expected failure")
  1055  	}
  1056  }
  1057  
  1058  // TestRestoreAllGrabNewLock tests that the iptables code will grab the
  1059  // iptables /run lock when using an iptables-restore version that does not
  1060  // support the --wait argument
  1061  func TestRestoreAllGrabNewLock(t *testing.T) {
  1062  	fcmd := fakeexec.FakeCmd{
  1063  		CombinedOutputScript: []fakeexec.FakeAction{
  1064  			// iptables version check
  1065  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1066  			// iptables-restore version check
  1067  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1068  		},
  1069  	}
  1070  	fexec := &fakeexec.FakeExec{
  1071  		CommandScript: []fakeexec.FakeCommandAction{
  1072  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1073  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1074  		},
  1075  	}
  1076  	lockPath14x, lockPath16x := getLockPaths()
  1077  	runner := newInternal(fexec, ProtocolIPv4, lockPath14x, lockPath16x)
  1078  
  1079  	// Grab the /run lock and ensure the RestoreAll fails
  1080  	runLock, err := os.OpenFile(lockPath16x, os.O_CREATE, 0600)
  1081  	if err != nil {
  1082  		t.Fatalf("expected to open %s, got %v", lockPath16x, err)
  1083  	}
  1084  	defer func() {
  1085  		runLock.Close()
  1086  		os.Remove(lockPath16x)
  1087  	}()
  1088  
  1089  	if err := grabIptablesFileLock(runLock); err != nil {
  1090  		t.Errorf("expected to lock %s, got %v", lockPath16x, err)
  1091  	}
  1092  
  1093  	err = runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1094  	if err == nil {
  1095  		t.Fatal("expected failure, got success instead")
  1096  	}
  1097  	if !strings.Contains(err.Error(), "failed to acquire new iptables lock: timed out waiting for the condition") {
  1098  		t.Errorf("expected timeout error, got %v", err)
  1099  	}
  1100  }
  1101  
  1102  // TestRestoreAllGrabOldLock tests that the iptables code will grab the
  1103  // iptables @xtables abstract unix socket lock when using an iptables-restore
  1104  // version that does not support the --wait argument
  1105  func TestRestoreAllGrabOldLock(t *testing.T) {
  1106  	fcmd := fakeexec.FakeCmd{
  1107  		CombinedOutputScript: []fakeexec.FakeAction{
  1108  			// iptables version check
  1109  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1110  			// iptables-restore version check
  1111  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1112  		},
  1113  	}
  1114  	fexec := &fakeexec.FakeExec{
  1115  		CommandScript: []fakeexec.FakeCommandAction{
  1116  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1117  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1118  		},
  1119  	}
  1120  	lockPath14x, lockPath16x := getLockPaths()
  1121  	// the lockPath14x is a UNIX socket which is cleaned up automatically on close, but the
  1122  	// lockPath16x is a plain file which is not cleaned up.
  1123  	defer os.Remove(lockPath16x)
  1124  	runner := newInternal(fexec, ProtocolIPv4, lockPath14x, lockPath16x)
  1125  
  1126  	var runLock *net.UnixListener
  1127  	// Grab the abstract @xtables socket, will retry if the socket exists
  1128  	err := wait.PollImmediate(time.Second, wait.ForeverTestTimeout, func() (done bool, err error) {
  1129  		runLock, err = net.ListenUnix("unix", &net.UnixAddr{Name: lockPath14x, Net: "unix"})
  1130  		if err != nil {
  1131  			t.Logf("Failed to lock %s: %v, will retry.", lockPath14x, err)
  1132  			return false, nil
  1133  		}
  1134  		return true, nil
  1135  	})
  1136  	if err != nil {
  1137  		t.Fatalf("Timed out locking %s", lockPath14x)
  1138  	}
  1139  	if runLock == nil {
  1140  		t.Fatal("Unexpected nil runLock")
  1141  	}
  1142  
  1143  	defer runLock.Close()
  1144  
  1145  	err = runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1146  	if err == nil {
  1147  		t.Fatal("expected failure, got success instead")
  1148  	}
  1149  	if !strings.Contains(err.Error(), "failed to acquire old iptables lock: timed out waiting for the condition") {
  1150  		t.Errorf("expected timeout error, got %v", err)
  1151  	}
  1152  }
  1153  
  1154  // TestRestoreAllWaitBackportedIptablesRestore tests that the "wait" flag is passed
  1155  // to a seemingly-old-but-actually-new iptables-restore
  1156  func TestRestoreAllWaitBackportedIptablesRestore(t *testing.T) {
  1157  	fcmd := fakeexec.FakeCmd{
  1158  		CombinedOutputScript: []fakeexec.FakeAction{
  1159  			// iptables version check
  1160  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1161  			// iptables-restore version check
  1162  			func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1163  			func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1164  			func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  1165  		},
  1166  	}
  1167  	fexec := &fakeexec.FakeExec{
  1168  		CommandScript: []fakeexec.FakeCommandAction{
  1169  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1170  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1171  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1172  			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1173  		},
  1174  	}
  1175  	lockPath14x, lockPath16x := getLockPaths()
  1176  	runner := newInternal(fexec, ProtocolIPv4, lockPath14x, lockPath16x)
  1177  
  1178  	err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1179  	if err != nil {
  1180  		t.Fatalf("expected success, got %v", err)
  1181  	}
  1182  
  1183  	commandSet := sets.New(fcmd.CombinedOutputLog[2]...)
  1184  	if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") {
  1185  		t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  1186  	}
  1187  	if !commandSet.HasAll(WaitString) {
  1188  		t.Errorf("wrong CombinedOutput() log (expected %s option), got %s", WaitString, fcmd.CombinedOutputLog[1])
  1189  	}
  1190  
  1191  	if fcmd.CombinedOutputCalls != 3 {
  1192  		t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  1193  	}
  1194  
  1195  	// Failure.
  1196  	err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  1197  	if err == nil {
  1198  		t.Errorf("expected failure")
  1199  	}
  1200  }
  1201  
  1202  // TestExtractLines tests that
  1203  func TestExtractLines(t *testing.T) {
  1204  	mkLines := func(lines ...LineData) []LineData {
  1205  		return lines
  1206  	}
  1207  	lines := "Line1: 1\nLine2: 2\nLine3: 3\nLine4: 4\nLine5: 5\nLine6: 6\nLine7: 7\nLine8: 8\nLine9: 9\nLine10: 10"
  1208  	tests := []struct {
  1209  		count int
  1210  		line  int
  1211  		name  string
  1212  		want  []LineData
  1213  	}{{
  1214  		name:  "test-line-0",
  1215  		count: 3,
  1216  		line:  0,
  1217  		want:  nil,
  1218  	}, {
  1219  		name:  "test-count-0",
  1220  		count: 0,
  1221  		line:  3,
  1222  		want:  mkLines(LineData{3, "Line3: 3"}),
  1223  	}, {
  1224  		name:  "test-common-cases",
  1225  		count: 3,
  1226  		line:  6,
  1227  		want: mkLines(
  1228  			LineData{3, "Line3: 3"},
  1229  			LineData{4, "Line4: 4"},
  1230  			LineData{5, "Line5: 5"},
  1231  			LineData{6, "Line6: 6"},
  1232  			LineData{7, "Line7: 7"},
  1233  			LineData{8, "Line8: 8"},
  1234  			LineData{9, "Line9: 9"}),
  1235  	}, {
  1236  		name:  "test4-bound-cases",
  1237  		count: 11,
  1238  		line:  10,
  1239  		want: mkLines(
  1240  			LineData{1, "Line1: 1"},
  1241  			LineData{2, "Line2: 2"},
  1242  			LineData{3, "Line3: 3"},
  1243  			LineData{4, "Line4: 4"},
  1244  			LineData{5, "Line5: 5"},
  1245  			LineData{6, "Line6: 6"},
  1246  			LineData{7, "Line7: 7"},
  1247  			LineData{8, "Line8: 8"},
  1248  			LineData{9, "Line9: 9"},
  1249  			LineData{10, "Line10: 10"}),
  1250  	}}
  1251  
  1252  	for _, tt := range tests {
  1253  		t.Run(tt.name, func(t *testing.T) {
  1254  			got := ExtractLines([]byte(lines), tt.line, tt.count)
  1255  			if !reflect.DeepEqual(got, tt.want) {
  1256  				t.Errorf("got = %v, want = %v", got, tt.want)
  1257  			}
  1258  		})
  1259  	}
  1260  }
  1261  

View as plain text