Skip to content

Commit bbe9075

Browse files
committed
virter: pass-through exit code from provisioning
When a provisioning step fails, it may be helpful to the caller of virter to know the exit code of the script. So, if an SSH or container provisioning step fails with an exit code, pass it through to the CLI, exiting from the virter process with the same exit code.
1 parent 697988e commit bbe9075

File tree

4 files changed

+36
-3
lines changed

4 files changed

+36
-3
lines changed

cmd/image_build.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func imageBuildCommand() *cobra.Command {
223223

224224
err = v.ImageBuild(ctx, tools, vmConfig, getReadyConfig(), buildConfig, virter.WithProgress(DefaultProgressFormat(p)))
225225
if err != nil {
226-
log.Fatalf("Failed to build image: %v", err)
226+
logProvisioningErrorAndExit(err)
227227
}
228228

229229
if push {

cmd/vm_exec.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package cmd
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"os"
68
"strings"
79

10+
"golang.org/x/crypto/ssh"
11+
812
log "github.com/sirupsen/logrus"
913

1014
"github.com/LINBIT/containerapi"
@@ -16,6 +20,23 @@ import (
1620
"github.com/spf13/cobra"
1721
)
1822

23+
// logProvisioningErrorAndExit logs an error from a virter.VMExec* function and exits with the appropriate exit code.
24+
// If the error is from a failed SSH or container provisioning step, the exit code is the exit code
25+
// of the respective command.
26+
// Otherwise, the exit code is 1.
27+
func logProvisioningErrorAndExit(err error) {
28+
log.Errorf("Failed to build image: %v", err)
29+
var sshErr *ssh.ExitError
30+
if errors.As(err, &sshErr) {
31+
os.Exit(sshErr.ExitStatus())
32+
}
33+
var containerErr *virter.ContainerExitError
34+
if errors.As(err, &containerErr) {
35+
os.Exit(containerErr.Status)
36+
}
37+
os.Exit(1)
38+
}
39+
1940
func vmExecCommand() *cobra.Command {
2041
var provisionFile string
2142
var provisionOverrides []string
@@ -35,7 +56,7 @@ func vmExecCommand() *cobra.Command {
3556
OverridePullPolicy: containerPullPolicy,
3657
}
3758
if err := execProvision(cmd.Context(), provOpt, args); err != nil {
38-
log.Fatal(err)
59+
logProvisioningErrorAndExit(err)
3960
}
4061
},
4162
ValidArgsFunction: suggestVmNames,

doc/provisioning.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ It can also be applied to one or multiple already running VMs:
1616
$ virter vm exec -p provisioning.toml centos-1 centos-2 centos-3
1717
```
1818

19+
If a container or shell provisioning step fails, the virter process will exit with the same exit code as the provisioning script.
20+
1921
## Provisioning types
2022

2123
The following provisioning types are supported.

internal/virter/container.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const (
2626
colorReset = "\u001b[0m"
2727
)
2828

29+
type ContainerExitError struct {
30+
Status int
31+
}
32+
33+
func (e *ContainerExitError) Error() string {
34+
return fmt.Sprintf("container exited with status %d", e.Status)
35+
}
36+
2937
func containerRun(ctx context.Context, containerProvider containerapi.ContainerProvider, containerCfg *containerapi.ContainerConfig, vmNames []string, vmSSHUserNames []string, vmIPs []string, keyStore sshkeys.KeyStore, knownHosts sshkeys.KnownHosts, copyStep *ProvisionContainerCopyStep) error {
3038
// This is roughly equivalent to
3139
// docker run --rm --network=host -e TARGETS=$vmIPs -e SSH_PRIVATE_KEY="$sshPrivateKey" $dockerImageName
@@ -192,13 +200,15 @@ func logLines(wg *sync.WaitGroup, vm string, stderr bool, r io.Reader) {
192200
}
193201
}
194202

203+
// containerWait waits for a container to exit.
204+
// If the container exits with a non-zero exit code, a ContainerExitError is returned.
195205
func containerWait(statusCh <-chan int64, errCh <-chan error) error {
196206
select {
197207
case err := <-errCh:
198208
return fmt.Errorf("error waiting for container: %w", err)
199209
case status := <-statusCh:
200210
if status != 0 {
201-
return fmt.Errorf("container returned non-zero exit code %d", status)
211+
return &ContainerExitError{Status: int(status)}
202212
}
203213
return nil
204214
}

0 commit comments

Comments
 (0)