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