2022-12-01 09:56:37 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-12-14 16:30:04 +01:00
|
|
|
"os"
|
2023-01-26 10:07:29 +01:00
|
|
|
"os/exec"
|
2022-12-14 16:30:04 +01:00
|
|
|
"strings"
|
2022-12-01 09:56:37 +01:00
|
|
|
|
|
|
|
"github.com/edwarnicke/exechelper"
|
|
|
|
)
|
|
|
|
|
2023-02-07 20:40:27 +01:00
|
|
|
var (
|
|
|
|
workDir, _ = os.Getwd()
|
|
|
|
)
|
|
|
|
|
2022-12-06 19:46:24 +01:00
|
|
|
type Volume struct {
|
2023-01-26 10:07:29 +01:00
|
|
|
hostDir string
|
|
|
|
containerDir string
|
|
|
|
isDefaultWorkDir bool
|
2022-12-06 19:46:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type Container struct {
|
2023-01-26 10:07:29 +01:00
|
|
|
suite *HstSuite
|
2023-01-13 21:33:43 +01:00
|
|
|
isOptional bool
|
|
|
|
name string
|
|
|
|
image string
|
|
|
|
extraRunningArgs string
|
|
|
|
volumes map[string]Volume
|
|
|
|
envVars map[string]string
|
2023-01-26 10:07:29 +01:00
|
|
|
vppInstance *VppInstance
|
2022-12-14 16:30:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewContainer(yamlInput ContainerConfig) (*Container, error) {
|
|
|
|
containerName := yamlInput["name"].(string)
|
|
|
|
if len(containerName) == 0 {
|
|
|
|
err := fmt.Errorf("container name must not be blank")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var container = new(Container)
|
|
|
|
container.volumes = make(map[string]Volume)
|
|
|
|
container.envVars = make(map[string]string)
|
|
|
|
container.name = containerName
|
|
|
|
|
|
|
|
if image, ok := yamlInput["image"]; ok {
|
|
|
|
container.image = image.(string)
|
|
|
|
} else {
|
|
|
|
container.image = "hs-test/vpp"
|
|
|
|
}
|
|
|
|
|
2023-01-13 21:33:43 +01:00
|
|
|
if args, ok := yamlInput["extra-args"]; ok {
|
|
|
|
container.extraRunningArgs = args.(string)
|
|
|
|
} else {
|
|
|
|
container.extraRunningArgs = ""
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:30:04 +01:00
|
|
|
if isOptional, ok := yamlInput["is-optional"]; ok {
|
|
|
|
container.isOptional = isOptional.(bool)
|
|
|
|
} else {
|
|
|
|
container.isOptional = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := yamlInput["volumes"]; ok {
|
2023-01-09 12:07:09 +01:00
|
|
|
r := strings.NewReplacer("$HST_DIR", workDir)
|
2022-12-14 16:30:04 +01:00
|
|
|
for _, volu := range yamlInput["volumes"].([]interface{}) {
|
|
|
|
volumeMap := volu.(ContainerConfig)
|
|
|
|
hostDir := r.Replace(volumeMap["host-dir"].(string))
|
|
|
|
containerDir := volumeMap["container-dir"].(string)
|
2023-01-26 10:07:29 +01:00
|
|
|
isDefaultWorkDir := false
|
2022-12-14 16:30:04 +01:00
|
|
|
|
2023-01-26 10:07:29 +01:00
|
|
|
if isDefault, ok := volumeMap["is-default-work-dir"]; ok {
|
|
|
|
isDefaultWorkDir = isDefault.(bool)
|
2022-12-14 16:30:04 +01:00
|
|
|
}
|
|
|
|
|
2023-01-26 10:07:29 +01:00
|
|
|
container.addVolume(hostDir, containerDir, isDefaultWorkDir)
|
|
|
|
|
2022-12-14 16:30:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := yamlInput["vars"]; ok {
|
|
|
|
for _, envVar := range yamlInput["vars"].([]interface{}) {
|
2023-01-26 10:07:29 +01:00
|
|
|
envVarMap := envVar.(ContainerConfig)
|
|
|
|
name := envVarMap["name"].(string)
|
|
|
|
value := envVarMap["value"].(string)
|
|
|
|
container.addEnvVar(name, value)
|
2022-12-14 16:30:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return container, nil
|
2022-12-01 09:56:37 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 08:58:04 +01:00
|
|
|
func (c *Container) Suite() *HstSuite {
|
|
|
|
return c.suite
|
|
|
|
}
|
|
|
|
|
2023-01-26 10:07:29 +01:00
|
|
|
func (c *Container) getWorkDirVolume() (res Volume, exists bool) {
|
|
|
|
for _, v := range c.volumes {
|
|
|
|
if v.isDefaultWorkDir {
|
|
|
|
res = v
|
|
|
|
exists = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Container) GetHostWorkDir() (res string) {
|
|
|
|
if v, ok := c.getWorkDirVolume(); ok {
|
|
|
|
res = v.hostDir
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Container) GetContainerWorkDir() (res string) {
|
|
|
|
if v, ok := c.getWorkDirVolume(); ok {
|
|
|
|
res = v.containerDir
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-12-19 20:35:27 +01:00
|
|
|
func (c *Container) getRunCommand() string {
|
2022-12-14 16:30:04 +01:00
|
|
|
cmd := "docker run --cap-add=all -d --privileged --network host --rm"
|
|
|
|
cmd += c.getVolumesAsCliOption()
|
|
|
|
cmd += c.getEnvVarsAsCliOption()
|
2023-01-13 21:33:43 +01:00
|
|
|
cmd += " --name " + c.name + " " + c.image + " " + c.extraRunningArgs
|
2022-12-19 20:35:27 +01:00
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Container) run() error {
|
|
|
|
if c.name == "" {
|
|
|
|
return fmt.Errorf("run container failed: name is blank")
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := c.getRunCommand()
|
2022-12-01 09:56:37 +01:00
|
|
|
err := exechelper.Run(cmd)
|
|
|
|
if err != nil {
|
2022-12-14 16:30:04 +01:00
|
|
|
return fmt.Errorf("container run failed: %s", err)
|
2022-12-01 09:56:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-26 10:07:29 +01:00
|
|
|
func (c *Container) addVolume(hostDir string, containerDir string, isDefaultWorkDir bool) {
|
2022-12-14 16:30:04 +01:00
|
|
|
var volume Volume
|
|
|
|
volume.hostDir = hostDir
|
|
|
|
volume.containerDir = containerDir
|
2023-01-26 10:07:29 +01:00
|
|
|
volume.isDefaultWorkDir = isDefaultWorkDir
|
2022-12-14 16:30:04 +01:00
|
|
|
c.volumes[hostDir] = volume
|
2022-12-06 19:46:24 +01:00
|
|
|
}
|
|
|
|
|
2022-12-14 16:30:04 +01:00
|
|
|
func (c *Container) getVolumesAsCliOption() string {
|
|
|
|
cliOption := ""
|
2022-12-06 19:46:24 +01:00
|
|
|
|
|
|
|
if len(c.volumes) > 0 {
|
|
|
|
for _, volume := range c.volumes {
|
2022-12-14 16:30:04 +01:00
|
|
|
cliOption += fmt.Sprintf(" -v %s:%s", volume.hostDir, volume.containerDir)
|
2022-12-06 19:46:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:30:04 +01:00
|
|
|
return cliOption
|
|
|
|
}
|
|
|
|
|
2023-01-26 10:07:29 +01:00
|
|
|
func (c *Container) addEnvVar(name string, value string) {
|
2022-12-14 16:30:04 +01:00
|
|
|
c.envVars[name] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Container) getEnvVarsAsCliOption() string {
|
|
|
|
cliOption := ""
|
|
|
|
if len(c.envVars) == 0 {
|
|
|
|
return cliOption
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, value := range c.envVars {
|
|
|
|
cliOption += fmt.Sprintf(" -e %s=%s", name, value)
|
|
|
|
}
|
|
|
|
return cliOption
|
|
|
|
}
|
|
|
|
|
2023-01-26 10:07:29 +01:00
|
|
|
func (c *Container) newVppInstance(additionalConfig ...Stanza) (*VppInstance, error) {
|
|
|
|
vpp := new(VppInstance)
|
|
|
|
vpp.container = c
|
2023-02-14 12:56:49 +01:00
|
|
|
|
|
|
|
if len(additionalConfig) > 0 {
|
|
|
|
vpp.additionalConfig = additionalConfig[0]
|
|
|
|
}
|
2023-01-26 10:07:29 +01:00
|
|
|
|
|
|
|
c.vppInstance = vpp
|
|
|
|
|
|
|
|
return vpp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Container) copy(sourceFileName string, targetFileName string) error {
|
|
|
|
cmd := exec.Command("docker", "cp", sourceFileName, c.name+":"+targetFileName)
|
|
|
|
return cmd.Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Container) createFile(destFileName string, content string) error {
|
|
|
|
f, err := os.CreateTemp("/tmp", "hst-config")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer os.Remove(f.Name())
|
|
|
|
|
|
|
|
if _, err := f.Write([]byte(content)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.copy(f.Name(), destFileName)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Executes in detached mode so that the started application can continue to run
|
|
|
|
* without blocking execution of test
|
|
|
|
*/
|
2023-02-02 08:58:04 +01:00
|
|
|
func (c *Container) execServer(command string, arguments ...any) {
|
|
|
|
serverCommand := fmt.Sprintf(command, arguments...)
|
|
|
|
containerExecCommand := "docker exec -d" + c.getEnvVarsAsCliOption() +
|
|
|
|
" " + c.name + " " + serverCommand
|
|
|
|
c.Suite().log(containerExecCommand)
|
|
|
|
c.Suite().assertNil(exechelper.Run(containerExecCommand))
|
2023-01-26 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 08:58:04 +01:00
|
|
|
func (c *Container) exec(command string, arguments ...any) string {
|
|
|
|
cliCommand := fmt.Sprintf(command, arguments...)
|
|
|
|
containerExecCommand := "docker exec" + c.getEnvVarsAsCliOption() +
|
|
|
|
" " + c.name + " " + cliCommand
|
|
|
|
c.Suite().log(containerExecCommand)
|
|
|
|
byteOutput, err := exechelper.CombinedOutput(containerExecCommand)
|
|
|
|
c.Suite().assertNil(err)
|
|
|
|
return string(byteOutput)
|
2022-12-14 16:30:04 +01:00
|
|
|
}
|
|
|
|
|
2022-12-06 19:46:24 +01:00
|
|
|
func (c *Container) stop() error {
|
2023-01-26 10:07:29 +01:00
|
|
|
if c.vppInstance != nil && c.vppInstance.apiChannel != nil {
|
|
|
|
c.vppInstance.disconnect()
|
|
|
|
}
|
|
|
|
c.vppInstance = nil
|
2023-01-17 13:02:51 -08:00
|
|
|
return exechelper.Run("docker stop " + c.name + " -t 0")
|
2022-12-06 19:46:24 +01:00
|
|
|
}
|