blob: 874ce3d1750e53481925e0360351e7e3b54a496e [file] [log] [blame]
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
extraRunningArgs 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 args, ok := yamlInput["extra-args"]; ok {
container.extraRunningArgs = args.(string)
} else {
container.extraRunningArgs = ""
}
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) getRunCommand() string {
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 + " " + c.extraRunningArgs
return cmd
}
func (c *Container) run() error {
if c.name == "" {
return fmt.Errorf("run container failed: name is blank")
}
exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", c.name))
cmd := c.getRunCommand()
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)
}