ba40beedeafb9d4d71cea9282c433b76da8b36a7
[vpp.git] / extras / hs-test / utils.go
1 package main
2
3 import (
4         "context"
5         "encoding/json"
6         "errors"
7         "fmt"
8         "io"
9         "io/ioutil"
10         "os"
11         "os/exec"
12         "strings"
13         "testing"
14         "time"
15
16         "github.com/edwarnicke/exechelper"
17 )
18
19 const configTemplate = `unix {
20   nodaemon
21   log %[1]s/var/log/vpp/vpp.log
22   full-coredump
23   cli-listen %[1]s/var/run/vpp/cli.sock
24   runtime-dir %[1]s/var/run
25   gid vpp
26 }
27
28 api-trace {
29   on
30 }
31
32 api-segment {
33   gid vpp
34 }
35
36 socksvr {
37   socket-name %[1]s/var/run/vpp/api.sock
38 }
39
40 statseg {
41   socket-name %[1]s/var/run/vpp/stats.sock
42 }
43
44 plugins {
45         plugin unittest_plugin.so { enable }
46     plugin dpdk_plugin.so { disable }
47     plugin crypto_aesni_plugin.so { enable }
48     plugin quic_plugin.so { enable }
49 }
50
51 `
52
53 const TopologyDir string = "topo/"
54
55 type Stanza struct {
56         content string
57         pad     int
58 }
59
60 type ActionResult struct {
61         Err       error
62         Desc      string
63         ErrOutput string
64         StdOutput string
65 }
66
67 type JsonResult struct {
68         Code      int
69         Desc      string
70         ErrOutput string
71         StdOutput string
72 }
73
74 func StartServerApp(running chan error, done chan struct{}, env []string) {
75         cmd := exec.Command("iperf3", "-4", "-s")
76         if env != nil {
77                 cmd.Env = env
78         }
79         err := cmd.Start()
80         if err != nil {
81                 msg := fmt.Errorf("failed to start iperf server: %v", err)
82                 running <- msg
83                 return
84         }
85         running <- nil
86         <-done
87         cmd.Process.Kill()
88 }
89
90 func StartClientApp(env []string, clnCh chan error) {
91         defer func() {
92                 clnCh <- nil
93         }()
94
95         nTries := 0
96
97         for {
98                 cmd := exec.Command("iperf3", "-c", "10.10.10.1", "-u", "-l", "1460", "-b", "10g")
99                 if env != nil {
100                         cmd.Env = env
101                 }
102                 o, err := cmd.CombinedOutput()
103                 if err != nil {
104                         if nTries > 5 {
105                                 clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o)
106                                 return
107                         }
108                         time.Sleep(1 * time.Second)
109                         nTries++
110                         continue
111                 } else {
112                         fmt.Printf("Client output: %s", o)
113                 }
114                 break
115         }
116 }
117
118 // run vpphelper in docker
119 func hstExec(args string, instance string) (string, error) {
120         syncFile := fmt.Sprintf("/tmp/%s/sync/rc", instance)
121         os.Remove(syncFile)
122
123         c := "docker exec -d " + instance + " /hs-test " + args
124         err := exechelper.Run(c)
125         if err != nil {
126                 return "", err
127         }
128
129         res, err := waitForSyncFile(syncFile)
130
131         if err != nil {
132                 return "", fmt.Errorf("failed to read sync file while executing './hs-test %s': %v", args, err)
133         }
134
135         o := res.StdOutput + res.ErrOutput
136         if res.Code != 0 {
137                 return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc)
138         }
139         return o, err
140 }
141
142 func waitForSyncFile(fname string) (*JsonResult, error) {
143         var res JsonResult
144
145         for i := 0; i < 60; i++ {
146                 f, err := os.Open(fname)
147                 if err == nil {
148                         defer f.Close()
149
150                         data, err := ioutil.ReadFile(fname)
151                         if err != nil {
152                                 return nil, fmt.Errorf("read error: %v", err)
153                         }
154                         err = json.Unmarshal(data, &res)
155                         if err != nil {
156                                 return nil, fmt.Errorf("json unmarshal error: %v", err)
157                         }
158                         return &res, nil
159                 }
160                 time.Sleep(1 * time.Second)
161         }
162         return nil, fmt.Errorf("no sync file found")
163 }
164
165 func dockerRun(instance, args string) error {
166         exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", instance))
167         syncPath := fmt.Sprintf("-v /tmp/%s/sync:/tmp/sync", instance)
168         cmd := "docker run --cap-add=all -d --privileged --network host --rm "
169         cmd += syncPath
170         cmd += " " + args
171         cmd += " --name " + instance + " hs-test/vpp"
172         fmt.Println(cmd)
173         return exechelper.Run(cmd)
174 }
175
176 func assertFileSize(f1, f2 string) error {
177         fi1, err := os.Stat(f1)
178         if err != nil {
179                 return err
180         }
181
182         fi2, err1 := os.Stat(f2)
183         if err1 != nil {
184                 return err1
185         }
186
187         if fi1.Size() != fi2.Size() {
188                 return fmt.Errorf("file sizes differ (%d vs %d)", fi1.Size(), fi2.Size())
189         }
190         return nil
191 }
192
193 func dockerExec(cmd string, instance string) ([]byte, error) {
194         c := "docker exec -d " + instance + " " + cmd
195         return exechelper.CombinedOutput(c)
196 }
197
198 func startEnvoy(ctx context.Context, dockerInstance string) <-chan error {
199         errCh := make(chan error)
200         wd, err := os.Getwd()
201         if err != nil {
202                 errCh <- err
203                 return errCh
204         }
205
206         c := []string{"docker", "run", "--rm", "--name", "envoy",
207                 "-v", fmt.Sprintf("%s/envoy/proxy.yaml:/etc/envoy/envoy.yaml", wd),
208                 "-v", fmt.Sprintf("shared-vol:/tmp/%s", dockerInstance),
209                 "-v", fmt.Sprintf("%s/envoy:/tmp", wd),
210                 "-e", "ENVOY_UID=0",
211                 "-e", "VCL_CONFIG=/tmp/vcl.conf",
212                 "envoyproxy/envoy-contrib:v1.21-latest"}
213         fmt.Println(c)
214
215         go func(errCh chan error) {
216                 count := 0
217                 var cmd *exec.Cmd
218                 for ; ; count++ {
219                         cmd = NewCommand(c, "")
220                         err = cmd.Start()
221                         if err == nil {
222                                 break
223                         }
224                         if count > 5 {
225                                 errCh <- fmt.Errorf("failed to start envoy docker after %d attempts", count)
226                                 return
227                         }
228                 }
229
230                 err = cmd.Wait()
231                 if err != nil {
232                         errCh <- fmt.Errorf("failed to start docker: %v", err)
233                         return
234                 }
235                 <-ctx.Done()
236         }(errCh)
237         return errCh
238 }
239
240 func setupEnvoy(t *testing.T, ctx context.Context, dockerInstance string) error {
241         errCh := startEnvoy(ctx, dockerInstance)
242         select {
243         case err := <-errCh:
244                 return err
245         default:
246         }
247
248         go func(ctx context.Context, errCh <-chan error) {
249                 for {
250                         select {
251                         // handle cancel() call from outside to gracefully stop the routine
252                         case <-ctx.Done():
253                                 return
254                         default:
255                                 select {
256                                 case err := <-errCh:
257                                         fmt.Printf("error while running envoy: %v", err)
258                                 default:
259                                 }
260                         }
261                 }
262         }(ctx, errCh)
263         return nil
264 }
265
266 func configureVppProxy() error {
267         _, err := dockerExec("vppctl test proxy server server-uri tcp://10.0.0.2/555 client-uri tcp://10.0.1.1/666",
268                 "vpp-proxy")
269         if err != nil {
270                 return fmt.Errorf("error while configuring vpp proxy test: %v", err)
271         }
272         return nil
273 }
274
275 func startHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) {
276         cmd := NewCommand([]string{"./http_server", addressPort}, netNs)
277         err := cmd.Start()
278         if err != nil {
279                 fmt.Println("Failed to start http server")
280                 return
281         }
282         running <- struct{}{}
283         <-done
284         cmd.Process.Kill()
285 }
286
287 func startWget(finished chan error, server_ip, port string, netNs string) {
288         fname := "test_file_10M"
289         defer func() {
290                 finished <- errors.New("wget error")
291         }()
292
293         cmd := NewCommand([]string{"wget", "--tries=5", "-q", "-O", "/dev/null", server_ip + ":" + port + "/" + fname},
294                 netNs)
295         o, err := cmd.CombinedOutput()
296         if err != nil {
297                 fmt.Printf("wget error: '%s'.\n%s", err, o)
298                 return
299         }
300         fmt.Printf("Client output: %s", o)
301         finished <- nil
302 }
303
304 func (c *Stanza) NewStanza(name string) *Stanza {
305         c.Append("\n" + name + " {")
306         c.pad += 2
307         return c
308 }
309
310 func (c *Stanza) Append(name string) *Stanza {
311         c.content += strings.Repeat(" ", c.pad)
312         c.content += name + "\n"
313         return c
314 }
315
316 func (c *Stanza) Close() *Stanza {
317         c.content += "}\n"
318         c.pad -= 2
319         return c
320 }
321
322 func (s *Stanza) ToString() string {
323         return s.content
324 }
325
326 func (s *Stanza) SaveToFile(fileName string) error {
327         fo, err := os.Create(fileName)
328         if err != nil {
329                 return err
330         }
331         defer fo.Close()
332
333         _, err = io.Copy(fo, strings.NewReader(s.content))
334         return err
335 }