setExitCodeFromError converts the error returned by containerd when starting a container, and applies the corresponding exitStatus to the container. It returns an errdefs error (either errdefs.ErrInvalidParameter or errdefs.ErrUnknown).
(setExitCode func(exitStatus), err error)
| 124 | // container. It returns an errdefs error (either errdefs.ErrInvalidParameter |
| 125 | // or errdefs.ErrUnknown). |
| 126 | func setExitCodeFromError(setExitCode func(exitStatus), err error) error { |
| 127 | if err == nil { |
| 128 | return nil |
| 129 | } |
| 130 | errDesc := status.Convert(err).Message() |
| 131 | contains := func(s1, s2 string) bool { |
| 132 | return strings.Contains(strings.ToLower(s1), s2) |
| 133 | } |
| 134 | |
| 135 | // set to 126 for container cmd can't be invoked errors |
| 136 | if contains(errDesc, syscall.EACCES.Error()) { |
| 137 | setExitCode(exitEaccess) |
| 138 | return startInvalidConfigError(errDesc) |
| 139 | } |
| 140 | |
| 141 | // Go 1.20 changed the error for attempting to execute a directory from |
| 142 | // syscall.EACCESS to syscall.EISDIR. Unfortunately docker/cli checks |
| 143 | // whether the error message contains syscall.EACCESS.Error() to |
| 144 | // determine whether to exit with code 126 or 125, so we have little |
| 145 | // choice but to fudge the error string. |
| 146 | if contains(errDesc, syscall.EISDIR.Error()) { |
| 147 | errDesc += ": " + syscall.EACCES.Error() |
| 148 | setExitCode(exitEaccess) |
| 149 | return startInvalidConfigError(errDesc) |
| 150 | } |
| 151 | |
| 152 | // attempted to mount a file onto a directory, or a directory onto a file, maybe from user-specified bind mounts |
| 153 | if contains(errDesc, syscall.ENOTDIR.Error()) { |
| 154 | errDesc += ": Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type" |
| 155 | setExitCode(exitCmdNotFound) |
| 156 | return startInvalidConfigError(errDesc) |
| 157 | } |
| 158 | |
| 159 | // if we receive an internal error from the initial start of a container then lets |
| 160 | // return it instead of entering the restart loop |
| 161 | // set to 127 for container cmd not found/does not exist. |
| 162 | if isInvalidCommand(errDesc) { |
| 163 | setExitCode(exitCmdNotFound) |
| 164 | return startInvalidConfigError(errDesc) |
| 165 | } |
| 166 | |
| 167 | // TODO: it would be nice to get some better errors from containerd so we can return better errors here |
| 168 | setExitCode(exitUnknown) |
| 169 | return errdefs.Unknown(errors.New(errDesc)) |
| 170 | } |
| 171 | |
| 172 | // isInvalidCommand tries to detect if the reason the container failed to start |
| 173 | // was due to an invalid command for the container (command not found, or not |
no test coverage detected
searching dependent graphs…