Skip to content

Commit

Permalink
Merge pull request rkt#1252 from steveeJ/tests-networking
Browse files Browse the repository at this point in the history
functional tests networking: various improvements, NAT tests and bridged grouping
  • Loading branch information
steveej committed Aug 14, 2015
2 parents b30f5c4 + 844d195 commit c1b7157
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 62 deletions.
238 changes: 179 additions & 59 deletions tests/rkt_privatenet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"log"
"net"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -78,8 +77,12 @@ func TestPrivateNetOmittedNetNS(t *testing.T) {
*/
func TestPrivateNetOmittedConnectivity(t *testing.T) {

httpServeAddr := "0.0.0.0:54321"
httpGetAddr := "http://127.0.0.1:54321"
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...)
Expand Down Expand Up @@ -177,32 +180,27 @@ 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, err := testutils.GetNextFreePort4()
if err != nil {
t.Fatalf("%v", err)
}
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:54321", ifaceIPsv4[0])
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)
Expand Down Expand Up @@ -264,7 +262,11 @@ 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, 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)}
Expand All @@ -287,7 +289,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
Expand Down Expand Up @@ -347,11 +349,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 {
Expand All @@ -360,7 +363,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",
Expand All @@ -375,7 +378,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
}

/*
Expand All @@ -385,35 +405,31 @@ 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, err := testutils.GetNextFreePort4()
if err != nil {
t.Fatalf("%v", err)
}

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)

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 {
Expand Down Expand Up @@ -447,13 +463,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 {
Expand Down Expand Up @@ -484,11 +500,91 @@ 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, 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()
}

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",
Expand All @@ -497,30 +593,54 @@ 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()
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 {
ifaceName = ifaces[1].Name
if iface.Name == "" {
t.Skipf("Cannot run test without non-lo host interface")
}
net := networkTemplateT{

nt := networkTemplateT{
Name: "macvlan0",
Type: "macvlan",
Master: ifaceName,
Master: iface.Name,
Ipam: ipamTemplateT{
Type: "host-local",
Subnet: "10.1.2.0/24",
},
}
testPrivateNetCustomDual(t, net)
testPrivateNetCustomDual(t, nt)
}

func TestPrivateNetCustomBridge(t *testing.T) {
iface, err := testutils.GetNonLoIfaceWithAddrs()
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: "bridge",
IpMasq: true,
IsGateway: true,
Master: iface.Name,
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)
}
6 changes: 3 additions & 3 deletions tests/testutils/httputils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit c1b7157

Please sign in to comment.