123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- package daemon // import "github.com/docker/docker/daemon"
- import (
- "fmt"
- "strings"
- "syscall"
- "github.com/docker/docker/errdefs"
- "github.com/pkg/errors"
- "google.golang.org/grpc/status"
- )
- func isNotRunning(err error) bool {
- var nre *containerNotRunningError
- return errors.As(err, &nre)
- }
- func errNotRunning(id string) error {
- return &containerNotRunningError{errors.Errorf("container %s is not running", id)}
- }
- type containerNotRunningError struct {
- error
- }
- func (e containerNotRunningError) Conflict() {}
- func containerNotFound(id string) error {
- return objNotFoundError{"container", id}
- }
- type objNotFoundError struct {
- object string
- id string
- }
- func (e objNotFoundError) Error() string {
- return "No such " + e.object + ": " + e.id
- }
- func (e objNotFoundError) NotFound() {}
- func errContainerIsRestarting(containerID string) error {
- cause := errors.Errorf("Container %s is restarting, wait until the container is running", containerID)
- return errdefs.Conflict(cause)
- }
- func errExecNotFound(id string) error {
- return objNotFoundError{"exec instance", id}
- }
- func errExecPaused(id string) error {
- cause := errors.Errorf("Container %s is paused, unpause the container before exec", id)
- return errdefs.Conflict(cause)
- }
- func errNotPaused(id string) error {
- cause := errors.Errorf("Container %s is already paused", id)
- return errdefs.Conflict(cause)
- }
- type nameConflictError struct {
- id string
- name string
- }
- func (e nameConflictError) Error() string {
- return fmt.Sprintf("Conflict. The container name %q is already in use by container %q. You have to remove (or rename) that container to be able to reuse that name.", e.name, e.id)
- }
- func (nameConflictError) Conflict() {}
- type invalidIdentifier string
- func (e invalidIdentifier) Error() string {
- return fmt.Sprintf("invalid name or ID supplied: %q", string(e))
- }
- func (invalidIdentifier) InvalidParameter() {}
- type incompatibleDeviceRequest struct {
- driver string
- caps [][]string
- }
- func (i incompatibleDeviceRequest) Error() string {
- return fmt.Sprintf("could not select device driver %q with capabilities: %v", i.driver, i.caps)
- }
- func (incompatibleDeviceRequest) InvalidParameter() {}
- type duplicateMountPointError string
- func (e duplicateMountPointError) Error() string {
- return "Duplicate mount point: " + string(e)
- }
- func (duplicateMountPointError) InvalidParameter() {}
- type containerFileNotFound struct {
- file string
- container string
- }
- func (e containerFileNotFound) Error() string {
- return "Could not find the file " + e.file + " in container " + e.container
- }
- func (containerFileNotFound) NotFound() {}
- type startInvalidConfigError string
- func (e startInvalidConfigError) Error() string {
- return string(e)
- }
- func (e startInvalidConfigError) InvalidParameter() {} // Is this right???
- // exitStatus is the exit-code as set by setExitCodeFromError
- type exitStatus = int
- const (
- exitEaccess exitStatus = 126 // container cmd can't be invoked (permission denied)
- exitCmdNotFound exitStatus = 127 // container cmd not found/does not exist or invalid bind-mount
- exitUnknown exitStatus = 128 // unknown error
- )
- // 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).
- func setExitCodeFromError(setExitCode func(exitStatus), err error) error {
- if err == nil {
- return nil
- }
- errDesc := status.Convert(err).Message()
- contains := func(s1, s2 string) bool {
- return strings.Contains(strings.ToLower(s1), s2)
- }
- // set to 126 for container cmd can't be invoked errors
- if contains(errDesc, syscall.EACCES.Error()) {
- setExitCode(exitEaccess)
- return startInvalidConfigError(errDesc)
- }
- // Go 1.20 changed the error for attempting to execute a directory from
- // syscall.EACCESS to syscall.EISDIR. Unfortunately docker/cli checks
- // whether the error message contains syscall.EACCESS.Error() to
- // determine whether to exit with code 126 or 125, so we have little
- // choice but to fudge the error string.
- if contains(errDesc, syscall.EISDIR.Error()) {
- errDesc += ": " + syscall.EACCES.Error()
- setExitCode(exitEaccess)
- return startInvalidConfigError(errDesc)
- }
- // attempted to mount a file onto a directory, or a directory onto a file, maybe from user specified bind mounts
- if contains(errDesc, syscall.ENOTDIR.Error()) {
- 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"
- setExitCode(exitCmdNotFound)
- return startInvalidConfigError(errDesc)
- }
- // if we receive an internal error from the initial start of a container then lets
- // return it instead of entering the restart loop
- // set to 127 for container cmd not found/does not exist.
- if isInvalidCommand(errDesc) {
- setExitCode(exitCmdNotFound)
- return startInvalidConfigError(errDesc)
- }
- // TODO: it would be nice to get some better errors from containerd so we can return better errors here
- setExitCode(exitUnknown)
- return errdefs.Unknown(errors.New(errDesc))
- }
- // isInvalidCommand tries to detect if the reason the container failed to start
- // was due to an invalid command for the container (command not found, or not
- // a valid executable).
- func isInvalidCommand(errMessage string) bool {
- errMessage = strings.ToLower(errMessage)
- errMessages := []string{
- "executable file not found",
- "no such file or directory",
- "system cannot find the file specified",
- "failed to run runc create/exec call",
- }
- for _, msg := range errMessages {
- if strings.Contains(errMessage, msg) {
- return true
- }
- }
- return false
- }
|