vpp/extras/hs-test/container.go
Maros Ondrejicka db823ed6e9 hs-test: abstract away topology from test cases
Definition of shared volumes and containers has been moved
to yaml files to be together with network topology.
Containers are automatically run at the beginning of each test case
and stopped afterward.

Type: test
Signed-off-by: Maros Ondrejicka <maros.ondrejicka@pantheon.tech>
Change-Id: I264cbb4f1355f8bd7aade221e9609fb5b9bd693e
2022-12-19 17:11:52 +00:00

181 lines
4.2 KiB
Go

package main
import (
"fmt"
"os"
"strings"
"github.com/edwarnicke/exechelper"
)
type Volume struct {
hostDir string
containerDir string
}
type Container struct {
isOptional bool
name string
image string
workDir string
volumes map[string]Volume
envVars map[string]string
}
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"
}
if isOptional, ok := yamlInput["is-optional"]; ok {
container.isOptional = isOptional.(bool)
} else {
container.isOptional = false
}
if _, ok := yamlInput["volumes"]; ok {
r:= strings.NewReplacer("$HST_DIR", workDir)
for _, volu := range yamlInput["volumes"].([]interface{}) {
volumeMap := volu.(ContainerConfig)
hostDir := r.Replace(volumeMap["host-dir"].(string))
containerDir := volumeMap["container-dir"].(string)
container.addVolume(hostDir, containerDir)
if isDefaultWorkDir, ok := volumeMap["is-default-work-dir"]; ok &&
isDefaultWorkDir.(bool) &&
len(container.workDir) == 0 {
container.workDir = containerDir
}
}
}
if _, ok := yamlInput["vars"]; ok {
for _, envVar := range yamlInput["vars"].([]interface{}) {
container.addEnvVar(envVar)
}
}
return container, nil
}
func (c *Container) run() error {
if c.name == "" {
return fmt.Errorf("create volume failed: container name is blank")
}
exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", c.name))
syncPath := fmt.Sprintf(" -v %s:/tmp/sync", c.getSyncPath())
cmd := "docker run --cap-add=all -d --privileged --network host --rm"
cmd += syncPath
cmd += c.getVolumesAsCliOption()
cmd += c.getEnvVarsAsCliOption()
cmd += " --name " + c.name + " " + c.image
fmt.Println(cmd)
err := exechelper.Run(cmd)
if err != nil {
return fmt.Errorf("container run failed: %s", err)
}
return nil
}
func (c *Container) addVolume(hostDir string, containerDir string) {
var volume Volume
volume.hostDir = hostDir
volume.containerDir = containerDir
c.volumes[hostDir] = volume
}
func (c *Container) getVolumeByHostDir(hostDir string) Volume {
return c.volumes[hostDir]
}
func (c *Container) getVolumesAsCliOption() string {
cliOption := ""
if len(c.volumes) > 0 {
for _, volume := range c.volumes {
cliOption += fmt.Sprintf(" -v %s:%s", volume.hostDir, volume.containerDir)
}
}
return cliOption
}
func (c *Container) getWorkDirAsCliOption() string {
if len(c.workDir) == 0 {
return ""
}
return fmt.Sprintf(" --workdir=\"%s\"", c.workDir)
}
func (c *Container) addEnvVar(envVar interface{}) {
envVarMap := envVar.(ContainerConfig)
name := envVarMap["name"].(string)
value := envVarMap["value"].(string)
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
}
func (c *Container) getSyncPath() string {
return fmt.Sprintf("/tmp/%s/sync", c.name)
}
func (c *Container) exec(command string) (string, error) {
cliCommand := "docker exec -d " + c.name + " " + command
byteOutput, err := exechelper.CombinedOutput(cliCommand)
return string(byteOutput), err
}
func (c *Container) execAction(args string) (string, error) {
syncFile := c.getSyncPath() + "/rc"
os.Remove(syncFile)
workDir := c.getWorkDirAsCliOption()
cmd := fmt.Sprintf("docker exec -d %s %s hs-test %s",
workDir,
c.name,
args)
err := exechelper.Run(cmd)
if err != nil {
return "", err
}
res, err := waitForSyncFile(syncFile)
if err != nil {
return "", fmt.Errorf("failed to read sync file while executing 'hs-test %s': %v", args, err)
}
o := res.StdOutput + res.ErrOutput
if res.Code != 0 {
return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc)
}
return o, err
}
func (c *Container) stop() error {
return exechelper.Run("docker stop " + c.name)
}