hs-test: restrict concurrency on envoy
[vpp.git] / extras / hs-test / container.go
1 package main
2
3 import (
4         "fmt"
5         "os"
6         "strings"
7
8         "github.com/edwarnicke/exechelper"
9 )
10
11 type Volume struct {
12         hostDir      string
13         containerDir string
14 }
15
16 type Container struct {
17         isOptional       bool
18         name             string
19         image            string
20         workDir          string
21         extraRunningArgs string
22         volumes          map[string]Volume
23         envVars          map[string]string
24 }
25
26 func NewContainer(yamlInput ContainerConfig) (*Container, error) {
27         containerName := yamlInput["name"].(string)
28         if len(containerName) == 0 {
29                 err := fmt.Errorf("container name must not be blank")
30                 return nil, err
31         }
32
33         var container = new(Container)
34         container.volumes = make(map[string]Volume)
35         container.envVars = make(map[string]string)
36         container.name = containerName
37
38         if image, ok := yamlInput["image"]; ok {
39                 container.image = image.(string)
40         } else {
41                 container.image = "hs-test/vpp"
42         }
43
44         if args, ok := yamlInput["extra-args"]; ok {
45                 container.extraRunningArgs = args.(string)
46         } else {
47                 container.extraRunningArgs = ""
48         }
49
50         if isOptional, ok := yamlInput["is-optional"]; ok {
51                 container.isOptional = isOptional.(bool)
52         } else {
53                 container.isOptional = false
54         }
55
56         if _, ok := yamlInput["volumes"]; ok {
57                 r := strings.NewReplacer("$HST_DIR", workDir)
58                 for _, volu := range yamlInput["volumes"].([]interface{}) {
59                         volumeMap := volu.(ContainerConfig)
60                         hostDir := r.Replace(volumeMap["host-dir"].(string))
61                         containerDir := volumeMap["container-dir"].(string)
62                         container.addVolume(hostDir, containerDir)
63
64                         if isDefaultWorkDir, ok := volumeMap["is-default-work-dir"]; ok &&
65                                 isDefaultWorkDir.(bool) &&
66                                 len(container.workDir) == 0 {
67                                 container.workDir = containerDir
68                         }
69
70                 }
71         }
72
73         if _, ok := yamlInput["vars"]; ok {
74                 for _, envVar := range yamlInput["vars"].([]interface{}) {
75                         container.addEnvVar(envVar)
76                 }
77         }
78         return container, nil
79 }
80
81 func (c *Container) getRunCommand() string {
82         syncPath := fmt.Sprintf(" -v %s:/tmp/sync", c.getSyncPath())
83         cmd := "docker run --cap-add=all -d --privileged --network host --rm"
84         cmd += syncPath
85         cmd += c.getVolumesAsCliOption()
86         cmd += c.getEnvVarsAsCliOption()
87         cmd += " --name " + c.name + " " + c.image + " " + c.extraRunningArgs
88         return cmd
89 }
90
91 func (c *Container) run() error {
92         if c.name == "" {
93                 return fmt.Errorf("run container failed: name is blank")
94         }
95
96         exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", c.name))
97         cmd := c.getRunCommand()
98         err := exechelper.Run(cmd)
99         if err != nil {
100                 return fmt.Errorf("container run failed: %s", err)
101         }
102
103         return nil
104 }
105
106 func (c *Container) addVolume(hostDir string, containerDir string) {
107         var volume Volume
108         volume.hostDir = hostDir
109         volume.containerDir = containerDir
110         c.volumes[hostDir] = volume
111 }
112
113 func (c *Container) getVolumeByHostDir(hostDir string) Volume {
114         return c.volumes[hostDir]
115 }
116
117 func (c *Container) getVolumesAsCliOption() string {
118         cliOption := ""
119
120         if len(c.volumes) > 0 {
121                 for _, volume := range c.volumes {
122                         cliOption += fmt.Sprintf(" -v %s:%s", volume.hostDir, volume.containerDir)
123                 }
124         }
125
126         return cliOption
127 }
128
129 func (c *Container) getWorkDirAsCliOption() string {
130         if len(c.workDir) == 0 {
131                 return ""
132         }
133         return fmt.Sprintf(" --workdir=\"%s\"", c.workDir)
134 }
135
136 func (c *Container) addEnvVar(envVar interface{}) {
137         envVarMap := envVar.(ContainerConfig)
138         name := envVarMap["name"].(string)
139         value := envVarMap["value"].(string)
140         c.envVars[name] = value
141 }
142
143 func (c *Container) getEnvVarsAsCliOption() string {
144         cliOption := ""
145         if len(c.envVars) == 0 {
146                 return cliOption
147         }
148
149         for name, value := range c.envVars {
150                 cliOption += fmt.Sprintf(" -e %s=%s", name, value)
151         }
152         return cliOption
153 }
154
155 func (c *Container) getSyncPath() string {
156         return fmt.Sprintf("/tmp/%s/sync", c.name)
157 }
158
159 func (c *Container) exec(command string) (string, error) {
160         cliCommand := "docker exec -d " + c.name + " " + command
161         byteOutput, err := exechelper.CombinedOutput(cliCommand)
162         return string(byteOutput), err
163 }
164
165 func (c *Container) execAction(args string) (string, error) {
166         syncFile := c.getSyncPath() + "/rc"
167         os.Remove(syncFile)
168
169         workDir := c.getWorkDirAsCliOption()
170         cmd := fmt.Sprintf("docker exec -d %s %s hs-test %s",
171                 workDir,
172                 c.name,
173                 args)
174         err := exechelper.Run(cmd)
175         if err != nil {
176                 return "", err
177         }
178         res, err := waitForSyncFile(syncFile)
179         if err != nil {
180                 return "", fmt.Errorf("failed to read sync file while executing 'hs-test %s': %v", args, err)
181         }
182         o := res.StdOutput + res.ErrOutput
183         if res.Code != 0 {
184                 return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc)
185         }
186         return o, err
187 }
188
189 func (c *Container) stop() error {
190         return exechelper.Run("docker stop " + c.name)
191 }