Skip to content

Commit

Permalink
Make exit code reflect internal shell exit code
Browse files Browse the repository at this point in the history
  • Loading branch information
Otterverse committed Dec 11, 2024
1 parent 43800b1 commit 215f9c8
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
bin/canon: *.go go.mod go.sum canon_setup.sh
go build -tags osusergo,netgo -ldflags "-s -w" -o bin/canon .
go build -trimpath -tags osusergo,netgo -ldflags "-s -w" -o bin/canon .

bin/golangci-lint:
GOBIN=`pwd`/bin go install github.com/golangci/golangci-lint/cmd/[email protected]
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Simply run `canon` with no arguments to get a shell inside the default canon env
Alternately, you can directly specify a command to be run.
Ex: `canon make tests`

### Exit Codes

The exit code of canon (as of version 1.2.0) will normally reflect the exit code of the internal shell or command that was run.
However, if an internal or docker error is encountered, the exit code will be 66.

### Arguments

Run `canon -help` for a brief listing of arguments you can set via CLI.
Expand Down
4 changes: 2 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,11 +416,11 @@ func swapArchImage(profile *Profile) {

func showConfig(profile *Profile) {
ret, err := yaml.Marshal(mergedCfg)
checkErr(err)
printIfErr(err)
fmt.Printf("# All explicitly parsed/merged config files (without builtin/default/cli)\n---\n%s\n\n", ret)

ret, err = yaml.Marshal(map[string]Profile{profile.name: *profile})
checkErr(err)
printIfErr(err)
fmt.Printf("# Active, merged profile (including builtin/user defaults and cli arguments)\n---\n%s\n", ret)
}

Expand Down
2 changes: 1 addition & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func startContainer(ctx context.Context, cli *client.Client, profile *Profile, s
go func() {
_, err := stdcopy.StdCopy(pipeW, pipeW, hijack.Reader)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
checkErr(err)
printIfErr(err)
}
}()

Expand Down
52 changes: 40 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,79 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
)

var defaultArgs = []string{"bash", "-l"}

func main() {
exitCode := 0
defer func() { os.Exit(exitCode) }()

err := parseConfigs()
if err != nil {
checkErr(err)
printIfErr(err)
exitCode = ExitCodeOnError
return
}

checkDockerSocket()

args := flag.Args()
if len(args) == 0 {
checkErr(shell(defaultArgs))
exitCode, err = shell(defaultArgs)
printIfErr(err)
} else {
switch args[0] {
case "shell":
checkErr(shell(defaultArgs))
exitCode, err = shell(defaultArgs)
printIfErr(err)
case "config":
showConfig(activeProfile)
case "update":
checkErr(checkUpdate(activeProfile, checkAll(args), true))
err = checkUpdate(activeProfile, checkAll(args), true)
if err != nil {
exitCode = ExitCodeOnError
printIfErr(err)
}
case "list":
checkErr(list(context.Background()))
err = list(context.Background())
if err != nil {
exitCode = ExitCodeOnError
printIfErr(err)
}
case "stop":
checkErr(stop(context.Background(), activeProfile, checkAll(args), false))
err = stop(context.Background(), activeProfile, checkAll(args), false)
if err != nil {
exitCode = ExitCodeOnError
printIfErr(err)
}
case "terminate":
checkErr(stop(context.Background(), activeProfile, checkAll(args), true))
err = stop(context.Background(), activeProfile, checkAll(args), true)
if err != nil {
exitCode = ExitCodeOnError
printIfErr(err)
}
case "--":
fallthrough
case "run":
checkErr(shell(args[1:]))
exitCode, err = shell(args[1:])
printIfErr(err)
default:
checkErr(shell(args))
exitCode, err = shell(args)
printIfErr(err)
}
}
}

