Skip to content

Commit

Permalink
add "command" parameter to docker provisioning
Browse files Browse the repository at this point in the history
setting a "command" will set the container command, i.e. will override
the default argument executed when the container starts. It supports
template expansion.
  • Loading branch information
WanzenBug committed Nov 30, 2020
1 parent 283145d commit 23a5c14
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 8 deletions.
2 changes: 1 addition & 1 deletion cmd/vm_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions doc/provisioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<repository>/<image>:<tag>`. 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 `<args>...` in `docker run <image> <args>...`). 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.
Expand Down
2 changes: 1 addition & 1 deletion internal/virter/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
28 changes: 22 additions & 6 deletions internal/virter/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions internal/virter/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func TestNewProvisionConfigTemplate(t *testing.T) {
[[steps]]
[steps.docker]
image = "some-image"
command = ["exit", "0"]
[steps.docker.env]
foo = "bar"
Expand All @@ -144,6 +145,7 @@ ShellEnv = "default-value"
[[steps]]
[steps.docker]
image = "{{.DockerImage}}"
command = ["echo", "{{.DockerCommandArg}}"]
[steps.docker.env]
foo = "hello {{.DockerEnv}}"
Expand Down Expand Up @@ -191,6 +193,7 @@ foo = "{{.ShellEnv"
ProvisionStep{
Docker: &ProvisionDockerStep{
Image: "some-image",
Command: []string{"exit", "0"},
Env: map[string]string{"foo": "bar"},
},
},
Expand All @@ -214,13 +217,15 @@ foo = "{{.ShellEnv"
Overrides: []string{
"values.DockerImage=template-image",
"values.DockerEnv=template-value",
"values.DockerCommandArg=template-arg",
"values.RsyncSource=template-source",
},
},
[]ProvisionStep{
ProvisionStep{
Docker: &ProvisionDockerStep{
Image: "template-image",
Command: []string{"echo", "template-arg"},
Env: map[string]string{"foo": "hello template-value"},
},
},
Expand Down

0 comments on commit 23a5c14

Please sign in to comment.