github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/tests/rkt_net_test.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build host coreos src kvm 16 17 package main 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "regexp" 26 "strconv" 27 "strings" 28 "syscall" 29 "testing" 30 "time" 31 32 "github.com/rkt/rkt/networking/netinfo" 33 "github.com/rkt/rkt/pkg/fileutil" 34 "github.com/rkt/rkt/tests/testutils" 35 "github.com/rkt/rkt/tests/testutils/logger" 36 "github.com/vishvananda/netlink" 37 ) 38 39 /* 40 * Host network 41 * --- 42 * Container must have the same network namespace as the host 43 */ 44 func NewNetHostTest() testutils.Test { 45 return testutils.TestFunc(func(t *testing.T) { 46 testImageArgs := []string{"--exec=/inspect --print-netns"} 47 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 48 defer os.Remove(testImage) 49 50 ctx := testutils.NewRktRunCtx() 51 defer ctx.Cleanup() 52 53 cmd := fmt.Sprintf("%s --net=host --debug --insecure-options=image run --mds-register=false %s", ctx.Cmd(), testImage) 54 child := spawnOrFail(t, cmd) 55 ctx.RegisterChild(child) 56 defer waitOrFail(t, child, 0) 57 58 expectedRegex := `NetNS: (net:\[\d+\])` 59 result, out, err := expectRegexWithOutput(child, expectedRegex) 60 if err != nil { 61 t.Fatalf("Error: %v\nOutput: %v", err, out) 62 } 63 64 ns, err := os.Readlink("/proc/self/ns/net") 65 if err != nil { 66 t.Fatalf("Cannot evaluate NetNS symlink: %v", err) 67 } 68 69 if nsChanged := ns != result[1]; nsChanged { 70 t.Fatalf("container left host netns") 71 } 72 }) 73 } 74 75 /* 76 * Host networking 77 * --- 78 * Container launches http server which must be reachable by the host via the 79 * localhost address 80 */ 81 func NewNetHostConnectivityTest() testutils.Test { 82 return testutils.TestFunc(func(t *testing.T) { 83 logger.SetLogger(t) 84 85 httpPort, err := testutils.GetNextFreePort4() 86 if err != nil { 87 t.Fatalf("%v", err) 88 } 89 httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) 90 httpGetAddr := fmt.Sprintf("http://127.0.0.1:%v", httpPort) 91 92 testImageArgs := []string{"--exec=/inspect --serve-http=" + httpServeAddr} 93 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 94 defer os.Remove(testImage) 95 96 ctx := testutils.NewRktRunCtx() 97 defer ctx.Cleanup() 98 99 cmd := fmt.Sprintf("%s --net=host --debug --insecure-options=image run --mds-register=false %s", ctx.Cmd(), testImage) 100 child := spawnOrFail(t, cmd) 101 ctx.RegisterChild(child) 102 103 ga := testutils.NewGoroutineAssistant(t) 104 ga.Add(2) 105 106 // Child opens the server 107 go func() { 108 defer ga.Done() 109 ga.WaitOrFail(child) 110 }() 111 112 // Host connects to the child 113 go func() { 114 defer ga.Done() 115 expectedRegex := `serving on` 116 _, out, err := expectRegexWithOutput(child, expectedRegex) 117 if err != nil { 118 ga.Fatalf("Error: %v\nOutput: %v", err, out) 119 } 120 body, err := testutils.HTTPGet(httpGetAddr) 121 if err != nil { 122 ga.Fatalf("%v\n", err) 123 } 124 t.Logf("HTTP-Get received: %s", body) 125 }() 126 127 ga.Wait() 128 }) 129 } 130 131 /* 132 * None networking 133 * --- 134 * must be in an empty netns 135 */ 136 func NewNetNoneTest() testutils.Test { 137 return testutils.TestFunc(func(t *testing.T) { 138 testImageArgs := []string{"--exec=/inspect --print-netns --print-iface-count"} 139 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 140 defer os.Remove(testImage) 141 142 ctx := testutils.NewRktRunCtx() 143 defer ctx.Cleanup() 144 145 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=none --mds-register=false %s", ctx.Cmd(), testImage) 146 147 child := spawnOrFail(t, cmd) 148 defer waitOrFail(t, child, 0) 149 expectedRegex := `NetNS: (net:\[\d+\])` 150 result, out, err := expectRegexWithOutput(child, expectedRegex) 151 if err != nil { 152 t.Fatalf("Error: %v\nOutput: %v", err, out) 153 } 154 155 ns, err := os.Readlink("/proc/self/ns/net") 156 if err != nil { 157 t.Fatalf("Cannot evaluate NetNS symlink: %v", err) 158 } 159 160 if nsChanged := ns != result[1]; !nsChanged { 161 t.Fatalf("container did not leave host netns") 162 } 163 164 expectedRegex = `Interface count: (\d+)` 165 result, out, err = expectRegexWithOutput(child, expectedRegex) 166 if err != nil { 167 t.Fatalf("Error: %v\nOutput: %v", err, out) 168 } 169 ifaceCount, err := strconv.Atoi(result[1]) 170 if err != nil { 171 t.Fatalf("Error parsing interface count: %v\nOutput: %v", err, out) 172 } 173 if ifaceCount != 1 { 174 t.Fatalf("Interface count must be 1 not %q", ifaceCount) 175 } 176 }) 177 } 178 179 /* 180 * Default net 181 * --- 182 * Container must be in a separate network namespace 183 */ 184 func NewTestNetDefaultNetNS() testutils.Test { 185 return testutils.TestFunc(func(t *testing.T) { 186 testImageArgs := []string{"--exec=/inspect --print-netns"} 187 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 188 defer os.Remove(testImage) 189 190 ctx := testutils.NewRktRunCtx() 191 defer ctx.Cleanup() 192 193 f := func(argument string) { 194 cmd := fmt.Sprintf("%s --debug --insecure-options=image run %s --mds-register=false %s", ctx.Cmd(), argument, testImage) 195 child := spawnOrFail(t, cmd) 196 defer waitOrFail(t, child, 0) 197 198 expectedRegex := `NetNS: (net:\[\d+\])` 199 result, out, err := expectRegexWithOutput(child, expectedRegex) 200 if err != nil { 201 t.Fatalf("Error: %v\nOutput: %v", err, out) 202 } 203 204 ns, err := os.Readlink("/proc/self/ns/net") 205 if err != nil { 206 t.Fatalf("Cannot evaluate NetNS symlink: %v", err) 207 } 208 209 if nsChanged := ns != result[1]; !nsChanged { 210 t.Fatalf("container did not leave host netns") 211 } 212 213 } 214 f("--net=default") 215 f("") 216 }) 217 } 218 219 /* 220 * Default net 221 * --- 222 * Host launches http server on all interfaces in the host netns 223 * Container must be able to connect via any IP address of the host in the 224 * default network, which is NATed 225 * TODO: test connection to host on an outside interface 226 */ 227 func NewNetDefaultConnectivityTest() testutils.Test { 228 return testutils.TestFunc(func(t *testing.T) { 229 ctx := testutils.NewRktRunCtx() 230 defer ctx.Cleanup() 231 232 f := func(argument string) { 233 httpPort, err := testutils.GetNextFreePort4() 234 if err != nil { 235 t.Fatalf("%v", err) 236 } 237 httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) 238 httpServeTimeout := 30 239 240 nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() 241 if err != nil { 242 t.Fatalf("%v", err) 243 } 244 if nonLoIPv4 == "" { 245 t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..") 246 } 247 248 httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort) 249 t.Log("Telling the child to connect via", httpGetAddr) 250 251 testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)} 252 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 253 defer os.Remove(testImage) 254 255 hostname, err := os.Hostname() 256 if err != nil { 257 t.Fatalf("Error getting hostname: %v", err) 258 } 259 260 ga := testutils.NewGoroutineAssistant(t) 261 ga.Add(2) 262 263 // Host opens the server 264 go func() { 265 defer ga.Done() 266 err := testutils.HTTPServe(httpServeAddr, httpServeTimeout) 267 if err != nil { 268 ga.Fatalf("Error during HTTPServe: %v", err) 269 } 270 }() 271 272 // Child connects to host 273 go func() { 274 defer ga.Done() 275 cmd := fmt.Sprintf("%s --debug --insecure-options=image run %s --mds-register=false %s", ctx.Cmd(), argument, testImage) 276 child := ga.SpawnOrFail(cmd) 277 defer ga.WaitOrFail(child) 278 279 expectedRegex := `HTTP-Get received: (.*)\r` 280 result, out, err := expectRegexWithOutput(child, expectedRegex) 281 if err != nil { 282 ga.Fatalf("Error: %v\nOutput: %v", err, out) 283 } 284 if result[1] != hostname { 285 ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname) 286 } 287 }() 288 289 ga.Wait() 290 } 291 f("--net=default") 292 f("") 293 }) 294 } 295 296 /* 297 * Default-restricted net 298 * --- 299 * Container launches http server on all its interfaces 300 * Host must be able to connects to container's http server via container's 301 * eth0's IPv4 302 * TODO: verify that the container isn't NATed 303 */ 304 func NewTestNetDefaultRestrictedConnectivity() testutils.Test { 305 return testutils.TestFunc(func(t *testing.T) { 306 ctx := testutils.NewRktRunCtx() 307 defer ctx.Cleanup() 308 309 f := func(argument string) { 310 httpPort := "8080" 311 iface := "eth0" 312 313 testImageArgs := []string{fmt.Sprintf("--exec=/inspect --print-ipv4=%v --serve-http=0.0.0.0:%v", iface, httpPort)} 314 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 315 defer os.Remove(testImage) 316 317 cmd := fmt.Sprintf("%s --insecure-options=image run %s --mds-register=false %s", ctx.Cmd(), argument, testImage) 318 child := spawnOrFail(t, cmd) 319 320 // Wait for the container to print out the IP address 321 expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` 322 result, out, err := expectRegexWithOutput(child, expectedRegex) 323 if err != nil { 324 t.Fatalf("Error: %v\nOutput: %v", err, out) 325 } 326 httpGetAddr := fmt.Sprintf("http://%v:%v", result[1], httpPort) 327 328 // Wait for the container to open the port 329 expectedRegex = `serving on` 330 _, out, err = expectRegexWithOutput(child, expectedRegex) 331 if err != nil { 332 t.Fatalf("Error: %v\nOutput: %v", err, out) 333 } 334 body, err := testutils.HTTPGet(httpGetAddr) 335 if err != nil { 336 t.Fatalf("%v\n", err) 337 } 338 t.Logf("HTTP-Get received: %s", body) 339 waitOrFail(t, child, 0) 340 } 341 f("--net=default-restricted") 342 }) 343 } 344 345 type PortFwdCase struct { 346 HttpGetIP string 347 HttpServePort int 348 ListenAddress string 349 RktArg string 350 ShouldSucceed bool 351 } 352 353 var ( 354 bannedPorts = make(map[int]struct{}, 0) 355 356 defaultSamePortFwdCase = PortFwdCase{"172.16.28.1", 0, "", "--net=default", true} 357 defaultDiffPortFwdCase = PortFwdCase{"172.16.28.1", 1024, "", "--net=default", true} 358 defaultSpecificIPFwdCase = PortFwdCase{"172.16.28.1", 1024, "172.16.28.1:", "--net=default", true} 359 defaultSpecificIPFwdFailCase = PortFwdCase{"127.0.0.1", 1024, "172.16.28.1:", "--net=default", false} 360 defaultLoSamePortFwdCase = PortFwdCase{"127.0.0.1", 0, "", "--net=default", true} 361 defaultLoDiffPortFwdCase = PortFwdCase{"127.0.0.1", 1014, "", "--net=default", true} 362 363 portFwdBridge = networkTemplateT{ 364 Name: "bridge1", 365 Type: "bridge", 366 Bridge: "bridge1", 367 IpMasq: true, 368 IsGateway: true, 369 Ipam: &ipamTemplateT{ 370 Type: "host-local", 371 Subnet: "11.11.5.0/24", 372 Routes: []map[string]string{ 373 {"dst": "0.0.0.0/0"}, 374 }, 375 }, 376 } 377 bridgeSamePortFwdCase = PortFwdCase{"11.11.5.1", 0, "", "--net=" + portFwdBridge.Name, true} 378 bridgeDiffPortFwdCase = PortFwdCase{"11.11.5.1", 1024, "", "--net=" + portFwdBridge.Name, true} 379 bridgeLoSamePortFwdCase = PortFwdCase{"127.0.0.1", 0, "", "--net=" + portFwdBridge.Name, true} 380 bridgeLoDiffPortFwdCase = PortFwdCase{"127.0.0.1", 1024, "", "--net=" + portFwdBridge.Name, true} 381 ) 382 383 func (ct PortFwdCase) Execute(t *testing.T) { 384 ctx := testutils.NewRktRunCtx() 385 defer ctx.Cleanup() 386 387 prepareTestNet(t, ctx, portFwdBridge) 388 389 httpPort, err := testutils.GetNextFreePort4Banned(bannedPorts) 390 if err != nil { 391 t.Fatalf("%v", err) 392 } 393 bannedPorts[httpPort] = struct{}{} 394 395 httpServePort := ct.HttpServePort 396 if httpServePort == 0 { 397 httpServePort = httpPort 398 } 399 400 httpServeAddr := fmt.Sprintf("0.0.0.0:%d", httpServePort) 401 testImageArgs := []string{ 402 fmt.Sprintf("--ports=http,protocol=tcp,port=%d", httpServePort), 403 fmt.Sprintf("--exec=/inspect --serve-http=%v", httpServeAddr), 404 } 405 t.Logf("testImageArgs: %v", testImageArgs) 406 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 407 defer os.Remove(testImage) 408 409 cmd := fmt.Sprintf( 410 "%s --debug --insecure-options=image run --port=http:%s%d %s --mds-register=false %s", 411 ctx.Cmd(), ct.ListenAddress, httpPort, ct.RktArg, testImage) 412 child := spawnOrFail(t, cmd) 413 414 httpGetAddr := fmt.Sprintf("http://%v:%v", ct.HttpGetIP, httpPort) 415 416 ga := testutils.NewGoroutineAssistant(t) 417 ga.Add(2) 418 419 // Child opens the server 420 go func() { 421 defer ga.Done() 422 ga.WaitOrFail(child) 423 }() 424 425 // Host connects to the child via the forward port on localhost 426 go func() { 427 defer ga.Done() 428 expectedRegex := `serving on` 429 _, out, err := expectRegexWithOutput(child, expectedRegex) 430 if err != nil { 431 ga.Fatalf("Error: %v\nOutput: %v", err, out) 432 } 433 body, err := testutils.HTTPGet(httpGetAddr) 434 switch { 435 case err != nil && ct.ShouldSucceed: 436 ga.Fatalf("%v\n", err) 437 case err == nil && !ct.ShouldSucceed: 438 ga.Fatalf("HTTP-Get to %q should have failed! But received %q", httpGetAddr, body) 439 case err != nil && !ct.ShouldSucceed: 440 t.Logf("HTTP-Get failed, as expected: %v", err) 441 default: 442 t.Logf("HTTP-Get received: %s", body) 443 } 444 }() 445 446 ga.Wait() 447 } 448 449 type portFwdTest []PortFwdCase 450 451 func (ct portFwdTest) Execute(t *testing.T) { 452 for _, testCase := range ct { 453 testCase.Execute(t) 454 } 455 } 456 457 /* 458 * Net port forwarding connectivity 459 * --- 460 * Container launches http server on all its interfaces 461 * Host must be able to connect to container's http server on it's own interfaces 462 */ 463 func NewNetPortFwdConnectivityTest(cases ...PortFwdCase) testutils.Test { 464 return portFwdTest(cases) 465 } 466 467 func writeNetwork(t *testing.T, net networkTemplateT, netd string) error { 468 var err error 469 path := filepath.Join(netd, net.Name+".conf") 470 file, err := os.Create(path) 471 if err != nil { 472 t.Errorf("%v", err) 473 } 474 475 b, err := json.Marshal(net) 476 if err != nil { 477 return err 478 } 479 480 fmt.Println("Writing", net.Name, "to", path) 481 _, err = file.Write(b) 482 if err != nil { 483 return err 484 } 485 486 return nil 487 } 488 489 // Compute what we should pass to the --net parameter at runtime 490 func (nt *networkTemplateT) NetParameter() string { 491 out := nt.Name 492 493 if len(nt.Args) > 0 { 494 out += ":" + strings.Join(nt.Args, ";") 495 } 496 497 return out 498 } 499 500 type networkTemplateT struct { 501 Name string 502 Type string 503 SubnetFile string `json:"subnetFile,omitempty"` 504 Master string `json:"master,omitempty"` 505 IpMasq bool 506 IsGateway bool 507 Bridge string `json:"bridge,omitempty"` 508 Ipam *ipamTemplateT `json:",omitempty"` 509 Delegate *delegateTemplateT `json:",omitempty"` 510 Args []string `json:"args,omitempty"` // Arguments to the CNI plugin, array of "foo=bar" pairs 511 } 512 513 type ipamTemplateT struct { 514 Type string `json:",omitempty"` 515 Subnet string `json:"subnet,omitempty"` 516 Routes []map[string]string `json:"routes,omitempty"` 517 } 518 519 type delegateTemplateT struct { 520 Bridge string `json:"bridge,omitempty"` 521 IsDefaultGateway bool `json:"isDefaultGateway"` 522 } 523 524 func TestNetTemplates(t *testing.T) { 525 net := networkTemplateT{ 526 Name: "ptp0", 527 Type: "ptp", 528 Args: []string{"two=three", "black=white"}, 529 Ipam: &ipamTemplateT{ 530 Type: "host-local", 531 Subnet: "11.11.3.0/24", 532 Routes: []map[string]string{{"dst": "0.0.0.0/0"}}, 533 }, 534 } 535 536 b, err := json.Marshal(net) 537 if err != nil { 538 t.Fatalf("%v", err) 539 } 540 expected := `{"Name":"ptp0","Type":"ptp","IpMasq":false,"IsGateway":false,"Ipam":{"Type":"host-local","subnet":"11.11.3.0/24","routes":[{"dst":"0.0.0.0/0"}]},"args":["two=three","black=white"]}` 541 if string(b) != expected { 542 t.Fatalf("Template extected:\n%v\ngot:\n%v\n", expected, string(b)) 543 } 544 } 545 546 // The format of the logfile from cniproxy 547 type cniProxyResult struct { 548 PluginPath string `json:"pluginPath"` 549 Stdin string `json:"stdin"` 550 Stdout string `json:"stdout"` 551 Stderr string `json:"stderr"` 552 ExitCode int `json:"exitCode"` 553 Env []string `json:"env"` 554 EnvMap map[string]string `json:"-"` 555 } 556 557 func parseCNIProxyLog(filepath string) (*cniProxyResult, error) { 558 fp, err := os.Open(filepath) 559 defer fp.Close() 560 if err != nil { 561 return nil, err 562 } 563 res := new(cniProxyResult) 564 err = json.NewDecoder(fp).Decode(res) 565 if err != nil { 566 return nil, err 567 } 568 res.EnvMap = make(map[string]string) 569 570 for _, envstr := range res.Env { 571 vals := strings.SplitN(envstr, "=", 2) 572 if len(vals) != 2 { 573 continue 574 } 575 res.EnvMap[vals[0]] = vals[1] 576 } 577 578 return res, nil 579 } 580 581 func prepareTestNet(t *testing.T, ctx *testutils.RktRunCtx, nt networkTemplateT) (netdir string) { 582 configdir := ctx.LocalDir() 583 netdir = filepath.Join(configdir, "net.d") 584 err := os.MkdirAll(netdir, 0644) 585 if err != nil { 586 t.Fatalf("Cannot create netdir: %v", err) 587 } 588 err = writeNetwork(t, nt, netdir) 589 if err != nil { 590 t.Fatalf("Cannot write network file: %v", err) 591 } 592 593 // If we're proxying the CNI call, then make sure it's in the netdir 594 if nt.Type == "cniproxy" { 595 dest := filepath.Join(netdir, "cniproxy") 596 err := fileutil.CopyRegularFile(testutils.GetValueFromEnvOrPanic("RKT_CNI_PROXY"), dest) 597 if err != nil { 598 t.Fatalf("Cannot copy cniproxy") 599 } 600 os.Chmod(dest, 0755) 601 if err != nil { 602 t.Fatalf("Cannot chmod cniproxy") 603 } 604 } 605 return netdir 606 } 607 608 /* 609 * Two containers spawn in the same custom network. 610 * --- 611 * Container 1 opens the http server 612 * Container 2 fires a HTTPGet on it 613 * The body of the HTTPGet is Container 1's hostname, which must match 614 */ 615 func testNetCustomDual(t *testing.T, nt networkTemplateT) { 616 httpPort, err := testutils.GetNextFreePort4() 617 if err != nil { 618 t.Fatalf("%v", err) 619 } 620 621 ctx := testutils.NewRktRunCtx() 622 defer ctx.Cleanup() 623 624 prepareTestNet(t, ctx, nt) 625 626 container1IPv4, container1Hostname := make(chan string), make(chan string) 627 ga := testutils.NewGoroutineAssistant(t) 628 ga.Add(2) 629 630 go func() { 631 defer ga.Done() 632 httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) 633 testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0 --serve-http=" + httpServeAddr} 634 testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...) 635 defer os.Remove(testImage) 636 637 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage) 638 child := ga.SpawnOrFail(cmd) 639 defer ga.WaitOrFail(child) 640 641 expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` 642 result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 643 if err != nil { 644 ga.Fatalf("Error: %v\nOutput: %v", err, out) 645 } 646 container1IPv4 <- result[1] 647 expectedRegex = ` ([a-zA-Z0-9\-]*): serving on` 648 result, out, err = expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 649 if err != nil { 650 ga.Fatalf("Error: %v\nOutput: %v", err, out) 651 } 652 container1Hostname <- result[1] 653 }() 654 655 go func() { 656 defer ga.Done() 657 658 var httpGetAddr string 659 httpGetAddr = fmt.Sprintf("http://%v:%v", <-container1IPv4, httpPort) 660 661 testImageArgs := []string{"--exec=/inspect --get-http=" + httpGetAddr} 662 testImage := patchTestACI("rkt-inspect-networking2.aci", testImageArgs...) 663 defer os.Remove(testImage) 664 665 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage) 666 child := ga.SpawnOrFail(cmd) 667 defer ga.WaitOrFail(child) 668 669 expectedHostname := <-container1Hostname 670 expectedRegex := `HTTP-Get received: (.*?)\r` 671 result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 20*time.Second) 672 if err != nil { 673 ga.Fatalf("Error: %v\nOutput: %v", err, out) 674 } 675 t.Logf("HTTP-Get received: %s", result[1]) 676 receivedHostname := result[1] 677 678 if receivedHostname != expectedHostname { 679 ga.Fatalf("Received hostname `%v` doesn't match `%v`", receivedHostname, expectedHostname) 680 } 681 }() 682 683 ga.Wait() 684 } 685 686 /* 687 * Host launches http server on all interfaces in the host netns 688 * Container must be able to connect via any IP address of the host in the 689 * macvlan network, which is NAT 690 * TODO: test connection to host on an outside interface 691 */ 692 func testNetCustomNatConnectivity(t *testing.T, nt networkTemplateT) { 693 ctx := testutils.NewRktRunCtx() 694 defer ctx.Cleanup() 695 696 prepareTestNet(t, ctx, nt) 697 698 httpPort, err := testutils.GetNextFreePort4() 699 if err != nil { 700 t.Fatalf("%v", err) 701 } 702 httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) 703 httpServeTimeout := 30 704 705 nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() 706 if err != nil { 707 t.Fatalf("%v", err) 708 } 709 if nonLoIPv4 == "" { 710 t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..") 711 } 712 713 httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort) 714 t.Log("Telling the child to connect via", httpGetAddr) 715 716 ga := testutils.NewGoroutineAssistant(t) 717 ga.Add(2) 718 719 // Host opens the server 720 go func() { 721 defer ga.Done() 722 err := testutils.HTTPServe(httpServeAddr, httpServeTimeout) 723 if err != nil { 724 ga.Fatalf("Error during HTTPServe: %v", err) 725 } 726 }() 727 728 // Child connects to host 729 hostname, err := os.Hostname() 730 if err != nil { 731 panic(err) 732 } 733 734 go func() { 735 defer ga.Done() 736 testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)} 737 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 738 defer os.Remove(testImage) 739 740 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage) 741 child := ga.SpawnOrFail(cmd) 742 defer ga.WaitOrFail(child) 743 744 expectedRegex := `HTTP-Get received: (.*?)\r` 745 result, out, err := expectRegexWithOutput(child, expectedRegex) 746 if err != nil { 747 ga.Fatalf("Error: %v\nOutput: %v", err, out) 748 } 749 750 if result[1] != hostname { 751 ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname) 752 } 753 }() 754 755 ga.Wait() 756 } 757 758 //Test that the CNI execution environment matches the spec 759 func NewNetCNIEnvTest() testutils.Test { 760 return testutils.TestFunc(func(t *testing.T) { 761 ctx := testutils.NewRktRunCtx() 762 defer ctx.Cleanup() 763 764 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 765 if err != nil { 766 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 767 } 768 if iface.Name == "" { 769 t.Skipf("Cannot run test without non-lo host interface") 770 } 771 772 // Declares a network of type cniproxy, which will record state and 773 // proxy through to $X_REAL_PLUGIN 774 nt := networkTemplateT{ 775 Name: "bridge0", 776 Type: "cniproxy", 777 Args: []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge"}, 778 IpMasq: true, 779 IsGateway: true, 780 Master: iface.Name, 781 Ipam: &ipamTemplateT{ 782 Type: "host-local", 783 Subnet: "11.11.3.0/24", 784 Routes: []map[string]string{ 785 {"dst": "0.0.0.0/0"}, 786 }, 787 }, 788 } 789 790 // bring the networking up, copy the proxy 791 netdir := prepareTestNet(t, ctx, nt) 792 793 appCmd := "--exec=/inspect -- --print-defaultgwv4 " 794 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s %s", 795 ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) 796 child := spawnOrFail(t, cmd) 797 798 expectedRegex := "DefaultGWv4: 11.11.3.1" 799 800 _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 801 if err != nil { 802 t.Fatalf("Error: %v\nOutput: %v", err, out) 803 } 804 waitOrFail(t, child, 0) 805 806 // Parse the log file 807 cniLogFilename := filepath.Join(netdir, "output.json") 808 proxyLog, err := parseCNIProxyLog(cniLogFilename) 809 if err != nil { 810 t.Fatal("Failed to read cniproxy ADD log", err) 811 } 812 os.Remove(cniLogFilename) 813 814 // Check that the stdin matches the network config file 815 expectedConfig, err := ioutil.ReadFile(filepath.Join(netdir, nt.Name+".conf")) 816 if err != nil { 817 t.Fatal("Failed to read network configuration", err) 818 } 819 820 if string(expectedConfig) != proxyLog.Stdin { 821 t.Fatalf("CNI plugin stdin incorrect, expected <<%v>>, actual <<%v>>", expectedConfig, proxyLog.Stdin) 822 } 823 824 // compare the CNI env against a set of regexes 825 checkEnv := func(step string, expectedEnv, actualEnv map[string]string) { 826 for k, v := range expectedEnv { 827 actual, exists := actualEnv[k] 828 if !exists { 829 t.Fatalf("Step %s, expected proxy CNI arg %s but not found", step, k) 830 } 831 832 re, err := regexp.Compile(v) 833 if err != nil { 834 t.Fatalf("Step %s, invalid CNI env regex for key %s %v", step, k, err) 835 } 836 found := re.FindString(actual) 837 if found == "" { 838 t.Fatalf("step %s cni environment %s was %s but expected pattern %s", step, k, actual, v) 839 } 840 } 841 } 842 843 expectedEnv := map[string]string{ 844 "CNI_VERSION": `^0\.1\.0$`, 845 "CNI_COMMAND": `^ADD$`, 846 "CNI_IFNAME": `^eth\d$`, 847 "CNI_PATH": "^" + netdir + ":/usr/lib/rkt/plugins/net:stage1/rootfs/usr/lib/rkt/plugins/net$", 848 "CNI_NETNS": `^/var/run/netns/cni-`, 849 "CNI_CONTAINERID": `^[a-fA-F0-9-]{36}$`, //UUID, close enough 850 } 851 checkEnv("add", expectedEnv, proxyLog.EnvMap) 852 853 /* 854 Run rkt GC, ensure the CNI invocation looks sane 855 */ 856 ctx.RunGC() 857 proxyLog, err = parseCNIProxyLog(cniLogFilename) 858 if err != nil { 859 t.Fatal("Failed to read cniproxy DEL log", err) 860 } 861 os.Remove(cniLogFilename) 862 863 expectedEnv["CNI_COMMAND"] = `^DEL$` 864 checkEnv("del", expectedEnv, proxyLog.EnvMap) 865 866 }) 867 } 868 869 // Test that CNI invocations which return DNS information are carried through to /etc/resolv.conf 870 func NewNetCNIDNSTest() testutils.Test { 871 return testutils.TestFunc(func(t *testing.T) { 872 ctx := testutils.NewRktRunCtx() 873 defer ctx.Cleanup() 874 875 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 876 if err != nil { 877 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 878 } 879 if iface.Name == "" { 880 t.Skipf("Cannot run test without non-lo host interface") 881 } 882 883 nt := networkTemplateT{ 884 Name: "bridge0", 885 Type: "cniproxy", 886 Args: []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"}, 887 IpMasq: true, 888 IsGateway: true, 889 Master: iface.Name, 890 Ipam: &ipamTemplateT{ 891 Type: "host-local", 892 Subnet: "11.11.3.0/24", 893 Routes: []map[string]string{ 894 {"dst": "0.0.0.0/0"}, 895 }, 896 }, 897 } 898 899 // bring the networking up, copy the proxy 900 prepareTestNet(t, ctx, nt) 901 902 ga := testutils.NewGoroutineAssistant(t) 903 ga.Add(1) 904 905 go func() { 906 defer ga.Done() 907 908 appCmd := "--exec=/inspect -- --read-file --file-name=/etc/resolv.conf" 909 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s %s", 910 ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) 911 child := ga.SpawnOrFail(cmd) 912 defer ga.WaitOrFail(child) 913 914 expectedRegex := "nameserver 1.2.3.4" 915 916 _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 917 if err != nil { 918 ga.Fatalf("Error: %v\nOutput: %v", err, out) 919 } 920 }() 921 922 ga.Wait() 923 }) 924 } 925 926 // Test that `rkt run --dns` overrides CNI DNS 927 func NewNetCNIDNSArgTest() testutils.Test { 928 return testutils.TestFunc(func(t *testing.T) { 929 ctx := testutils.NewRktRunCtx() 930 defer ctx.Cleanup() 931 932 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 933 if err != nil { 934 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 935 } 936 if iface.Name == "" { 937 t.Skipf("Cannot run test without non-lo host interface") 938 } 939 940 nt := networkTemplateT{ 941 Name: "bridge0", 942 Type: "cniproxy", 943 Args: []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"}, 944 IpMasq: true, 945 IsGateway: true, 946 Master: iface.Name, 947 Ipam: &ipamTemplateT{ 948 Type: "host-local", 949 Subnet: "11.11.3.0/24", 950 Routes: []map[string]string{ 951 {"dst": "0.0.0.0/0"}, 952 }, 953 }, 954 } 955 956 // bring the networking up, copy the proxy 957 prepareTestNet(t, ctx, nt) 958 959 appCmd := "--exec=/inspect -- --read-file --file-name=/etc/resolv.conf" 960 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false --dns=244.244.244.244 %s %s", 961 ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) 962 child := spawnOrFail(t, cmd) 963 defer waitOrFail(t, child, 0) 964 965 expectedRegex := "nameserver 244.244.244.244" 966 967 _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 968 if err != nil { 969 t.Fatalf("Error: %v\nOutput: %v", err, out) 970 } 971 }) 972 } 973 974 // Test that `rkt run --dns=none` means no resolv.conf is created, even when 975 // CNI returns DNS informationparseHostsEntries(flagHosts) 976 func NewNetCNIDNSArgNoneTest() testutils.Test { 977 return testutils.TestFunc(func(t *testing.T) { 978 ctx := testutils.NewRktRunCtx() 979 defer ctx.Cleanup() 980 981 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 982 if err != nil { 983 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 984 } 985 if iface.Name == "" { 986 t.Skipf("Cannot run test without non-lo host interface") 987 } 988 989 nt := networkTemplateT{ 990 Name: "bridge0", 991 Type: "cniproxy", 992 Args: []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"}, 993 IpMasq: true, 994 IsGateway: true, 995 Master: iface.Name, 996 Ipam: &ipamTemplateT{ 997 Type: "host-local", 998 Subnet: "11.11.3.0/24", 999 Routes: []map[string]string{ 1000 {"dst": "0.0.0.0/0"}, 1001 }, 1002 }, 1003 } 1004 1005 // bring the networking up, copy the proxy 1006 prepareTestNet(t, ctx, nt) 1007 1008 appCmd := "--exec=/inspect -- --stat-file --file-name=/etc/resolv.conf" 1009 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false --dns=none %s %s", 1010 ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) 1011 child := spawnOrFail(t, cmd) 1012 ctx.RegisterChild(child) 1013 defer waitOrFail(t, child, 254) 1014 1015 expectedRegex := `Cannot stat file "/etc/resolv.conf": stat /etc/resolv.conf: no such file or directory` 1016 1017 _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 1018 if err != nil { 1019 t.Fatalf("Error: %v\nOutput: %v", err, out) 1020 } 1021 }) 1022 } 1023 1024 func NewNetCustomPtpTest(runCustomDual bool) testutils.Test { 1025 return testutils.TestFunc(func(t *testing.T) { 1026 nt := networkTemplateT{ 1027 Name: "ptp0", 1028 Type: "ptp", 1029 IpMasq: true, 1030 Ipam: &ipamTemplateT{ 1031 Type: "host-local", 1032 Subnet: "11.11.1.0/24", 1033 Routes: []map[string]string{ 1034 {"dst": "0.0.0.0/0"}, 1035 }, 1036 }, 1037 } 1038 testNetCustomNatConnectivity(t, nt) 1039 if runCustomDual { 1040 testNetCustomDual(t, nt) 1041 } 1042 }) 1043 } 1044 1045 func NewNetCustomMacvlanTest() testutils.Test { 1046 return testutils.TestFunc(func(t *testing.T) { 1047 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 1048 if err != nil { 1049 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 1050 } 1051 if iface.Name == "" { 1052 t.Skipf("Cannot run test without non-lo host interface") 1053 } 1054 1055 nt := networkTemplateT{ 1056 Name: "macvlan0", 1057 Type: "macvlan", 1058 Master: iface.Name, 1059 Ipam: &ipamTemplateT{ 1060 Type: "host-local", 1061 Subnet: "11.11.2.0/24", 1062 }, 1063 } 1064 testNetCustomDual(t, nt) 1065 }) 1066 } 1067 1068 func NewNetCustomBridgeTest() testutils.Test { 1069 return testutils.TestFunc(func(t *testing.T) { 1070 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 1071 if err != nil { 1072 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 1073 } 1074 if iface.Name == "" { 1075 t.Skipf("Cannot run test without non-lo host interface") 1076 } 1077 1078 nt := networkTemplateT{ 1079 Name: "bridge0", 1080 Type: "bridge", 1081 IpMasq: true, 1082 IsGateway: true, 1083 Master: iface.Name, 1084 Ipam: &ipamTemplateT{ 1085 Type: "host-local", 1086 Subnet: "11.11.3.0/24", 1087 Routes: []map[string]string{ 1088 {"dst": "0.0.0.0/0"}, 1089 }, 1090 }, 1091 } 1092 testNetCustomNatConnectivity(t, nt) 1093 testNetCustomDual(t, nt) 1094 }) 1095 } 1096 1097 func NewNetOverrideTest() testutils.Test { 1098 return testutils.TestFunc(func(t *testing.T) { 1099 ctx := testutils.NewRktRunCtx() 1100 defer ctx.Cleanup() 1101 1102 iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) 1103 if err != nil { 1104 t.Fatalf("Error while getting non-lo host interface: %v\n", err) 1105 } 1106 if iface.Name == "" { 1107 t.Skipf("Cannot run test without non-lo host interface") 1108 } 1109 1110 nt := networkTemplateT{ 1111 Name: "overridemacvlan", 1112 Type: "macvlan", 1113 Master: iface.Name, 1114 Ipam: &ipamTemplateT{ 1115 Type: "host-local", 1116 Subnet: "11.11.4.0/24", 1117 }, 1118 } 1119 1120 prepareTestNet(t, ctx, nt) 1121 1122 testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0"} 1123 testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...) 1124 defer os.Remove(testImage) 1125 1126 expectedIP := "11.11.4.244" 1127 1128 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=\"%s:IP=%s\" --mds-register=false %s", ctx.Cmd(), nt.Name, expectedIP, testImage) 1129 child := spawnOrFail(t, cmd) 1130 defer waitOrFail(t, child, 0) 1131 1132 expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` 1133 result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 1134 if err != nil { 1135 t.Fatalf("Error: %v\nOutput: %v", err, out) 1136 return 1137 } 1138 1139 containerIP := result[1] 1140 if expectedIP != containerIP { 1141 t.Fatalf("overriding IP did not work: Got %q but expected %q", containerIP, expectedIP) 1142 } 1143 }) 1144 } 1145 1146 /* 1147 * Pass the IP arg to the default networks, ensure it works 1148 */ 1149 func NewNetDefaultIPArgTest() testutils.Test { 1150 doTest := func(netArg, expectedIP string, t *testing.T) { 1151 ctx := testutils.NewRktRunCtx() 1152 defer ctx.Cleanup() 1153 1154 appCmd := "--exec=/inspect -- --print-ipv4=eth0" 1155 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=\"%s\" --mds-register=false %s %s", 1156 ctx.Cmd(), netArg, getInspectImagePath(), appCmd) 1157 child := spawnOrFail(t, cmd) 1158 defer waitOrFail(t, child, 0) 1159 1160 expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` 1161 result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) 1162 if err != nil { 1163 t.Fatalf("Error: %v\nOutput: %v", err, out) 1164 return 1165 } 1166 1167 containerIP := result[1] 1168 if expectedIP != containerIP { 1169 t.Fatalf("--net=%s setting IP failed: Got %q but expected %q", netArg, containerIP, expectedIP) 1170 } 1171 } 1172 return testutils.TestFunc(func(t *testing.T) { 1173 doTest("default:IP=172.16.28.123", "172.16.28.123", t) 1174 doTest("default-restricted:IP=172.31.42.42", "172.31.42.42", t) 1175 }) 1176 } 1177 1178 /* 1179 * Try and start two containers with the same IP, ensure 1180 * the second invocation fails 1181 */ 1182 func NewNetIPConflictTest() testutils.Test { 1183 return testutils.TestFunc(func(t *testing.T) { 1184 ctx := testutils.NewRktRunCtx() 1185 defer ctx.Cleanup() 1186 1187 // Launch one container and grab the IP it uses -- and have it idle 1188 appCmd := "--exec=/inspect -- --print-ipv4=eth0 --serve-http=0.0.0.0:80" 1189 cmd1 := fmt.Sprintf("%s --debug --insecure-options=image run --mds-register=false %s %s", 1190 ctx.Cmd(), getInspectImagePath(), appCmd) 1191 1192 child1 := spawnOrFail(t, cmd1) 1193 1194 expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` 1195 result, out, err := expectRegexTimeoutWithOutput(child1, expectedRegex, 30*time.Second) 1196 if err != nil { 1197 t.Fatalf("Error: %v\nOutput: %v", err, out) 1198 return 1199 } 1200 ip := result[1] 1201 1202 // Launch a second container with the same IP 1203 cmd2 := fmt.Sprintf("%s --debug --insecure-options=image run --net=\"default:IP=%s\" %s --exec=/inspect -- --print-ipv4=eth0", 1204 ctx.Cmd(), ip, getInspectImagePath()) 1205 child2 := spawnOrFail(t, cmd2) 1206 1207 expectedOutput := fmt.Sprintf(`requested IP address "%s" is not available in network: default`, ip) 1208 1209 _, out, err = expectRegexTimeoutWithOutput(child2, expectedOutput, 10*time.Second) 1210 if err != nil { 1211 t.Fatalf("Error: %v\nOutput: %v", err, out) 1212 return 1213 } 1214 1215 // Clean up 1216 waitOrFail(t, child2, 254) 1217 syscall.Kill(child1.Cmd.Process.Pid, syscall.SIGTERM) 1218 waitOrFail(t, child1, 0) 1219 }) 1220 } 1221 1222 func NewTestNetLongName() testutils.Test { 1223 return testutils.TestFunc(func(t *testing.T) { 1224 nt := networkTemplateT{ 1225 Name: "thisnameiswaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaytoolong", 1226 Type: "ptp", 1227 IpMasq: true, 1228 Ipam: &ipamTemplateT{ 1229 Type: "host-local", 1230 Subnet: "11.11.6.0/24", 1231 Routes: []map[string]string{ 1232 {"dst": "0.0.0.0/0"}, 1233 }, 1234 }, 1235 } 1236 testNetCustomNatConnectivity(t, nt) 1237 }) 1238 } 1239 1240 /* 1241 * mockFlannelNetwork creates fake flannel network status file and configuration pointing to this network. 1242 * We won't have connectivity, but we could check if: netName was correct and if default gateway was set. 1243 */ 1244 func mockFlannelNetwork(t *testing.T, ctx *testutils.RktRunCtx) (string, networkTemplateT, error) { 1245 // write fake flannel info 1246 subnetPath := filepath.Join(ctx.DataDir(), "subnet.env") 1247 file, err := os.Create(subnetPath) 1248 if err != nil { 1249 return "", networkTemplateT{}, err 1250 } 1251 mockedFlannel := strings.Join([]string{ 1252 "FLANNEL_NETWORK=11.11.0.0/16", 1253 "FLANNEL_SUBNET=11.11.3.1/24", 1254 "FLANNEL_MTU=1472", 1255 "FLANNEL_IPMASQ=true", 1256 }, "\n") 1257 if _, err = file.WriteString(mockedFlannel); err != nil { 1258 return "", networkTemplateT{}, err 1259 } 1260 1261 file.Close() 1262 1263 // write net config for "flannel" based network 1264 ntFlannel := networkTemplateT{ 1265 Name: "rkt.kubernetes.io", 1266 Type: "flannel", 1267 SubnetFile: subnetPath, 1268 Delegate: &delegateTemplateT{ 1269 IsDefaultGateway: true, 1270 }, 1271 } 1272 1273 netdir := prepareTestNet(t, ctx, ntFlannel) 1274 1275 return netdir, ntFlannel, nil 1276 } 1277 1278 /* 1279 * NewNetPreserveNetNameTest checks if netName is set if network is configured via flannel 1280 */ 1281 func NewNetPreserveNetNameTest() testutils.Test { 1282 return testutils.TestFunc(func(t *testing.T) { 1283 ctx := testutils.NewRktRunCtx() 1284 defer ctx.Cleanup() 1285 1286 _, ntFlannel, err := mockFlannelNetwork(t, ctx) 1287 if err != nil { 1288 t.Errorf("Can't mock flannel network: %v", err) 1289 } 1290 1291 defer os.Remove(ntFlannel.SubnetFile) 1292 1293 podUUIDFile := filepath.Join(ctx.DataDir(), "pod_uuid") 1294 defer os.Remove(podUUIDFile) 1295 1296 // start container with 'flannel' network 1297 testImageArgs := []string{"--exec=/inspect"} 1298 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 1299 defer os.Remove(testImage) 1300 1301 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --uuid-file-save=%s --net=%s --mds-register=false %s", ctx.Cmd(), podUUIDFile, ntFlannel.Name, testImage) 1302 spawnAndWaitOrFail(t, cmd, 0) 1303 1304 podUUID, err := ioutil.ReadFile(podUUIDFile) 1305 if err != nil { 1306 t.Fatalf("Can't read pod UUID: %v", err) 1307 } 1308 1309 // read net-info.json created for pod 1310 podDir := filepath.Join(ctx.DataDir(), "pods", "run", string(podUUID)) 1311 podDirfd, err := syscall.Open(podDir, syscall.O_RDONLY|syscall.O_DIRECTORY, 0) 1312 if err != nil { 1313 t.Fatalf("Can't open pod directory for reading! %v", err) 1314 } 1315 1316 info, err := netinfo.LoadAt(podDirfd) 1317 if err != nil { 1318 t.Fatalf("Can't open net-info.json for reading: %v", err) 1319 } 1320 1321 if len(info) != 1 { 1322 t.Fatalf("Incorrect number of networks: %v", len(info)) 1323 } 1324 1325 if info[0].NetName != ntFlannel.Name { 1326 t.Fatalf("Network '%s' not found!\nnetInfo[0]: %v\nnetInfo[1]: %v", ntFlannel.Name, info[0], info[1]) 1327 } 1328 }) 1329 } 1330 1331 /* 1332 * NewNetDefaultGWTest checks if default gateway is correct if only configured network is one provided by flannel. 1333 */ 1334 func NewNetDefaultGWTest() testutils.Test { 1335 return testutils.TestFunc(func(t *testing.T) { 1336 ctx := testutils.NewRktRunCtx() 1337 defer ctx.Cleanup() 1338 1339 _, ntFlannel, err := mockFlannelNetwork(t, ctx) 1340 if err != nil { 1341 t.Errorf("Can't mock flannel network: %v", err) 1342 } 1343 1344 defer os.Remove(ntFlannel.SubnetFile) 1345 1346 testImageArgs := []string{"--exec=/inspect --print-defaultgwv4"} 1347 testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) 1348 defer os.Remove(testImage) 1349 1350 cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%s --mds-register=false %s", ctx.Cmd(), ntFlannel.Name, testImage) 1351 child := spawnOrFail(t, cmd) 1352 defer waitOrFail(t, child, 0) 1353 1354 expectedRegex := `DefaultGWv4: (\d+\.\d+\.\d+\.\d+)` 1355 if _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, time.Minute); err != nil { 1356 t.Fatalf("No default gateway!\nError: %v\nOutput: %v", err, out) 1357 } 1358 }) 1359 }