func checkErr(err error) {
func printIfErr(err error) {
if err == nil {
return
}
_, err2 := fmt.Fprintf(os.Stderr, "Error: %s\n", err)

pc, filename, line, _ := runtime.Caller(1)
_, err2 := fmt.Fprintf(os.Stderr, "Error in %s[%s:%d] %v\n", runtime.FuncForPC(pc).Name(), filename, line, err)

if err2 != nil {
fmt.Printf("Error encountered printing to stderr: %s\nOriginal Error: %s", err2, err)
}
Expand All @@ -63,7 +91,7 @@ func checkDockerSocket() {
_, err := os.Stat("/var/run/docker.sock")
if err != nil {
homedir, err := os.UserHomeDir()
checkErr(err)
printIfErr(err)
if err == nil {
hostPath := filepath.Join(homedir, ".docker/run/docker.sock")
_, err = os.Stat(hostPath)
Expand Down
50 changes: 31 additions & 19 deletions shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ import (
"github.com/moby/term"
)

func shell(args []string) error {
const (
ExitCodeOnError = 66
)

func shell(args []string) (int, error) {
if len(args) < 1 {
return errors.New("shell needs at least one argument to run")
return ExitCodeOnError, errors.New("shell needs at least one argument to run")
}
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return err
return ExitCodeOnError, err
}

var sshSock string
Expand All @@ -38,20 +42,20 @@ func shell(args []string) error {
}
}

checkErr(checkUpdate(activeProfile, false, false))
printIfErr(checkUpdate(activeProfile, false, false))

var containerID string
if activeProfile.Persistent {
containerID, err = getPersistentContainer(ctx, cli, activeProfile)
if err != nil {
return err
return ExitCodeOnError, err
}
}

if containerID != "" {
needsUpdate, err := checkContainerImageVersion(ctx, cli, containerID)
if err != nil {
return err
return ExitCodeOnError, err
}
if needsUpdate {
fmt.Print(
Expand All @@ -62,13 +66,13 @@ func shell(args []string) error {
} else {
containerID, err = startContainer(ctx, cli, activeProfile, sshSock)
if err != nil {
return err
return ExitCodeOnError, err
}
}

wd, err := getWorkingDir(activeProfile)
if err != nil {
return err
return ExitCodeOnError, err
}

execCfg := container.ExecOptions{
Expand All @@ -86,13 +90,13 @@ func shell(args []string) error {

execResp, err := cli.ContainerExecCreate(ctx, containerID, execCfg)
if err != nil {
return err
return ExitCodeOnError, err
}
execID := execResp.ID

hijack, err := cli.ContainerExecAttach(ctx, execID, container.ExecAttachOptions{Tty: execCfg.Tty})
if err != nil {
return err
return ExitCodeOnError, err
}
defer hijack.Close()

Expand All @@ -102,14 +106,14 @@ func shell(args []string) error {
// for very fast commands, the resize may happen too early or too late
if !strings.Contains(err.Error(), "cannot resize a stopped container") &&
!strings.Contains(err.Error(), "no such exec") {
return err
return ExitCodeOnError, err
}
}
monitorTtySize(ctx, cli, execID)

termState, err := term.SetRawTerminal(os.Stdin.Fd())
if err != nil {
return err
return ExitCodeOnError, err
}
defer func() {
err = errors.Join(err, term.RestoreTerminal(os.Stdin.Fd(), termState))
Expand All @@ -128,33 +132,41 @@ func shell(args []string) error {

err = cli.ContainerExecStart(ctx, execID, container.ExecStartOptions{})
if err != nil {
return err
return ExitCodeOnError, err
}

select {
case err := <-outErr:
if err != nil {
return err
return ExitCodeOnError, err
}
break
case err := <-inErr:
if err != nil {
return err
return ExitCodeOnError, err
}
select {
case err := <-outErr:
if err != nil {
return err
return ExitCodeOnError, err
}
case <-ctx.Done():
return ctx.Err()
return ExitCodeOnError, ctx.Err()
}
}

details, err := cli.ContainerExecInspect(ctx, execID)
if err != nil {
return ExitCodeOnError, err
}

if !activeProfile.Persistent {
return removeContainer(ctx, cli, containerID)
err = removeContainer(ctx, cli, containerID)
if err != nil {
return ExitCodeOnError, err
}
}
return nil
return details.ExitCode, nil
}

func resizeTty(ctx context.Context, cli *client.Client, execID string) error {
Expand Down
2 changes: 1 addition & 1 deletion update.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func update(images ...ImageDef) error {
return err
}
defer func() {
checkErr(dropLock(lock))
printIfErr(dropLock(lock))
}()

checkData, err := readCheckData()
Expand Down

0 comments on commit 215f9c8

Please sign in to comment.