From 229f5fcf188cf710f4a8fb269d92f1a1d04a99da Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Tue, 9 Aug 2022 14:44:47 +0000 Subject: misc: add test framework for host stack Type: feature Signed-off-by: Filip Tehlar Change-Id: I5a64a2c095cae3a4d5f8fdc73e624b010339ec8e --- extras/hs-test/utils.go | 334 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100755 extras/hs-test/utils.go (limited to 'extras/hs-test/utils.go') diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go new file mode 100755 index 00000000000..25303591a43 --- /dev/null +++ b/extras/hs-test/utils.go @@ -0,0 +1,334 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "strings" + "testing" + "time" + + "github.com/edwarnicke/exechelper" +) + +const configTemplate = `unix { + nodaemon + log %[1]s/var/log/vpp/vpp.log + full-coredump + cli-listen %[1]s/var/run/vpp/cli.sock + runtime-dir %[1]s/var/run + gid vpp +} + +api-trace { + on +} + +api-segment { + gid vpp +} + +socksvr { + socket-name %[1]s/var/run/vpp/api.sock +} + +statseg { + socket-name %[1]s/var/run/vpp/stats.sock +} + +plugins { + plugin unittest_plugin.so { enable } + plugin dpdk_plugin.so { disable } + plugin crypto_aesni_plugin.so { enable } + plugin quic_plugin.so { enable } +} + +` + +const TopologyDir string = "topo/" + +type Stanza struct { + content string + pad int +} + +type ActionResult struct { + Err error + Desc string + ErrOutput string + StdOutput string +} + +type JsonResult struct { + Code int + Desc string + ErrOutput string + StdOutput string +} + +func StartServerApp(running chan error, done chan struct{}, env []string) { + cmd := exec.Command("iperf3", "-4", "-s") + if env != nil { + cmd.Env = env + } + err := cmd.Start() + if err != nil { + msg := fmt.Errorf("failed to start iperf server: %v", err) + running <- msg + return + } + running <- nil + <-done + cmd.Process.Kill() +} + +func StartClientApp(env []string, clnCh chan error) { + defer func() { + clnCh <- nil + }() + + nTries := 0 + + for { + cmd := exec.Command("iperf3", "-c", "10.10.10.1", "-u", "-l", "1460", "-b", "10g") + if env != nil { + cmd.Env = env + } + o, err := cmd.CombinedOutput() + if err != nil { + if nTries > 5 { + clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o) + return + } + time.Sleep(1 * time.Second) + nTries++ + continue + } else { + fmt.Printf("Client output: %s", o) + } + break + } +} + +// run vpphelper in docker +func hstExec(args string, instance string) (string, error) { + syncFile := fmt.Sprintf("/tmp/%s/sync/rc", instance) + os.Remove(syncFile) + + c := "docker exec -d " + instance + " /hs-test " + args + err := exechelper.Run(c) + if err != nil { + return "", err + } + + res, err := waitForSyncFile(syncFile) + + if err != nil { + return "", fmt.Errorf("failed to read sync file while executing './hs-test %s': %v", args, err) + } + + o := res.StdOutput + res.ErrOutput + if res.Code != 0 { + return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc) + } + return o, err +} + +func waitForSyncFile(fname string) (*JsonResult, error) { + var res JsonResult + + for i := 0; i < 60; i++ { + f, err := os.Open(fname) + if err == nil { + defer f.Close() + + data, err := ioutil.ReadFile(fname) + if err != nil { + return nil, fmt.Errorf("read error: %v", err) + } + err = json.Unmarshal(data, &res) + if err != nil { + return nil, fmt.Errorf("json unmarshal error: %v", err) + } + return &res, nil + } + time.Sleep(1 * time.Second) + } + return nil, fmt.Errorf("no sync file found") +} + +func dockerRun(instance, args string) error { + exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", instance)) + syncPath := fmt.Sprintf("-v /tmp/%s/sync:/tmp/sync", instance) + cmd := "docker run --cap-add=all -d --privileged --network host --rm " + cmd += syncPath + cmd += " " + args + cmd += " --name " + instance + " hs-test/vpp" + fmt.Println(cmd) + return exechelper.Run(cmd) +} + +func assertFileSize(f1, f2 string) error { + fi1, err := os.Stat(f1) + if err != nil { + return err + } + + fi2, err1 := os.Stat(f2) + if err1 != nil { + return err1 + } + + if fi1.Size() != fi2.Size() { + return fmt.Errorf("file sizes differ (%d vs %d)", fi1.Size(), fi2.Size()) + } + return nil +} + +func dockerExec(cmd string, instance string) ([]byte, error) { + c := "docker exec -d " + instance + " " + cmd + return exechelper.CombinedOutput(c) +} + +func startEnvoy(ctx context.Context, dockerInstance string) <-chan error { + errCh := make(chan error) + wd, err := os.Getwd() + if err != nil { + errCh <- err + return errCh + } + + c := []string{"docker", "run", "--rm", "--name", "envoy", + "-v", fmt.Sprintf("%s/envoy/proxy.yaml:/etc/envoy/envoy.yaml", wd), + "-v", fmt.Sprintf("shared-vol:/tmp/%s", dockerInstance), + "-v", fmt.Sprintf("%s/envoy:/tmp", wd), + "-e", "VCL_CONFIG=/tmp/vcl.conf", + "envoyproxy/envoy-contrib:v1.21-latest"} + fmt.Println(c) + + go func(errCh chan error) { + count := 0 + var cmd *exec.Cmd + for ; ; count++ { + cmd = NewCommand(c, "") + err = cmd.Start() + if err == nil { + break + } + if count > 5 { + errCh <- fmt.Errorf("Failed to start envoy docker after %d attempts", count) + return + } + } + + err = cmd.Wait() + if err != nil { + errCh <- fmt.Errorf("failed to start docker: %v", err) + return + } + <-ctx.Done() + }(errCh) + return errCh +} + +func setupEnvoy(t *testing.T, ctx context.Context, dockerInstance string) error { + errCh := startEnvoy(ctx, dockerInstance) + select { + case err := <-errCh: + return err + default: + } + + go func(ctx context.Context, errCh <-chan error) { + for { + select { + // handle cancel() call from outside to gracefully stop the routine + case <-ctx.Done(): + return + default: + select { + case err := <-errCh: + fmt.Printf("error while running envoy: %v", err) + default: + } + } + } + }(ctx, errCh) + return nil +} + +func configureVppProxy() error { + _, err := dockerExec("vppctl test proxy server server-uri tcp://10.0.0.2/555 client-uri tcp://10.0.1.1/666", + "vpp-proxy") + if err != nil { + return fmt.Errorf("error while configuring vpp proxy test: %v", err) + } + return nil +} + +func startHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) { + cmd := NewCommand([]string{"./http_server", addressPort}, netNs) + err := cmd.Start() + if err != nil { + fmt.Println("Failed to start http server") + return + } + running <- struct{}{} + <-done + cmd.Process.Kill() +} + +func startWget(finished chan error, server_ip, port string, netNs string) { + fname := "test_file_10M" + defer func() { + finished <- errors.New("wget error") + }() + + cmd := NewCommand([]string{"wget", "--tries=5", "-q", "-O", "/dev/null", server_ip + ":" + port + "/" + fname}, + netNs) + o, err := cmd.CombinedOutput() + if err != nil { + fmt.Printf("wget error: '%s'.\n%s", err, o) + return + } + fmt.Printf("Client output: %s", o) + finished <- nil +} + +func (c *Stanza) NewStanza(name string) *Stanza { + c.Append("\n" + name + " {") + c.pad += 2 + return c +} + +func (c *Stanza) Append(name string) *Stanza { + c.content += strings.Repeat(" ", c.pad) + c.content += name + "\n" + return c +} + +func (c *Stanza) Close() *Stanza { + c.content += "}\n" + c.pad -= 2 + return c +} + +func (s *Stanza) ToString() string { + return s.content +} + +func (s *Stanza) SaveToFile(fileName string) error { + fo, err := os.Create(fileName) + if err != nil { + return err + } + defer fo.Close() + + _, err = io.Copy(fo, strings.NewReader(s.content)) + return err +} -- cgit 1.2.3-korg