From 23a5c143616e734cd5d5055936410fd9cb297e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20=22WanzenBug=22=20Wanzenb=C3=B6ck?= Date: Mon, 30 Nov 2020 09:14:09 +0100 Subject: [PATCH] add "command" parameter to docker provisioning setting a "command" will set the container command, i.e. will override the default argument executed when the container starts. It supports template expansion. --- cmd/vm_exec.go | 2 +- doc/provisioning.md | 1 + internal/virter/image.go | 2 +- internal/virter/provision.go | 28 ++++++++++++++++++++++------ internal/virter/provision_test.go | 5 +++++ 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/cmd/vm_exec.go b/cmd/vm_exec.go index 5243372..48ce275 100644 --- a/cmd/vm_exec.go +++ b/cmd/vm_exec.go @@ -84,7 +84,7 @@ func execDocker(ctx context.Context, v *virter.Virter, s *virter.ProvisionDocker log.Fatal(err) } - containerCfg := containerapi.NewContainerConfig("virter-"+strings.Join(vmNames, "-"), s.Image, s.Env) + containerCfg := containerapi.NewContainerConfig("virter-"+strings.Join(vmNames, "-"), s.Image, s.Env, containerapi.WithCommand(s.Command...)) return v.VMExecDocker(ctx, containerProvider, vmNames, containerCfg, privateKey, s.Copy) } diff --git a/doc/provisioning.md b/doc/provisioning.md index e842160..12ce817 100644 --- a/doc/provisioning.md +++ b/doc/provisioning.md @@ -26,6 +26,7 @@ A `docker` provisioning step allows specifying a Docker image to run provisionin The Docker provisioning step can be parameterized using the following configuration options: * `image` is the Docker image used to provision the VM. It follows the standard Docker format of `/:`. This is a Go template. * `env` is a map of environment variables to be passed to the Docker container, in `KEY=value` format. The values are Go templates. +* `command` is a string array and sets the command to execute in the container (basically `...` in `docker run ...`). The items are Go templates. Note that Virter already passes two environment variables by default: * `TARGETS` is a comma separated list of all VMs to run the provisioning on. diff --git a/internal/virter/image.go b/internal/virter/image.go index ee40fee..4616367 100644 --- a/internal/virter/image.go +++ b/internal/virter/image.go @@ -123,7 +123,7 @@ func (v *Virter) imageBuildProvisionCommit(ctx context.Context, tools ImageBuild for _, s := range buildConfig.ProvisionConfig.Steps { if s.Docker != nil { - containerCfg := containerapi.NewContainerConfig(buildConfig.ContainerName, s.Docker.Image, s.Docker.Env) + containerCfg := containerapi.NewContainerConfig(buildConfig.ContainerName, s.Docker.Image, s.Docker.Env, containerapi.WithCommand(s.Docker.Command...)) err = v.VMExecDocker(ctx, tools.ContainerProvider, vmNames, containerCfg, sshPrivateKey, nil) } else if s.Shell != nil { err = v.VMExecShell(ctx, vmNames, sshPrivateKey, s.Shell) diff --git a/internal/virter/provision.go b/internal/virter/provision.go index 4d60af2..7df155c 100644 --- a/internal/virter/provision.go +++ b/internal/virter/provision.go @@ -14,9 +14,10 @@ import ( // ProvisionDockerStep is a single provisioniong step executed in a docker container type ProvisionDockerStep struct { - Image string `toml:"image"` - Env map[string]string `toml:"env"` - Copy *ProvisionDockerCopyStep `toml:"copy"` + Image string `toml:"image"` + Env map[string]string `toml:"env"` + Command []string `toml:"command"` + Copy *ProvisionDockerCopyStep `toml:"copy"` } type ProvisionDockerCopyStep struct { @@ -134,10 +135,14 @@ func newProvisionConfigReader(provReader io.Reader, provOpt ProvisionOption) (Pr return pc, fmt.Errorf("failed to execute template for docker.image for step %d: %w", i, err) } - if err := executeTemplates(s.Docker.Env, pc.Values); err != nil { + if err := executeTemplateMap(s.Docker.Env, pc.Values); err != nil { return pc, fmt.Errorf("failed to execute template for docker.env for step %d: %w", i, err) } + if err := executeTemplateArray(s.Docker.Command, pc.Values); err != nil { + return pc, fmt.Errorf("failed to execute tempalte for docker.command for step %d: %w", i, err) + } + if copyStep := s.Docker.Copy; copyStep != nil { if copyStep.Dest, err = executeTemplate(copyStep.Dest, pc.Values); err != nil { return pc, fmt.Errorf("failed to execute template for docker.copy.dest for step %d: %w", i, err) @@ -146,7 +151,7 @@ func newProvisionConfigReader(provReader io.Reader, provOpt ProvisionOption) (Pr } else if s.Shell != nil { s.Shell.Env = mergeEnv(&pc.Env, &s.Shell.Env) - if err := executeTemplates(s.Shell.Env, pc.Values); err != nil { + if err := executeTemplateMap(s.Shell.Env, pc.Values); err != nil { return pc, fmt.Errorf("failed to execute template for shell.env for step %d: %w", i, err) } } else if s.Rsync != nil { @@ -171,7 +176,7 @@ func genValueMap(provOpt ProvisionOption) (map[string]interface{}, error) { return base, nil } -func executeTemplates(templates map[string]string, templateData map[string]string) error { +func executeTemplateMap(templates map[string]string, templateData map[string]string) error { for k, v := range templates { result, err := executeTemplate(v, templateData) if err != nil { @@ -182,6 +187,17 @@ func executeTemplates(templates map[string]string, templateData map[string]strin return nil } +func executeTemplateArray(templates []string, templateData map[string]string) error { + for i, t := range templates { + result, err := executeTemplate(t, templateData) + if err != nil { + return err + } + templates[i] = result + } + return nil +} + func executeTemplate(templateText string, templateData map[string]string) (string, error) { tmpl, err := template.New("").Option("missingkey=error").Parse(templateText) if err != nil { diff --git a/internal/virter/provision_test.go b/internal/virter/provision_test.go index 3ee010c..6ec7633 100644 --- a/internal/virter/provision_test.go +++ b/internal/virter/provision_test.go @@ -122,6 +122,7 @@ func TestNewProvisionConfigTemplate(t *testing.T) { [[steps]] [steps.docker] image = "some-image" +command = ["exit", "0"] [steps.docker.env] foo = "bar" @@ -144,6 +145,7 @@ ShellEnv = "default-value" [[steps]] [steps.docker] image = "{{.DockerImage}}" +command = ["echo", "{{.DockerCommandArg}}"] [steps.docker.env] foo = "hello {{.DockerEnv}}" @@ -191,6 +193,7 @@ foo = "{{.ShellEnv" ProvisionStep{ Docker: &ProvisionDockerStep{ Image: "some-image", + Command: []string{"exit", "0"}, Env: map[string]string{"foo": "bar"}, }, }, @@ -214,6 +217,7 @@ foo = "{{.ShellEnv" Overrides: []string{ "values.DockerImage=template-image", "values.DockerEnv=template-value", + "values.DockerCommandArg=template-arg", "values.RsyncSource=template-source", }, }, @@ -221,6 +225,7 @@ foo = "{{.ShellEnv" ProvisionStep{ Docker: &ProvisionDockerStep{ Image: "template-image", + Command: []string{"echo", "template-arg"}, Env: map[string]string{"foo": "hello template-value"}, }, },