Skip to content

Commit

Permalink
rename "docker" provisioning step to "container"
Browse files Browse the repository at this point in the history
We also support Podman containers, so we should not limit this to just
docker. Adjust the documentation, examples, etc accordingly.

Since this is a breaking change, increase the provision file version.
  • Loading branch information
chrboe committed Mar 27, 2023
1 parent b04dbe5 commit 898964d
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ e2e-test:
- virter registry update
- cd tests/
- EXCLUDED_BASE_IMAGES=$EXCLUDED PULL_LOCATION=${LINBIT_DOCKER_REGISTRY}/vm ./all-base-images.sh > vms.toml
- vmshed --set values.DockerImage=${LINBIT_DOCKER_REGISTRY}/virter/ssh:$CI_COMMIT_REF_SLUG --nvms ${LINBIT_CI_MAX_CPUS:-20}
- vmshed --set values.ContainerImage=${LINBIT_DOCKER_REGISTRY}/virter/ssh:$CI_COMMIT_REF_SLUG --nvms ${LINBIT_CI_MAX_CPUS:-20}
artifacts:
when: always
paths:
Expand Down
2 changes: 1 addition & 1 deletion cmd/image_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func imagePullCommand() *cobra.Command {
Use: "pull name [tag|url]",
Short: "Pull an image",
Long: `Pull an image into a libvirt storage pool. If a URL or
Docker tag is explicitly given, the image will be fetched from there.
Container tag is explicitly given, the image will be fetched from there.
Otherwise the URL for the specified name from the local image registry
will be used.`,
Args: cobra.RangeArgs(1, 2),
Expand Down
8 changes: 4 additions & 4 deletions cmd/vm_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func execProvision(ctx context.Context, provOpt virter.ProvisionOption, vmNames
defer v.ForceDisconnect()

for _, s := range pc.Steps {
if s.Docker != nil {
if err := execDocker(ctx, v, s.Docker, vmNames); err != nil {
if s.Container != nil {
if err := execContainer(ctx, v, s.Container, vmNames); err != nil {
return err
}
} else if s.Shell != nil {
Expand All @@ -79,7 +79,7 @@ func execProvision(ctx context.Context, provOpt virter.ProvisionOption, vmNames
return nil
}

func execDocker(ctx context.Context, v *virter.Virter, s *virter.ProvisionDockerStep, vmNames []string) error {
func execContainer(ctx context.Context, v *virter.Virter, s *virter.ProvisionContainerStep, vmNames []string) error {
containerProvider, err := containerapi.NewProvider(ctx, containerProvider())
if err != nil {
return err
Expand All @@ -94,5 +94,5 @@ func execDocker(ctx context.Context, v *virter.Virter, s *virter.ProvisionDocker
containerapi.WithPullConfig(s.Pull.ForContainer()),
)

return v.VMExecDocker(ctx, containerProvider, vmNames, containerCfg, s.Copy)
return v.VMExecContainer(ctx, containerProvider, vmNames, containerCfg, s.Copy)
}
20 changes: 11 additions & 9 deletions doc/provisioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ $ virter vm exec -p provisioning.toml centos-1 centos-2 centos-3

The following provisioning types are supported.

### `docker`
A `docker` provisioning step allows specifying a Docker image to run provisioning steps in. This image will be executed on the host, so it will need to connect to the target VM and run its provisioning commands over SSH (or use a provisioning tool such as Ansible).
### `container`
A `container` provisioning step allows specifying a container image to run provisioning steps in.
This image will be executed on the host using the provider configured in `container.provider` (such as Docker or Podman).
It will need to connect to the target VM and run its provisioning commands over SSH (or use a provisioning tool such as Ansible).

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.
The container provisioning step can be parameterized using the following configuration options:
* `image` is the container image used to provision the VM. It follows the standard format of `<repository>/<image>:<tag>`. This is a Go template.
* `pull` specifies when the above image should be pulled. Valid values are `Always`, `IfNotExist` or `Never`. If not specified, the default is `IfNotExist`. Can be overridden during execution using `--container-pull-policy`.
* `env` is a map of environment variables to be passed to the Docker container, in `KEY=value` format. The values are Go templates.
* `env` is a map of environment variables to be passed to the container, in `KEY=value` format. The values 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 All @@ -42,7 +44,7 @@ In addition, every container binds the following paths:
* A SSH config file that contains a mapping from VMs to user names to be used for ssh connections (to support platforms where the "root" user does not exist). This file is mapped under `/etc/ssh/ssh_config.virter`. Use `ssh -F /etc/ssh/ssh_config.virter <ip-address-or-hostname>` to use this file (without specifying user name explicitly).

### Shell
The `shell` provisioning step allows running arbitrary commands on the target VM over SSH. This is easier to use than the `docker` step, but also less flexible.
The `shell` provisioning step allows running arbitrary commands on the target VM over SSH. This is easier to use than the `container` step, but also less flexible.

The `shell` provisioning step accepts the following parameters:
* `script` is a string containing the command(s) to be run.
Expand Down Expand Up @@ -89,13 +91,13 @@ Image = "virter-hello-text"
foo = "rck"
[[steps]]
[steps.docker]
[steps.container]
# Go templating can be used for many values
image = "{{.Image}}"
[steps.docker.env]
[steps.container.env]
TEXT = "foo"
VAR_BAR = "hi"
[steps.docker.copy]
[steps.container.copy]
source = "/tmp/somefile"
dest = "."
Expand Down
4 changes: 2 additions & 2 deletions examples/hello-world-docker/hello-world-docker.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
Image = "virter-hello-world"

[[steps]]
[steps.docker]
[steps.container]
image = "{{.Image}}"
pull = "Never"
# example of how to copy a file from the container after provisioning is finished:
[steps.docker.copy]
[steps.container.copy]
source = "/entry.sh"
dest = "/tmp/"
8 changes: 4 additions & 4 deletions internal/virter/docker.go → internal/virter/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const (
colorReset = "\u001b[0m"
)

func containerRun(ctx context.Context, containerProvider containerapi.ContainerProvider, containerCfg *containerapi.ContainerConfig, vmNames []string, vmSSHUserNames []string, keyStore sshkeys.KeyStore, knownHosts sshkeys.KnownHosts, copyStep *ProvisionDockerCopyStep) error {
func containerRun(ctx context.Context, containerProvider containerapi.ContainerProvider, containerCfg *containerapi.ContainerConfig, vmNames []string, vmSSHUserNames []string, keyStore sshkeys.KeyStore, knownHosts sshkeys.KnownHosts, copyStep *ProvisionContainerCopyStep) error {
// This is roughly equivalent to
// docker run --rm --network=host -e TARGETS=$vmIPs -e SSH_PRIVATE_KEY="$sshPrivateKey" $dockerImageName

Expand Down Expand Up @@ -153,8 +153,8 @@ func streamLogs(ctx context.Context, containerProvider containerapi.ContainerPro
var wg sync.WaitGroup
wg.Add(2)

go logLines(&wg, "Docker", false, stdout)
go logLines(&wg, "Docker", true, stderr)
go logLines(&wg, "Container", false, stdout)
go logLines(&wg, "Container", true, stderr)

wg.Wait()
return nil
Expand Down Expand Up @@ -203,7 +203,7 @@ func containerWait(statusCh <-chan int64, errCh <-chan error) error {
}
}

func containerCopy(ctx context.Context, provider containerapi.ContainerProvider, containerID string, step *ProvisionDockerCopyStep) error {
func containerCopy(ctx context.Context, provider containerapi.ContainerProvider, containerID string, step *ProvisionContainerCopyStep) error {
destDir, err := filepath.Abs(step.Dest)
if err != nil {
return fmt.Errorf("failed to determine absolute path of destination %q: %w", step.Dest, err)
Expand Down
File renamed without changes.
12 changes: 6 additions & 6 deletions internal/virter/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,15 +558,15 @@ func (v *Virter) imageBuildProvisionCommit(ctx context.Context, tools ImageBuild
}

for _, s := range buildConfig.ProvisionConfig.Steps {
if s.Docker != nil {
if s.Container != nil {
containerCfg := containerapi.NewContainerConfig(
buildConfig.ContainerName,
s.Docker.Image,
s.Docker.Env,
containerapi.WithCommand(s.Docker.Command...),
containerapi.WithPullConfig(s.Docker.Pull.ForContainer()),
s.Container.Image,
s.Container.Env,
containerapi.WithCommand(s.Container.Command...),
containerapi.WithPullConfig(s.Container.Pull.ForContainer()),
)
err = v.VMExecDocker(ctx, tools.ContainerProvider, vmNames, containerCfg, nil)
err = v.VMExecContainer(ctx, tools.ContainerProvider, vmNames, containerCfg, nil)
} else if s.Shell != nil {
err = v.VMExecShell(ctx, vmNames, s.Shell)
} else if s.Rsync != nil {
Expand Down
56 changes: 28 additions & 28 deletions internal/virter/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ import (
"github.com/LINBIT/virter/pkg/pullpolicy"
)

const CurrentProvisionFileVersion = 0
const CurrentProvisionFileVersion = 1

// ProvisionDockerStep is a single provisioniong step executed in a docker container
type ProvisionDockerStep struct {
Image string `toml:"image"`
Pull pullpolicy.PullPolicy `toml:"pull"`
Env map[string]string `toml:"env"`
Command []string `toml:"command"`
Copy *ProvisionDockerCopyStep `toml:"copy"`
// ProvisionContainerStep is a single provisioning step executed in a container
type ProvisionContainerStep struct {
Image string `toml:"image"`
Pull pullpolicy.PullPolicy `toml:"pull"`
Env map[string]string `toml:"env"`
Command []string `toml:"command"`
Copy *ProvisionContainerCopyStep `toml:"copy"`
}

type ProvisionDockerCopyStep struct {
type ProvisionContainerCopyStep struct {
Source string `toml:"source"`
Dest string `toml:"dest"`
}
Expand All @@ -42,11 +42,11 @@ type ProvisionRsyncStep struct {
Dest string `toml:"dest"`
}

// ProvisionStep is a single provisioniong step
// ProvisionStep is a single provisioning step
type ProvisionStep struct {
Docker *ProvisionDockerStep `toml:"docker,omitempty"`
Shell *ProvisionShellStep `toml:"shell,omitempty"`
Rsync *ProvisionRsyncStep `toml:"rsync,omitempty"`
Container *ProvisionContainerStep `toml:"container,omitempty"`
Shell *ProvisionShellStep `toml:"shell,omitempty"`
Rsync *ProvisionRsyncStep `toml:"rsync,omitempty"`
}

// ProvisionConfig holds the configuration of the whole provisioning
Expand All @@ -57,10 +57,10 @@ type ProvisionConfig struct {
Steps []ProvisionStep `toml:"steps"`
}

// NeedsContainers checks if there is a provision step that requires a docker client
// NeedsContainers checks if there is a provision step that requires a container provider (like Docker or Podman)
func (p *ProvisionConfig) NeedsContainers() bool {
for _, s := range p.Steps {
if s.Docker != nil {
if s.Container != nil {
return true
}
}
Expand Down Expand Up @@ -140,33 +140,33 @@ func newProvisionConfigReader(provReader io.Reader, provOpt ProvisionOption) (Pr
}

for i, s := range pc.Steps {
if s.Docker != nil {
s.Docker.Env = mergeEnv(&pc.Env, &s.Docker.Env)
if s.Container != nil {
s.Container.Env = mergeEnv(&pc.Env, &s.Container.Env)

if s.Docker.Image, err = executeTemplate(s.Docker.Image, pc.Values); err != nil {
return pc, fmt.Errorf("failed to execute template for docker.image for step %d: %w", i, err)
if s.Container.Image, err = executeTemplate(s.Container.Image, pc.Values); err != nil {
return pc, fmt.Errorf("failed to execute template for container.image for step %d: %w", i, err)
}

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 := executeTemplateMap(s.Container.Env, pc.Values); err != nil {
return pc, fmt.Errorf("failed to execute template for container.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 err := executeTemplateArray(s.Container.Command, pc.Values); err != nil {
return pc, fmt.Errorf("failed to execute tempalte for container.command for step %d: %w", i, err)
}

if copyStep := s.Docker.Copy; copyStep != nil {
if copyStep := s.Container.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)
return pc, fmt.Errorf("failed to execute template for container.copy.dest for step %d: %w", i, err)
}
}

if s.Docker.Pull == "" {
s.Docker.Pull = provOpt.DefaultPullPolicy
if s.Container.Pull == "" {
s.Container.Pull = provOpt.DefaultPullPolicy
}

if provOpt.OverridePullPolicy != "" {
s.Docker.Pull = provOpt.OverridePullPolicy
s.Container.Pull = provOpt.OverridePullPolicy
}
} else if s.Shell != nil {
s.Shell.Env = mergeEnv(&pc.Env, &s.Shell.Env)
Expand Down
24 changes: 12 additions & 12 deletions internal/virter/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,10 @@ func envEqual(env, expected []string) bool {
func TestNewProvisionConfigTemplate(t *testing.T) {
noTemplate := `
[[steps]]
[steps.docker]
[steps.container]
image = "some-image"
command = ["exit", "0"]
[steps.docker.env]
[steps.container.env]
foo = "bar"
[[steps]]
Expand All @@ -145,12 +145,12 @@ dest = "some-dest"
ShellEnv = "default-value"
[[steps]]
[steps.docker]
image = "{{.DockerImage}}"
[steps.container]
image = "{{.ContainerImage}}"
pull = "Always"
command = ["echo", "{{.DockerCommandArg}}"]
[steps.docker.env]
foo = "hello {{.DockerEnv}}"
command = ["echo", "{{.ContainerCommandArg}}"]
[steps.container.env]
foo = "hello {{.ContainerEnv}}"
[[steps]]
[steps.shell]
Expand Down Expand Up @@ -194,7 +194,7 @@ foo = "{{.ShellEnv"
{
"no-template", noTemplate, true, ProvisionOption{}, []ProvisionStep{
ProvisionStep{
Docker: &ProvisionDockerStep{
Container: &ProvisionContainerStep{
Image: "some-image",
Command: []string{"exit", "0"},
Env: map[string]string{"foo": "bar"},
Expand All @@ -219,15 +219,15 @@ foo = "{{.ShellEnv"
"all-template", allTemplate, true,
ProvisionOption{
Overrides: []string{
"values.DockerImage=template-image",
"values.DockerEnv=template-value",
"values.DockerCommandArg=template-arg",
"values.ContainerImage=template-image",
"values.ContainerEnv=template-value",
"values.ContainerCommandArg=template-arg",
"values.RsyncSource=template-source",
},
},
[]ProvisionStep{
ProvisionStep{
Docker: &ProvisionDockerStep{
Container: &ProvisionContainerStep{
Image: "template-image",
Command: []string{"echo", "template-arg"},
Env: map[string]string{"foo": "hello template-value"},
Expand Down
12 changes: 6 additions & 6 deletions internal/virter/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (v *Virter) VMRun(vmConfig VMConfig) error {
}

meta := &VMMeta{
HostKey: hostkey.PublicKey(),
HostKey: hostkey.PublicKey(),
SSHUserName: vmConfig.SSHUserName,
}

Expand Down Expand Up @@ -527,7 +527,7 @@ func (v *Virter) VMGetKnownHosts(vmName string) (string, error) {
func (v *Virter) getSSHUserName(vmName string) string {
meta, err := v.getMetaForVM(vmName)

/* VM created with an older virter? */
/* VM created with an older virter? */
if err != nil || meta.SSHUserName == "" {
return "root"
}
Expand All @@ -545,8 +545,9 @@ func (v *Virter) getSSHUserNames(vmNames []string) []string {
return vmSSHUserNames
}

// VMExecDocker runs a docker container against some VMs.
func (v *Virter) VMExecDocker(ctx context.Context, containerProvider containerapi.ContainerProvider, vmNames []string, containerCfg *containerapi.ContainerConfig, copyStep *ProvisionDockerCopyStep) error {
// VMExecContainer runs a container against some VMs.
func (v *Virter) VMExecContainer(ctx context.Context, containerProvider containerapi.ContainerProvider,
vmNames []string, containerCfg *containerapi.ContainerConfig, copyStep *ProvisionContainerCopyStep) error {
ips, err := v.getIPs(vmNames)
if err != nil {
return err
Expand Down Expand Up @@ -647,12 +648,11 @@ func (v *Virter) VMExecShell(ctx context.Context, vmNames []string, shellStep *P
remoteUser := v.getSSHUserName(vmName)
sshConfig := ssh.ClientConfig{
Auth: v.sshkeys.Auth(),
User: remoteUser,
User: remoteUser,
HostKeyCallback: hostkeyCheck,
HostKeyAlgorithms: supportedAlgos,
}


log.Println("Provisioning via SSH:", shellStep.Script, "in", ip)
g.Go(func() error {
return runSSHCommand(ctx, &sshConfig, vmName, net.JoinHostPort(ip, "22"), shellStep.Script, EnvmapToSlice(shellStep.Env))
Expand Down
Loading

0 comments on commit 898964d

Please sign in to comment.