k8s.io/kubernetes@v1.29.3/pkg/util/iptables/iptables_test.go (about) 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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString) { 691 t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2]) 692 } 693 if sets.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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.NewString(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 }