From 364a32e14c9b03bcfd461af13eaa3ace9ebfdaa0 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Fri, 7 Aug 2015 01:39:43 +0200 Subject: [PATCH 1/5] testutils: allow concurrent serve with httputils Use ServeMux instead of Server which allows concurrent listeners registered for the same path. --- tests/testutils/httputils.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testutils/httputils.go b/tests/testutils/httputils.go index d8b658cd93..5dea83be12 100644 --- a/tests/testutils/httputils.go +++ b/tests/testutils/httputils.go @@ -58,13 +58,13 @@ func HttpServe(addr string, timeout int) error { sl.Stop() }() - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + serveMux := http.NewServeMux() + serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Printf("%v: Serve got a connection from %v\n", hostname, r.RemoteAddr) fmt.Fprintf(w, "%v", hostname) c <- r.RemoteAddr }) - server := http.Server{} - err = server.Serve(sl) + err = http.Serve(sl, serveMux) if err != nil && err.Error() == "Listener stopped" { err = nil } From ba9b0d461f1df0a35c7cafe650e0ce9f2d6f2ab3 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Fri, 7 Aug 2015 01:51:46 +0200 Subject: [PATCH 2/5] tests for NAT and bridge mode - general: use different port numbers for http server for each test - bridge: test ccontainer to container communication - bridge and ptp: test NAT communication with host --- tests/rkt_privatenet_test.go | 215 ++++++++++++++++++++++++++++------- 1 file changed, 177 insertions(+), 38 deletions(-) diff --git a/tests/rkt_privatenet_test.go b/tests/rkt_privatenet_test.go index e41eae0276..baec22a3d7 100644 --- a/tests/rkt_privatenet_test.go +++ b/tests/rkt_privatenet_test.go @@ -28,6 +28,14 @@ import ( "github.com/coreos/rkt/tests/testutils" ) +var httpPortCnt int = 54321 + +func getNextHttpPort() int { + // TODO: use random available port + httpPortCnt++ + return httpPortCnt +} + /* * No private net * --- @@ -78,8 +86,9 @@ func TestPrivateNetOmittedNetNS(t *testing.T) { */ func TestPrivateNetOmittedConnectivity(t *testing.T) { - httpServeAddr := "0.0.0.0:54321" - httpGetAddr := "http://127.0.0.1:54321" + httpPort := getNextHttpPort() + 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...) @@ -177,9 +186,11 @@ func TestPrivateNetDefaultNetNS(t *testing.T) { * 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 TestPrivateNetDefaultConnectivity(t *testing.T) { - httpServeAddr := "0.0.0.0:54321" + httpPort := getNextHttpPort() + httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 ifaces, err := net.Interfaces() @@ -194,7 +205,7 @@ func TestPrivateNetDefaultConnectivity(t *testing.T) { t.Fatalf("Cannot get IPV4 address for interface %v: %v", name, err) } if len(ifaceIPsv4) > 0 { - httpGetAddr = fmt.Sprintf("http://%v:54321", ifaceIPsv4[0]) + httpGetAddr = fmt.Sprintf("http://%v:%v", ifaceIPsv4[0], httpPort) t.Log("Telling the child to connect via", httpGetAddr) break } @@ -264,7 +275,8 @@ func TestPrivateNetDefaultConnectivity(t *testing.T) { * TODO: verify that the container isn't NATed */ func TestPrivateNetDefaultRestrictedConnectivity(t *testing.T) { - httpServeAddr := "0.0.0.0:54321" + httpPort := getNextHttpPort() + 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)} @@ -287,7 +299,7 @@ func TestPrivateNetDefaultRestrictedConnectivity(t *testing.T) { if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) } - httpGetAddr := fmt.Sprintf("http://%v:54321", result[1]) + httpGetAddr := fmt.Sprintf("http://%v:%v", result[1], httpPort) ga := testutils.NewGoroutineAssistant(t) // Child opens the server @@ -347,11 +359,12 @@ func writeNetwork(t *testing.T, net networkTemplateT, netd string) error { } type networkTemplateT struct { - Name string - Type string - Master string `json:"master,omitempty"` - IpMasq bool - Ipam ipamTemplateT + Name string + Type string + Master string `json:"master,omitempty"` + IpMasq bool + IsGateway bool + Ipam ipamTemplateT } type ipamTemplateT struct { @@ -360,7 +373,7 @@ type ipamTemplateT struct { Routes []map[string]string `json:"routes,omitempty"` } -func TestTemplates(t *testing.T) { +func TestPrivateNetTemplates(t *testing.T) { net := networkTemplateT{ Name: "ptp0", Type: "ptp", @@ -375,7 +388,24 @@ func TestTemplates(t *testing.T) { if err != nil { t.Fatalf("%v", err) } - t.Logf("%v", string(b)) + expected := `{"Name":"ptp0","Type":"ptp","IpMasq":false,"IsGateway":false,"Ipam":{"Type":"host-local-ptp","subnet":"10.1.3.0/24","routes":[{"dst":"0.0.0.0/0"}]}}` + if string(b) != expected { + t.Fatalf("Template extected:\n%v\ngot:\n%v\n", expected, string(b)) + } +} + +func prepareTestNet(t *testing.T, ctx *rktRunCtx, nt networkTemplateT) (netdir string) { + configdir := ctx.directories[1].dir + netdir = filepath.Join(configdir, "net.d") + err := os.MkdirAll(netdir, 0644) + if err != nil { + t.Fatalf("Cannot create netdir: %v", err) + } + err = writeNetwork(t, nt, netdir) + if err != nil { + t.Fatalf("Cannot write network file: %v", err) + } + return netdir } /* @@ -385,22 +415,15 @@ func TestTemplates(t *testing.T) { * 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, net networkTemplateT) { +func testPrivateNetCustomDual(t *testing.T, nt networkTemplateT) { + httpPort := getNextHttpPort() + ctx := newRktRunCtx() defer ctx.cleanup() defer ctx.reset() - configdir := ctx.directories[1].dir - netdir := filepath.Join(configdir, "net.d") - err := os.MkdirAll(netdir, 0644) - if err != nil { - t.Fatalf("Cannot create netdir: %v", err) - } + netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) - err = writeNetwork(t, net, netdir) - if err != nil { - t.Fatalf("Cannot write network file: %v", err) - } container1IPv4, container1Hostname := make(chan string), make(chan string) ga := testutils.NewGoroutineAssistant(t) @@ -408,12 +431,12 @@ func testPrivateNetCustomDual(t *testing.T, net networkTemplateT) { ga.Add(1) go func() { defer ga.Done() - httpServeAddr := "0.0.0.0:54321" + 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(), net.Name, 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 { @@ -447,13 +470,13 @@ func testPrivateNetCustomDual(t *testing.T, net networkTemplateT) { defer ga.Done() var httpGetAddr string - httpGetAddr = fmt.Sprintf("http://%v:54321", <-container1IPv4) + 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(), net.Name, 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 { @@ -484,11 +507,99 @@ func testPrivateNetCustomDual(t *testing.T, net networkTemplateT) { 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 := getNextHttpPort() + httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) + 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:%v", ifaceIPsv4[0], httpPort) + 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..") + } + + 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() +} + func TestPrivateNetCustomPtp(t *testing.T) { - net := networkTemplateT{ + nt := networkTemplateT{ Name: "ptp0", Type: "ptp", - IpMasq: false, + IpMasq: true, Ipam: ipamTemplateT{ Type: "host-local-ptp", Subnet: "10.1.1.0/24", @@ -497,12 +608,10 @@ func TestPrivateNetCustomPtp(t *testing.T) { }, }, } - testPrivateNetCustomDual(t, net) + testPrivateNetCustomNatConnectivity(t, nt) + testPrivateNetCustomDual(t, nt) } -/* - * TODO: test connection to host on an outside interface - */ func TestPrivateNetCustomMacvlan(t *testing.T) { ifaces, err := net.Interfaces() if err != nil { @@ -510,10 +619,11 @@ func TestPrivateNetCustomMacvlan(t *testing.T) { } var ifaceName string - if len(ifaces) >= 2 { - ifaceName = ifaces[1].Name + if len(ifaces) < 2 { + t.Skipf("Cannot run test without non-lo host interface") } - net := networkTemplateT{ + ifaceName = ifaces[1].Name + nt := networkTemplateT{ Name: "macvlan0", Type: "macvlan", Master: ifaceName, @@ -522,5 +632,34 @@ func TestPrivateNetCustomMacvlan(t *testing.T) { Subnet: "10.1.2.0/24", }, } - testPrivateNetCustomDual(t, net) + testPrivateNetCustomDual(t, nt) +} + +func TestPrivateNetCustomBridge(t *testing.T) { + ifaces, err := net.Interfaces() + if err != nil { + t.Fatalf("Cannot get network host's interfaces: %v", err) + } + + var ifaceName string + if len(ifaces) < 2 { + t.Skipf("Cannot run test without non-lo host interface") + } + ifaceName = ifaces[1].Name + nt := networkTemplateT{ + Name: "bridge0", + Type: "bridge", + IpMasq: true, + IsGateway: true, + Master: ifaceName, + Ipam: ipamTemplateT{ + Type: "host-local", + Subnet: "10.1.3.0/24", + Routes: []map[string]string{ + {"dst": "0.0.0.0/0"}, + }, + }, + } + testPrivateNetCustomNatConnectivity(t, nt) + testPrivateNetCustomDual(t, nt) } From e9037af0a7d42aa8b5577817257a2dfd29db6d88 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Wed, 12 Aug 2015 15:30:03 +0200 Subject: [PATCH 3/5] testutils: extend iputils with iface discovery --- tests/testutils/iputils.go | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/testutils/iputils.go b/tests/testutils/iputils.go index 9c9f79bf1a..7bfdcb0a37 100644 --- a/tests/testutils/iputils.go +++ b/tests/testutils/iputils.go @@ -88,3 +88,49 @@ func GetGWv4(iface string) (string, error) { func GetGWv6(iface string) (string, error) { return GetGW(iface, netlink.FAMILY_V4) } + +func GetNonLoIfaceWithAddrs() (iface net.Interface, err error) { + ifaces, err := net.Interfaces() + if err != nil { + return iface, err + } + + for _, i := range ifaces { + if i.Flags&net.FlagLoopback == 0 { + ifaceNameLower := strings.ToLower(i.Name) + // Don't use rkt's interfaces + if strings.HasSuffix(ifaceNameLower, "cni") || + strings.HasSuffix(ifaceNameLower, "veth") { + continue + } + addrs, err := i.Addrs() + if err != nil { + return iface, fmt.Errorf("Cannot get IPV4 address for interface %v: %v", i.Name, err) + } + if len(addrs) > 0 { + iface = i + break + } + } + } + return iface, err +} + +func GetNonLoIfaceIPv4() (string, error) { + iface, err := GetNonLoIfaceWithAddrs() + if err != nil { + return "", fmt.Errorf("Error while getting non-lo host interface: %v\n", err) + } + if iface.Name == "" { + return "", nil + } + + ifaceIPsv4, err := GetIPsv4(iface.Name) + if err != nil { + return "", fmt.Errorf("Cannot get IPV4 address for interface %v: %v", iface.Name, err) + } + if len(ifaceIPsv4) == 0 { + return "", nil + } + return ifaceIPsv4[0], nil +} From bd569a5612f8f438476c25b0987f1991427cdead Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Wed, 12 Aug 2015 16:07:09 +0200 Subject: [PATCH 4/5] tests private-net: improve interface/addr discovery --- tests/rkt_privatenet_test.go | 70 ++++++++++++------------------------ 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/tests/rkt_privatenet_test.go b/tests/rkt_privatenet_test.go index baec22a3d7..8f255ed11e 100644 --- a/tests/rkt_privatenet_test.go +++ b/tests/rkt_privatenet_test.go @@ -18,7 +18,6 @@ import ( "encoding/json" "fmt" "log" - "net" "os" "path/filepath" "testing" @@ -193,27 +192,17 @@ func TestPrivateNetDefaultConnectivity(t *testing.T) { httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 - ifaces, err := net.Interfaces() + nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() 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:%v", ifaceIPsv4[0], httpPort) - t.Log("Telling the child to connect via", httpGetAddr) - break - } + t.Fatalf("%v", err) } - if httpGetAddr == "" { + 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) @@ -525,28 +514,17 @@ func testPrivateNetCustomNatConnectivity(t *testing.T, nt networkTemplateT) { httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 - ifaces, err := net.Interfaces() + nonLoIPv4, err := testutils.GetNonLoIfaceIPv4() 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:%v", ifaceIPsv4[0], httpPort) - t.Log("Telling the child to connect via", httpGetAddr) - break - } + t.Fatalf("%v", err) } - if httpGetAddr == "" { + 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 @@ -613,20 +591,18 @@ func TestPrivateNetCustomPtp(t *testing.T) { } func TestPrivateNetCustomMacvlan(t *testing.T) { - ifaces, err := net.Interfaces() + iface, err := testutils.GetNonLoIfaceWithAddrs() if err != nil { - t.Fatalf("Cannot get network host's interfaces: %v", err) + t.Fatalf("Error while getting non-lo host interface: %v\n", err) } - - var ifaceName string - if len(ifaces) < 2 { + if iface.Name == "" { t.Skipf("Cannot run test without non-lo host interface") } - ifaceName = ifaces[1].Name + nt := networkTemplateT{ Name: "macvlan0", Type: "macvlan", - Master: ifaceName, + Master: iface.Name, Ipam: ipamTemplateT{ Type: "host-local", Subnet: "10.1.2.0/24", @@ -636,22 +612,20 @@ func TestPrivateNetCustomMacvlan(t *testing.T) { } func TestPrivateNetCustomBridge(t *testing.T) { - ifaces, err := net.Interfaces() + iface, err := testutils.GetNonLoIfaceWithAddrs() if err != nil { - t.Fatalf("Cannot get network host's interfaces: %v", err) + t.Fatalf("Error while getting non-lo host interface: %v\n", err) } - - var ifaceName string - if len(ifaces) < 2 { + if iface.Name == "" { t.Skipf("Cannot run test without non-lo host interface") } - ifaceName = ifaces[1].Name + nt := networkTemplateT{ Name: "bridge0", Type: "bridge", IpMasq: true, IsGateway: true, - Master: ifaceName, + Master: iface.Name, Ipam: ipamTemplateT{ Type: "host-local", Subnet: "10.1.3.0/24", From 844d195cd414edc4860cd277a78608e3b16e1d3e Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Thu, 13 Aug 2015 16:56:10 +0200 Subject: [PATCH 5/5] tests private-net: check ports for availability --- tests/rkt_privatenet_test.go | 33 +++++++++++++++++----------- tests/testutils/iputils.go | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/tests/rkt_privatenet_test.go b/tests/rkt_privatenet_test.go index 8f255ed11e..a8526473a5 100644 --- a/tests/rkt_privatenet_test.go +++ b/tests/rkt_privatenet_test.go @@ -27,14 +27,6 @@ import ( "github.com/coreos/rkt/tests/testutils" ) -var httpPortCnt int = 54321 - -func getNextHttpPort() int { - // TODO: use random available port - httpPortCnt++ - return httpPortCnt -} - /* * No private net * --- @@ -85,7 +77,10 @@ func TestPrivateNetOmittedNetNS(t *testing.T) { */ func TestPrivateNetOmittedConnectivity(t *testing.T) { - httpPort := getNextHttpPort() + 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) @@ -188,7 +183,10 @@ func TestPrivateNetDefaultNetNS(t *testing.T) { * TODO: test connection to host on an outside interface */ func TestPrivateNetDefaultConnectivity(t *testing.T) { - httpPort := getNextHttpPort() + httpPort, err := testutils.GetNextFreePort4() + if err != nil { + t.Fatalf("%v", err) + } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 @@ -264,7 +262,10 @@ func TestPrivateNetDefaultConnectivity(t *testing.T) { * TODO: verify that the container isn't NATed */ func TestPrivateNetDefaultRestrictedConnectivity(t *testing.T) { - httpPort := getNextHttpPort() + httpPort, err := testutils.GetNextFreePort4() + if err != nil { + t.Fatalf("%v", err) + } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) iface := "eth0" @@ -405,7 +406,10 @@ func prepareTestNet(t *testing.T, ctx *rktRunCtx, nt networkTemplateT) (netdir s * The body of the HttpGet is Container 1's hostname, which must match */ func testPrivateNetCustomDual(t *testing.T, nt networkTemplateT) { - httpPort := getNextHttpPort() + httpPort, err := testutils.GetNextFreePort4() + if err != nil { + t.Fatalf("%v", err) + } ctx := newRktRunCtx() defer ctx.cleanup() @@ -510,7 +514,10 @@ func testPrivateNetCustomNatConnectivity(t *testing.T, nt networkTemplateT) { netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) - httpPort := getNextHttpPort() + httpPort, err := testutils.GetNextFreePort4() + if err != nil { + t.Fatalf("%v", err) + } httpServeAddr := fmt.Sprintf("0.0.0.0:%v", httpPort) httpServeTimeout := 30 diff --git a/tests/testutils/iputils.go b/tests/testutils/iputils.go index 7bfdcb0a37..d4df2d0774 100644 --- a/tests/testutils/iputils.go +++ b/tests/testutils/iputils.go @@ -15,8 +15,12 @@ package testutils import ( + "bufio" "fmt" "net" + "os" + "regexp" + "strconv" "strings" "github.com/coreos/rkt/Godeps/_workspace/src/github.com/vishvananda/netlink" @@ -134,3 +138,41 @@ func GetNonLoIfaceIPv4() (string, error) { } return ifaceIPsv4[0], nil } + +func CheckTcp4Port(port int) (bool, error) { + tcpFile, err := os.Open("/proc/net/tcp") + if err != nil { + return false, err + } + defer tcpFile.Close() + + re := regexp.MustCompile(`:([A-Z0-9]+) `) + scanner := bufio.NewScanner(tcpFile) + for scanner.Scan() { + line := scanner.Text() + result := re.FindAllStringSubmatch(line, -1) + if result != nil { + i, err := strconv.ParseInt(result[0][1], 16, 32) + if err != nil { + return false, err + } + if int(i) == port { + return false, nil + } + } + } + return true, nil +} + +func GetNextFreePort4() (int, error) { + for port := 49152; port <= 65535; port++ { + avail, err := CheckTcp4Port(port) + if err != nil { + return 0, err + } + if avail { + return port, nil + } + } + return 0, fmt.Errorf("No available ports") +}