/* * Default-restricted net * --- * Container launches http server on all its interfaces * Host must be able to connects to container's http server via container's * eth0's IPv4 * TODO: verify that the container isn't NATed */ func TestNetDefaultRestrictedConnectivity(t *testing.T) { ctx := newRktRunCtx() defer ctx.cleanup() f := func(argument string) { httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) iface := "eth0" testImageArgs := []string{fmt.Sprintf("--exec=/inspect --print-ipv4=%v --serve-http=%v", iface, httpServeAddr)} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run %s --mds-register=false %s", ctx.cmd(), argument, testImage) child := spawnOrFail(t, cmd) expectedRegex := `IPv4: (.*)\r` result, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) } httpGetAddr := fmt.Sprintf("http://%v:%v", result[1], httpPort) ga := testutils.NewGoroutineAssistant(t) // Child opens the server ga.Add(1) go func() { defer ga.Done() waitOrFail(t, child, true) }() // Host connects to the child ga.Add(1) go func() { defer ga.Done() expectedRegex := `serving on` _, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } body, err := testutils.HttpGet(httpGetAddr) if err != nil { ga.Fatalf("%v\n", err) return } log.Printf("HTTP-Get received: %s", body) if err != nil { ga.Fatalf("%v\n", err) } }() ga.Wait() } f("--net=default-restricted") f("--net=none") }
/* * Default-restricted private-net * --- * Container launches http server on all its interfaces * Host must be able to connects to container's http server via container's * eth0's IPv4 * TODO: verify that the container isn't NATed */ func TestPrivateNetDefaultRestrictedConnectivity(t *testing.T) { httpServeAddr := "0.0.0.0:54321" iface := "eth0" testImageArgs := []string{fmt.Sprintf("--exec=/inspect --print-ipv4=%v --serve-http=%v", iface, httpServeAddr)} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) ctx := newRktRunCtx() defer ctx.cleanup() defer ctx.reset() cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run --private-net=default-restricted --mds-register=false %s", ctx.cmd(), testImage) t.Logf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { t.Fatalf("Cannot exec rkt: %v", err) } expectedRegex := `IPv4: (.*)\r` result, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) } httpGetAddr := fmt.Sprintf("http://%v:54321", result[1]) ga := testutils.NewGoroutineAssistant(t) // Child opens the server ga.Add(1) go func() { defer ga.Done() err = child.Wait() if err != nil { ga.Fatalf("rkt didn't terminate correctly: %v", err) } }() // Host connects to the child ga.Add(1) go func() { defer ga.Done() expectedRegex := `serving on` _, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } body, err := testutils.HttpGet(httpGetAddr) if err != nil { ga.Fatalf("%v\n", err) return } log.Printf("HTTP-Get received: %s", body) if err != nil { ga.Fatalf("%v\n", err) } }() ga.Wait() }
// Test that CNI invocations which return DNS information are carried through to /etc/resolv.conf func NewNetCNIDNSTest() testutils.Test { return testutils.TestFunc(func(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) if err != nil { t.Fatalf("Error while getting non-lo host interface: %v\n", err) } if iface.Name == "" { t.Skipf("Cannot run test without non-lo host interface") } nt := networkTemplateT{ Name: "bridge0", Type: "cniproxy", Args: []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge", "X_ADD_DNS=1"}, IpMasq: true, IsGateway: true, Master: iface.Name, Ipam: &ipamTemplateT{ Type: "host-local", Subnet: "11.11.3.0/24", Routes: []map[string]string{ {"dst": "0.0.0.0/0"}, }, }, } // bring the networking up, copy the proxy netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) ga := testutils.NewGoroutineAssistant(t) ga.Add(1) go func() { defer ga.Done() appCmd := "--exec=/inspect -- --read-file --file-name=/etc/resolv.conf" cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s %s", ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) child := ga.SpawnOrFail(cmd) defer ga.WaitOrFail(child) expectedRegex := "nameserver 1.2.3.4" _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } }() ga.Wait() }) }
/* * Host networking * --- * Container launches http server which must be reachable by the host via the * localhost address */ func TestNetHostConnectivity(t *testing.T) { httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpGetAddr := fmt.Sprintf("http://127.0.0.1:%v", httpPort) testImageArgs := []string{"--exec=/inspect --serve-http=" + httpServeAddr} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) ctx := newRktRunCtx() defer ctx.cleanup() cmd := fmt.Sprintf("%s --net=host --debug --insecure-skip-verify run --mds-register=false %s", ctx.cmd(), testImage) t.Logf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { t.Fatalf("Cannot exec rkt: %v", err) } ga := testutils.NewGoroutineAssistant(t) // Child opens the server ga.Add(1) go func() { defer ga.Done() err = child.Wait() if err != nil { ga.Fatalf("rkt didn't terminate correctly: %v", err) return } }() // Host connects to the child ga.Add(1) go func() { defer ga.Done() expectedRegex := `serving on` _, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } body, err := testutils.HttpGet(httpGetAddr) if err != nil { ga.Fatalf("%v\n", err) return } log.Printf("HTTP-Get received: %s", body) }() ga.Wait() }
/* * Host networking * --- * Container launches http server which must be reachable by the host via the * localhost address */ func NewNetHostConnectivityTest() testutils.Test { return testutils.TestFunc(func(t *testing.T) { logger.SetLogger(t) httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpGetAddr := fmt.Sprintf("http://127.0.0.1:%v", httpPort) testImageArgs := []string{"--exec=/inspect --serve-http=" + httpServeAddr} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() cmd := fmt.Sprintf("%s --net=host --debug --insecure-options=image run --mds-register=false %s", ctx.Cmd(), testImage) child := spawnOrFail(t, cmd) ctx.RegisterChild(child) ga := testutils.NewGoroutineAssistant(t) ga.Add(2) // Child opens the server go func() { defer ga.Done() ga.WaitOrFail(child) }() // Host connects to the child go func() { defer ga.Done() expectedRegex := `serving on` _, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } body, err := testutils.HTTPGet(httpGetAddr) if err != nil { ga.Fatalf("%v\n", err) } t.Logf("HTTP-Get received: %s", body) }() ga.Wait() }) }
func TestDBRace(t *testing.T) { // TODO(sgotti) this will not find concurrent accesses to ql db from // multiple processes using multiple goroutines. A test that spawns at // least two processes using multiple goroutines is needed. // See https://github.com/coreos/rkt/pull/2391 oldGoMaxProcs := runtime.GOMAXPROCS(runtime.NumCPU()) defer runtime.GOMAXPROCS(oldGoMaxProcs) dir, err := ioutil.TempDir("", "rkt_db_test") if err != nil { t.Fatalf("Unexpected error: %v", err) } defer os.RemoveAll(dir) db, err := NewDB(dir) if err != nil { t.Fatalf("Unexpected error: %v", err) } // Create the table. createTable(db, t) // Insert values concurrently. ga := testutils.NewGoroutineAssistant(t) runs := 100 ga.Add(runs) for i := 0; i < runs; i++ { go func() { if err := insertValue(db); err != nil { ga.Fatalf("Failed to insert value: %v", err) } ga.Done() }() } ga.Wait() // Check the final values. maxCount := getMaxCount(db, t) if maxCount != runs { t.Errorf("Expected: %v, saw: %v", runs, maxCount) } }
func TestDBRace(t *testing.T) { oldGoMaxProcs := runtime.GOMAXPROCS(runtime.NumCPU()) defer runtime.GOMAXPROCS(oldGoMaxProcs) dir, err := ioutil.TempDir("", "rkt_db_test") if err != nil { t.Fatalf("Unexpected error: %v", err) } defer os.RemoveAll(dir) db, err := NewDB(dir) if err != nil { t.Fatalf("Unexpected error: %v", err) } // Create the table. createTable(db, t) // Insert values concurrently. ga := testutils.NewGoroutineAssistant(t) runs := 100 ga.Add(runs) for i := 0; i < runs; i++ { go func() { if err := insertValue(db); err != nil { ga.Fatalf("Failed to insert value: %v", err) } ga.Done() }() } ga.Wait() // Check the final values. maxCount := getMaxCount(db, t) if maxCount != runs { t.Errorf("Expected: %v, saw: %v", runs, maxCount) } }
/* * Host launches http server on all interfaces in the host netns * Container must be able to connect via any IP address of the host in the * macvlan network, which is NAT * TODO: test connection to host on an outside interface */ func testNetCustomNatConnectivity(t *testing.T, nt networkTemplateT) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() prepareTestNet(t, ctx, nt) httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() if err != nil { t.Fatalf("%v", err) } if nonLoIPv4 == "" { t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..") } httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort) t.Log("Telling the child to connect via", httpGetAddr) ga := testutils.NewGoroutineAssistant(t) ga.Add(2) // Host opens the server go func() { defer ga.Done() err := testutils.HTTPServe(httpServeAddr, httpServeTimeout) if err != nil { ga.Fatalf("Error during HTTPServe: %v", err) } }() // Child connects to host hostname, err := os.Hostname() if err != nil { panic(err) } go func() { defer ga.Done() testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage) child := ga.SpawnOrFail(cmd) defer ga.WaitOrFail(child) expectedRegex := `HTTP-Get received: (.*?)\r` result, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } if result[1] != hostname { ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname) } }() ga.Wait() }
/* * Two containers spawn in the same custom network. * --- * Container 1 opens the http server * Container 2 fires a HTTPGet on it * The body of the HTTPGet is Container 1's hostname, which must match */ func testNetCustomDual(t *testing.T, nt networkTemplateT) { httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() prepareTestNet(t, ctx, nt) container1IPv4, container1Hostname := make(chan string), make(chan string) ga := testutils.NewGoroutineAssistant(t) ga.Add(2) go func() { defer ga.Done() httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0 --serve-http=" + httpServeAddr} testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage) child := ga.SpawnOrFail(cmd) defer ga.WaitOrFail(child) expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } container1IPv4 <- result[1] expectedRegex = ` ([a-zA-Z0-9\-]*): serving on` result, out, err = expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } container1Hostname <- result[1] }() go func() { defer ga.Done() var httpGetAddr string httpGetAddr = fmt.Sprintf("http://%v:%v", <-container1IPv4, httpPort) testImageArgs := []string{"--exec=/inspect --get-http=" + httpGetAddr} testImage := patchTestACI("rkt-inspect-networking2.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s", ctx.Cmd(), nt.Name, testImage) child := ga.SpawnOrFail(cmd) defer ga.WaitOrFail(child) expectedHostname := <-container1Hostname expectedRegex := `HTTP-Get received: (.*?)\r` result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 20*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } t.Logf("HTTP-Get received: %s", result[1]) receivedHostname := result[1] if receivedHostname != expectedHostname { ga.Fatalf("Received hostname `%v` doesn't match `%v`", receivedHostname, expectedHostname) } }() ga.Wait() }
func (ct PortFwdCase) Execute(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() prepareTestNet(t, ctx, portFwdBridge) httpPort, err := testutils.GetNextFreePort4Banned(bannedPorts) if err != nil { t.Fatalf("%v", err) } bannedPorts[httpPort] = struct{}{} httpServePort := ct.HttpServePort if httpServePort == 0 { httpServePort = httpPort } httpServeAddr := fmt.Sprintf("0.0.0.0:%d", httpServePort) testImageArgs := []string{ fmt.Sprintf("--ports=http,protocol=tcp,port=%d", httpServePort), fmt.Sprintf("--exec=/inspect --serve-http=%v", httpServeAddr), } t.Logf("testImageArgs: %v", testImageArgs) testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf( "%s --debug --insecure-options=image run --port=http:%s%d %s --mds-register=false %s", ctx.Cmd(), ct.ListenAddress, httpPort, ct.RktArg, testImage) child := spawnOrFail(t, cmd) httpGetAddr := fmt.Sprintf("http://%v:%v", ct.HttpGetIP, httpPort) ga := testutils.NewGoroutineAssistant(t) ga.Add(2) // Child opens the server go func() { defer ga.Done() ga.WaitOrFail(child) }() // Host connects to the child via the forward port on localhost go func() { defer ga.Done() expectedRegex := `serving on` _, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } body, err := testutils.HTTPGet(httpGetAddr) switch { case err != nil && ct.ShouldSucceed: ga.Fatalf("%v\n", err) case err == nil && !ct.ShouldSucceed: ga.Fatalf("HTTP-Get to %q should have failed! But received %q", httpGetAddr, body) case err != nil && !ct.ShouldSucceed: t.Logf("HTTP-Get failed, as expected: %v", err) default: t.Logf("HTTP-Get received: %s", body) } }() ga.Wait() }
/* * Host launches http server on all interfaces in the host netns * Container must be able to connect via any IP address of the host in the * macvlan network, which is NAT * TODO: test connection to host on an outside interface */ func testPrivateNetCustomNatConnectivity(t *testing.T, nt networkTemplateT) { ctx := newRktRunCtx() defer ctx.cleanup() defer ctx.reset() netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() if err != nil { t.Fatalf("%v", err) } if nonLoIPv4 == "" { t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..") } httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort) t.Log("Telling the child to connect via", httpGetAddr) ga := testutils.NewGoroutineAssistant(t) // Host opens the server ga.Add(1) go func() { defer ga.Done() err := testutils.HttpServe(httpServeAddr, httpServeTimeout) if err != nil { t.Fatalf("Error during HttpServe: %v", err) } }() // Child connects to host ga.Add(1) hostname, err := os.Hostname() go func() { defer ga.Done() testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run --private-net=%v --mds-register=false %s", ctx.cmd(), nt.Name, testImage) t.Logf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { ga.Fatalf("Cannot exec rkt: %v", err) return } expectedRegex := `HTTP-Get received: (.*)\r` result, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } if result[1] != hostname { ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname) return } err = child.Wait() if err != nil { ga.Fatalf("rkt didn't terminate correctly: %v", err) } }() ga.Wait() }
/* * Two containers spawn in the same custom network. * --- * Container 1 opens the http server * Container 2 fires a HttpGet on it * The body of the HttpGet is Container 1's hostname, which must match */ func testPrivateNetCustomDual(t *testing.T, nt networkTemplateT) { httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } ctx := newRktRunCtx() defer ctx.cleanup() defer ctx.reset() netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) container1IPv4, container1Hostname := make(chan string), make(chan string) ga := testutils.NewGoroutineAssistant(t) ga.Add(1) go func() { defer ga.Done() httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0 --serve-http=" + httpServeAddr} testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run --private-net=%v --mds-register=false %s", ctx.cmd(), nt.Name, testImage) fmt.Printf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { ga.Fatalf("Cannot exec rkt: %v", err) return } expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } container1IPv4 <- result[1] expectedRegex = `(rkt-.*): serving on` result, out, err = expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } container1Hostname <- result[1] err = child.Wait() if err != nil { ga.Fatalf("rkt didn't terminate correctly: %v", err) } }() ga.Add(1) go func() { defer ga.Done() var httpGetAddr string httpGetAddr = fmt.Sprintf("http://%v:%v", <-container1IPv4, httpPort) testImageArgs := []string{"--exec=/inspect --get-http=" + httpGetAddr} testImage := patchTestACI("rkt-inspect-networking2.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run --private-net=%v --mds-register=false %s", ctx.cmd(), nt.Name, testImage) fmt.Printf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { t.Fatalf("Cannot exec rkt: %v", err) } expectedHostname := <-container1Hostname expectedRegex := `HTTP-Get received: (.*)\r` result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 20*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } t.Logf("HTTP-Get received: %s", result[1]) receivedHostname := result[1] if receivedHostname != expectedHostname { ga.Fatalf("Received hostname `%v` doesn't match `%v`", receivedHostname, expectedHostname) return } err = child.Wait() if err != nil { ga.Fatalf("rkt didn't terminate correctly: %v", err) } }() ga.Wait() }
//Test that the CNI execution environment matches the spec func NewNetCNIEnvTest() testutils.Test { return testutils.TestFunc(func(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() iface, _, err := testutils.GetNonLoIfaceWithAddrs(netlink.FAMILY_V4) if err != nil { t.Fatalf("Error while getting non-lo host interface: %v\n", err) } if iface.Name == "" { t.Skipf("Cannot run test without non-lo host interface") } // Declares a network of type cniproxy, which will record state and // proxy through to $X_REAL_PLUGIN nt := networkTemplateT{ Name: "bridge0", Type: "cniproxy", Args: []string{"X_LOG=output.json", "X_REAL_PLUGIN=bridge"}, IpMasq: true, IsGateway: true, Master: iface.Name, Ipam: &ipamTemplateT{ Type: "host-local", Subnet: "11.11.3.0/24", Routes: []map[string]string{ {"dst": "0.0.0.0/0"}, }, }, } // bring the networking up, copy the proxy netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) ga := testutils.NewGoroutineAssistant(t) ga.Add(1) go func() { defer ga.Done() appCmd := "--exec=/inspect -- --print-defaultgwv4 " cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false %s %s", ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) child := ga.SpawnOrFail(cmd) defer ga.WaitOrFail(child) expectedRegex := "DefaultGWv4: 11.11.3.1" _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } }() ga.Wait() // Parse the log file proxyLog, err := parseCNIProxyLog(filepath.Join(netdir, "output.json")) if err != nil { t.Fatal("Failed to read cniproxy log", err) } // Check that the stdin matches the network config file expectedConfig, err := ioutil.ReadFile(filepath.Join(netdir, nt.Name+".conf")) if err != nil { t.Fatal("Failed to read network configuration", err) } if string(expectedConfig) != proxyLog.Stdin { t.Fatalf("CNI plugin stdin incorrect, expected <<%v>>, actual <<%v>>", expectedConfig, proxyLog.Stdin) } // Check that the environment is sane - these are regexes expectedEnv := map[string]string{ "CNI_VERSION": "0.3.0", "CNI_COMMAND": "ADD", "CNI_IFNAME": `eth\d`, "CNI_PATH": netdir, "CNI_NETNS": `/var/run/netns/cni-`, "CNI_CONTAINERID": `^[a-fA-F0-9-]{36}`, //UUID, close enough } for k, v := range expectedEnv { actual, exists := proxyLog.EnvMap[k] if !exists { t.Fatalf("Expected proxy CNI arg %s but not found", k) } re, err := regexp.Compile(v) if err != nil { t.Fatal("Invalid CNI env regex", v, err) } found := re.FindString(actual) if found == "" { t.Fatalf("CNI_ARG %s was %s but expected pattern %s", k, actual, v) } } }) }
/* * Default net port forwarding connectivity * --- * Container launches http server on all its interfaces * Host must be able to connect to container's http server on it's own interfaces */ func TestNetDefaultPortFwdConnectivity(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() bannedPorts := make(map[int]struct{}, 0) f := func(httpGetIP string, rktArg string, shouldSucceed bool) { httpPort, err := testutils.GetNextFreePort4Banned(bannedPorts) if err != nil { t.Fatalf("%v", err) } bannedPorts[httpPort] = struct{}{} httpServeAddr := fmt.Sprintf("0.0.0.0:%d", httpPort) testImageArgs := []string{ fmt.Sprintf("--ports=http,protocol=tcp,port=%d", httpPort), fmt.Sprintf("--exec=/inspect --serve-http=%v", httpServeAddr), } testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) cmd := fmt.Sprintf( "%s --debug --insecure-options=image run --port=http:%d %s --mds-register=false %s", ctx.Cmd(), httpPort, rktArg, testImage) child := spawnOrFail(t, cmd) httpGetAddr := fmt.Sprintf("http://%v:%v", httpGetIP, httpPort) ga := testutils.NewGoroutineAssistant(t) ga.Add(2) // Child opens the server go func() { defer ga.Done() ga.WaitOrFail(child) }() // Host connects to the child via the forward port on localhost go func() { defer ga.Done() expectedRegex := `serving on` _, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) } body, err := testutils.HTTPGet(httpGetAddr) switch { case err != nil && shouldSucceed: ga.Fatalf("%v\n", err) case err == nil && !shouldSucceed: ga.Fatalf("HTTP-Get to %q should have failed! But received %q", httpGetAddr, body) case err != nil && !shouldSucceed: child.Close() fallthrough default: t.Logf("HTTP-Get received: %s", body) } }() ga.Wait() } f("172.16.28.1", "--net=default", true) f("127.0.0.1", "--net=default", true) // TODO: ensure that default-restricted is not accessible from non-host // f("172.16.28.1", "--net=default-restricted", true) // f("127.0.0.1", "--net=default-restricted", true) }
/* * Default net * --- * Host launches http server on all interfaces in the host netns * Container must be able to connect via any IP address of the host in the * default network, which is NATed * TODO: test connection to host on an outside interface */ func TestNetDefaultConnectivity(t *testing.T) { ctx := newRktRunCtx() defer ctx.cleanup() f := func(argument string) { httpPort, err := testutils.GetNextFreePort4() if err != nil { t.Fatalf("%v", err) } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() if err != nil { t.Fatalf("%v", err) } if nonLoIPv4 == "" { t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..") } httpGetAddr := fmt.Sprintf("http://%v:%v", nonLoIPv4, httpPort) t.Log("Telling the child to connect via", httpGetAddr) testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) ga := testutils.NewGoroutineAssistant(t) // Host opens the server ga.Add(1) go func() { defer ga.Done() err := testutils.HttpServe(httpServeAddr, httpServeTimeout) if err != nil { ga.Fatalf("Error during HttpServe: %v", err) } }() // Child connects to host ga.Add(1) hostname, err := os.Hostname() if err != nil { ga.Fatalf("Error getting hostname: %v", err) } go func() { defer ga.Done() cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run %s --mds-register=false %s", ctx.cmd(), argument, testImage) child := spawnOrFail(t, cmd) defer waitOrFail(t, child, true) expectedRegex := `HTTP-Get received: (.*)\r` result, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } if result[1] != hostname { ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname) return } }() ga.Wait() } f("--net=default") f("") }
/* * Default private-net * --- * Host launches http server on all interfaces in the host netns * Container must be able to connect via any IP address of the host in the * default network, which is NATed */ func TestPrivateNetDefaultConnectivity(t *testing.T) { httpServeAddr := "0.0.0.0:54321" httpServeTimeout := 30 ifaces, err := net.Interfaces() if err != nil { t.Fatalf("Cannot get network host's interfaces: %v", err) } var httpGetAddr string for _, iface := range ifaces[1:] { name := iface.Name ifaceIPsv4, err := testutils.GetIPsv4(name) if err != nil { t.Fatalf("Cannot get IPV4 address for interface %v: %v", name, err) } if len(ifaceIPsv4) > 0 { httpGetAddr = fmt.Sprintf("http://%v:54321", ifaceIPsv4[0]) t.Log("Telling the child to connect via", httpGetAddr) break } } if httpGetAddr == "" { t.Skipf("Can not find any NAT'able IPv4 on the host, skipping..") } testImageArgs := []string{fmt.Sprintf("--exec=/inspect --get-http=%v", httpGetAddr)} testImage := patchTestACI("rkt-inspect-networking.aci", testImageArgs...) defer os.Remove(testImage) ctx := newRktRunCtx() defer ctx.cleanup() defer ctx.reset() ga := testutils.NewGoroutineAssistant(t) // Host opens the server ga.Add(1) go func() { defer ga.Done() err := testutils.HttpServe(httpServeAddr, httpServeTimeout) if err != nil { t.Fatalf("Error during HttpServe: %v", err) } }() // Child connects to host ga.Add(1) hostname, err := os.Hostname() go func() { defer ga.Done() cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run --private-net=default --mds-register=false %s", ctx.cmd(), testImage) t.Logf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { ga.Fatalf("Cannot exec rkt: %v", err) return } expectedRegex := `HTTP-Get received: (.*)\r` result, out, err := expectRegexWithOutput(child, expectedRegex) if err != nil { ga.Fatalf("Error: %v\nOutput: %v", err, out) return } if result[1] != hostname { ga.Fatalf("Hostname received by client `%v` doesn't match `%v`", result[1], hostname) return } err = child.Wait() if err != nil { ga.Fatalf("rkt didn't terminate correctly: %v", err) } }() ga.Wait() }