github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/containerupdate/error.go (about) 1 package containerupdate 2 3 import ( 4 "errors" 5 "fmt" 6 7 "k8s.io/client-go/util/exec" 8 9 "github.com/tilt-dev/tilt/internal/build" 10 "github.com/tilt-dev/tilt/internal/docker" 11 "github.com/tilt-dev/tilt/pkg/model" 12 ) 13 14 const ( 15 // GenericExitCodeKilled indicates the container runtime killed the process. 16 GenericExitCodeKilled = 137 17 18 // GenericExitCodeCannotExec indicates the command cannot be executed. 19 // In a shell, this generally is a form of permission issues (i.e. the 20 // binary was found but is not +x). However, container runtimes also 21 // use this to indicate that the binary wasn't found at all, which is 22 // extremely common when we try to use common tools such as `tar` but 23 // the image is missing them. 24 GenericExitCodeCannotExec = 126 25 26 GenericExitCodeNotFound = 127 27 ) 28 29 // ExtractExitCode returns exit status information from different types of 30 // errors. 31 func ExtractExitCode(err error) (int, bool) { 32 var k8sExitErr exec.ExitError 33 if errors.As(err, &k8sExitErr) { 34 return k8sExitErr.ExitStatus(), true 35 } 36 37 var dockerExitErr docker.ExitError 38 if errors.As(err, &dockerExitErr) { 39 return dockerExitErr.ExitCode, true 40 } 41 42 return -1, false 43 } 44 45 type ExecError struct { 46 Cmd model.Cmd 47 ExitCode int 48 } 49 50 func NewExecError(cmd model.Cmd, exitCode int) ExecError { 51 return ExecError{Cmd: cmd, ExitCode: exitCode} 52 } 53 54 func (e ExecError) Error() string { 55 var reason string 56 switch e.ExitCode { 57 case GenericExitCodeKilled: 58 reason = "killed by container runtime" 59 case GenericExitCodeCannotExec: 60 // docker uses this when it can't find the command for an exec, so we 61 // have a single error message that covers both cases 62 fallthrough 63 case GenericExitCodeNotFound: 64 reason = "not found in PATH or not executable" 65 } 66 67 if reason != "" { 68 reason = " (" + reason + ")" 69 } 70 71 return fmt.Sprintf("command %q failed with exit code: %d%s", e.Cmd.String(), e.ExitCode, reason) 72 } 73 74 func wrapRunStepError(err error) error { 75 var execErr ExecError 76 if errors.As(err, &execErr) { 77 if execErr.ExitCode == GenericExitCodeKilled { 78 // a SIGKILL of a run() command is not treated as a step failure 79 // but as a more generic "infra" error, so it's not wrapped 80 return err 81 } 82 return build.NewRunStepFailure(err) 83 } 84 return err 85 }