blob: 874ce3d1750e53481925e0360351e7e3b54a496e [file] [log] [blame]
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01001package main
2
3import (
4 "fmt"
Maros Ondrejickadb823ed2022-12-14 16:30:04 +01005 "os"
6 "strings"
Maros Ondrejicka11a03e92022-12-01 09:56:37 +01007
8 "github.com/edwarnicke/exechelper"
9)
10
Maros Ondrejicka0e79abb2022-12-06 19:46:24 +010011type Volume struct {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010012 hostDir string
13 containerDir string
Maros Ondrejicka0e79abb2022-12-06 19:46:24 +010014}
15
16type Container struct {
Filip Tehlar3f951432023-01-13 21:33:43 +010017 isOptional bool
18 name string
19 image string
20 workDir string
21 extraRunningArgs string
22 volumes map[string]Volume
23 envVars map[string]string
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010024}
25
26func 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
Filip Tehlar3f951432023-01-13 21:33:43 +010044 if args, ok := yamlInput["extra-args"]; ok {
45 container.extraRunningArgs = args.(string)
46 } else {
47 container.extraRunningArgs = ""
48 }
49
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010050 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 {
Filip Tehlarf3ee2b62023-01-09 12:07:09 +010057 r := strings.NewReplacer("$HST_DIR", workDir)
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010058 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 &&
Filip Tehlarf3ee2b62023-01-09 12:07:09 +010065 isDefaultWorkDir.(bool) &&
66 len(container.workDir) == 0 {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010067 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
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010079}
80
Maros Ondrejicka87531802022-12-19 20:35:27 +010081func (c *Container) getRunCommand() string {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010082 syncPath := fmt.Sprintf(" -v %s:/tmp/sync", c.getSyncPath())
83 cmd := "docker run --cap-add=all -d --privileged --network host --rm"
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010084 cmd += syncPath
Maros Ondrejickadb823ed2022-12-14 16:30:04 +010085 cmd += c.getVolumesAsCliOption()
86 cmd += c.getEnvVarsAsCliOption()
Filip Tehlar3f951432023-01-13 21:33:43 +010087 cmd += " --name " + c.name + " " + c.image + " " + c.extraRunningArgs
Maros Ondrejicka87531802022-12-19 20:35:27 +010088 return cmd
89}
90
91func (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()
Maros Ondrejicka11a03e92022-12-01 09:56:37 +010098 err := exechelper.Run(cmd)
99 if err != nil {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100100 return fmt.Errorf("container run failed: %s", err)
Maros Ondrejicka11a03e92022-12-01 09:56:37 +0100101 }
102
103 return nil
104}
105
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100106func (c *Container) addVolume(hostDir string, containerDir string) {
107 var volume Volume
108 volume.hostDir = hostDir
109 volume.containerDir = containerDir
110 c.volumes[hostDir] = volume
Maros Ondrejicka0e79abb2022-12-06 19:46:24 +0100111}
112
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100113func (c *Container) getVolumeByHostDir(hostDir string) Volume {
114 return c.volumes[hostDir]
115}
116
117func (c *Container) getVolumesAsCliOption() string {
118 cliOption := ""
Maros Ondrejicka0e79abb2022-12-06 19:46:24 +0100119
120 if len(c.volumes) > 0 {
121 for _, volume := range c.volumes {
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100122 cliOption += fmt.Sprintf(" -v %s:%s", volume.hostDir, volume.containerDir)
Maros Ondrejicka0e79abb2022-12-06 19:46:24 +0100123 }
124 }
125
Maros Ondrejickadb823ed2022-12-14 16:30:04 +0100126 return cliOption
127}
128
129func (c *Container) getWorkDirAsCliOption() string {
130 if len(c.workDir) == 0 {
131 return ""
132 }
133 return fmt.Sprintf(" --workdir=\"%s\"", c.workDir)
134}
135
136func (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
143func (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
155func (c *Container) getSyncPath() string {
156 return fmt.Sprintf("/tmp/%s/sync", c.name)
157}
158
159func (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
165func (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
Maros Ondrejicka0e79abb2022-12-06 19:46:24 +0100187}
188
189func (c *Container) stop() error {
190 return exechelper.Run("docker stop " + c.name)
191}