func NewNetCustomBridgeTest() testutils.Test { return testutils.TestFunc(func(t *testing.T) { 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: "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"}, }, }, } testNetCustomNatConnectivity(t, nt) testNetCustomDual(t, nt) }) }
func TestPrivateNetOverride(t *testing.T) { ctx := newRktRunCtx() defer ctx.cleanup() defer ctx.reset() 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: "overridemacvlan", Type: "macvlan", Master: iface.Name, Ipam: ipamTemplateT{ Type: "host-local", Subnet: "10.1.4.0/24", }, } netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0"} testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...) defer os.Remove(testImage) expectedIP := "10.1.4.244" cmd := fmt.Sprintf("%s --debug --insecure-skip-verify run --private-net=all --private-net=\"%s:IP=%s\" --mds-register=false %s", ctx.cmd(), nt.Name, expectedIP, testImage) fmt.Printf("Command: %v\n", cmd) child, err := gexpect.Spawn(cmd) if err != nil { t.Fatalf("Cannot exec rkt: %v", err) return } defer func() { err = child.Wait() if err != nil { t.Fatalf("rkt didn't terminate correctly: %v", err) } }() expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) return } containerIP := result[1] if expectedIP != containerIP { t.Fatalf("overriding IP did not work: Got %q but expected %q", containerIP, expectedIP) } }
// 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() }) }
func NewNetOverrideTest() 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: "overridemacvlan", Type: "macvlan", Master: iface.Name, Ipam: &ipamTemplateT{ Type: "host-local", Subnet: "11.11.4.0/24", }, } netdir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netdir) testImageArgs := []string{"--exec=/inspect --print-ipv4=eth0"} testImage := patchTestACI("rkt-inspect-networking1.aci", testImageArgs...) defer os.Remove(testImage) expectedIP := "11.11.4.244" cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=all --net=\"%s:IP=%s\" --mds-register=false %s", ctx.Cmd(), nt.Name, expectedIP, testImage) child := spawnOrFail(t, cmd) defer waitOrFail(t, child, 0) expectedRegex := `IPv4: (\d+\.\d+\.\d+\.\d+)` result, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) return } containerIP := result[1] if expectedIP != containerIP { t.Fatalf("overriding IP did not work: Got %q but expected %q", containerIP, expectedIP) } }) }
// Test that `rkt run --dns=none` means no resolv.conf is created, even when // CNI returns DNS informationparseHostsEntries(flagHosts) func NewNetCNIDNSArgNoneTest() 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 prepareTestNet(t, ctx, nt) appCmd := "--exec=/inspect -- --stat-file --file-name=/etc/resolv.conf" cmd := fmt.Sprintf("%s --debug --insecure-options=image run --net=%v --mds-register=false --dns=none %s %s", ctx.Cmd(), nt.NetParameter(), getInspectImagePath(), appCmd) child := spawnOrFail(t, cmd) ctx.RegisterChild(child) defer waitOrFail(t, child, 254) expectedRegex := `Cannot stat file "/etc/resolv.conf": stat /etc/resolv.conf: no such file or directory` _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) } }) }
func TestNetCustomMacvlan(t *testing.T) { 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: "macvlan0", Type: "macvlan", Master: iface.Name, Ipam: ipamTemplateT{ Type: "host-local", Subnet: "11.11.2.0/24", }, } testNetCustomDual(t, nt) }
//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) 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 := spawnOrFail(t, cmd) expectedRegex := "DefaultGWv4: 11.11.3.1" _, out, err := expectRegexTimeoutWithOutput(child, expectedRegex, 30*time.Second) if err != nil { t.Fatalf("Error: %v\nOutput: %v", err, out) } waitOrFail(t, child, 0) // Parse the log file cniLogFilename := filepath.Join(netdir, "output.json") proxyLog, err := parseCNIProxyLog(cniLogFilename) if err != nil { t.Fatal("Failed to read cniproxy ADD log", err) } os.Remove(cniLogFilename) // 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) } // compare the CNI env against a set of regexes checkEnv := func(step string, expectedEnv, actualEnv map[string]string) { for k, v := range expectedEnv { actual, exists := actualEnv[k] if !exists { t.Fatalf("Step %s, expected proxy CNI arg %s but not found", step, k) } re, err := regexp.Compile(v) if err != nil { t.Fatalf("Step %s, invalid CNI env regex for key %s %v", step, k, err) } found := re.FindString(actual) if found == "" { t.Fatalf("step %s cni environment %s was %s but expected pattern %s", step, k, actual, v) } } } expectedEnv := map[string]string{ "CNI_VERSION": `^0\.1\.0$`, "CNI_COMMAND": `^ADD$`, "CNI_IFNAME": `^eth\d$`, "CNI_PATH": "^" + netdir + ":/usr/lib/rkt/plugins/net:stage1/rootfs/usr/lib/rkt/plugins/net$", "CNI_NETNS": `^/var/run/netns/cni-`, "CNI_CONTAINERID": `^[a-fA-F0-9-]{36}$`, //UUID, close enough } checkEnv("add", expectedEnv, proxyLog.EnvMap) /* Run rkt GC, ensure the CNI invocation looks sane */ ctx.RunGC() proxyLog, err = parseCNIProxyLog(cniLogFilename) if err != nil { t.Fatal("Failed to read cniproxy DEL log", err) } os.Remove(cniLogFilename) expectedEnv["CNI_COMMAND"] = `^DEL$` checkEnv("del", expectedEnv, proxyLog.EnvMap) }) }
func TestSocketProxyd(t *testing.T) { if !sd_util.IsRunningSystemd() { t.Skip("Systemd is not running on the host.") } socketProxydPath := "/lib/systemd/systemd-socket-proxyd" if _, err := os.Stat(socketProxydPath); os.IsNotExist(err) { t.Skip("systemd-socket-proxyd is not installed.") } 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: "ptp0", Type: "ptp", IpMasq: true, Master: iface.Name, Ipam: &ipamTemplateT{ Type: "host-local", Subnet: "192.168.0.0/24", Routes: []map[string]string{ {"dst": "0.0.0.0/0"}, }, }, } netDir := prepareTestNet(t, ctx, nt) defer os.RemoveAll(netDir) port, err := randomFreePort(t) if err != nil { t.Fatal(err) } echoImage := patchTestACI("rkt-inspect-echo.aci", "--exec=/echo-socket-activated", "--ports=test-port,protocol=tcp,port=80,socketActivated=true") defer os.Remove(echoImage) conn, err := sd_dbus.New() if err != nil { t.Fatal(err) } rktTestingEchoService := ` [Unit] Description=Socket-activated echo server [Service] ExecStart=%s KillMode=process ` r := rand.New(rand.NewSource(time.Now().UnixNano())) rnd := r.Int() // Write unit files directly to runtime system units directory // (/run/systemd/system) to avoid calling LinkUnitFiles - it is buggy in // systemd v219 as it does not work with absolute paths. unitsDir := "/run/systemd/system" containerIP := "192.168.0.101" cmd := fmt.Sprintf("%s --insecure-options=image --debug run --net=%s:IP=%s --port=test-port:%d --mds-register=false %s", ctx.Cmd(), nt.Name, containerIP, port, echoImage) serviceContent := fmt.Sprintf(rktTestingEchoService, cmd) serviceTargetBase := fmt.Sprintf("rkt-testing-socket-activation-%d.service", rnd) serviceTarget := filepath.Join(unitsDir, serviceTargetBase) if err := ioutil.WriteFile(serviceTarget, []byte(serviceContent), 0666); err != nil { t.Fatal(err) } defer os.Remove(serviceTarget) rktTestingEchoSocket := ` [Unit] Description=Socket-activated netcat server socket [Socket] ListenStream=%d [Install] WantedBy=sockets.target ` socketContent := fmt.Sprintf(rktTestingEchoSocket, port) socketTargetBase := fmt.Sprintf("proxy-to-rkt-testing-socket-activation-%d.socket", rnd) socketTarget := filepath.Join(unitsDir, socketTargetBase) if err := ioutil.WriteFile(socketTarget, []byte(socketContent), 0666); err != nil { t.Fatal(err) } defer os.Remove(socketTarget) proxyToRktTestingEchoService := ` [Unit] Requires=%s After=%s [Service] ExecStart=%s %s:%d ` proxyContent := fmt.Sprintf(proxyToRktTestingEchoService, serviceTargetBase, serviceTargetBase, socketProxydPath, containerIP, port) proxyContentBase := fmt.Sprintf("proxy-to-rkt-testing-socket-activation-%d.service", rnd) proxyTarget := filepath.Join(unitsDir, proxyContentBase) if err := ioutil.WriteFile(proxyTarget, []byte(proxyContent), 0666); err != nil { t.Fatal(err) } defer os.Remove(proxyTarget) reschan := make(chan string) doJob := func() { job := <-reschan if job != "done" { t.Fatal("Job is not done:", job) } } if _, err := conn.StartUnit(socketTargetBase, "replace", reschan); err != nil { t.Fatal(err) } doJob() defer func() { if _, err := conn.StopUnit(socketTargetBase, "replace", reschan); err != nil { t.Fatal(err) } doJob() if _, err := conn.StopUnit(serviceTargetBase, "replace", reschan); err != nil { t.Fatal(err) } doJob() if _, err := conn.StopUnit(proxyContentBase, "replace", reschan); err != nil { t.Fatal(err) } doJob() }() expected := "HELO\n" sockConn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port)) if err != nil { t.Fatal(err) } if _, err := fmt.Fprintf(sockConn, expected); err != nil { t.Fatal(err) } answer, err := bufio.NewReader(sockConn).ReadString('\n') if err != nil { t.Fatal(err) } if answer != expected { t.Fatalf("Expected %q, Got %q", expected, answer) } return }
//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) } } }) }