github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/daemon/errors.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 4 "fmt" 5 "strings" 6 "syscall" 7 8 "github.com/docker/docker/errdefs" 9 "github.com/pkg/errors" 10 "google.golang.org/grpc/status" 11 ) 12 13 func isNotRunning(err error) bool { 14 var nre *containerNotRunningError 15 return errors.As(err, &nre) 16 } 17 18 func errNotRunning(id string) error { 19 return &containerNotRunningError{errors.Errorf("container %s is not running", id)} 20 } 21 22 type containerNotRunningError struct { 23 error 24 } 25 26 func (e containerNotRunningError) Conflict() {} 27 28 func containerNotFound(id string) error { 29 return objNotFoundError{"container", id} 30 } 31 32 type objNotFoundError struct { 33 object string 34 id string 35 } 36 37 func (e objNotFoundError) Error() string { 38 return "No such " + e.object + ": " + e.id 39 } 40 41 func (e objNotFoundError) NotFound() {} 42 43 func errContainerIsRestarting(containerID string) error { 44 cause := errors.Errorf("Container %s is restarting, wait until the container is running", containerID) 45 return errdefs.Conflict(cause) 46 } 47 48 func errExecNotFound(id string) error { 49 return objNotFoundError{"exec instance", id} 50 } 51 52 func errExecPaused(id string) error { 53 cause := errors.Errorf("Container %s is paused, unpause the container before exec", id) 54 return errdefs.Conflict(cause) 55 } 56 57 func errNotPaused(id string) error { 58 cause := errors.Errorf("Container %s is already paused", id) 59 return errdefs.Conflict(cause) 60 } 61 62 type nameConflictError struct { 63 id string 64 name string 65 } 66 67 func (e nameConflictError) Error() string { 68 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) 69 } 70 71 func (nameConflictError) Conflict() {} 72 73 type invalidIdentifier string 74 75 func (e invalidIdentifier) Error() string { 76 return fmt.Sprintf("invalid name or ID supplied: %q", string(e)) 77 } 78 79 func (invalidIdentifier) InvalidParameter() {} 80 81 type incompatibleDeviceRequest struct { 82 driver string 83 caps [][]string 84 } 85 86 func (i incompatibleDeviceRequest) Error() string { 87 return fmt.Sprintf("could not select device driver %q with capabilities: %v", i.driver, i.caps) 88 } 89 90 func (incompatibleDeviceRequest) InvalidParameter() {} 91 92 type duplicateMountPointError string 93 94 func (e duplicateMountPointError) Error() string { 95 return "Duplicate mount point: " + string(e) 96 } 97 func (duplicateMountPointError) InvalidParameter() {} 98 99 type containerFileNotFound struct { 100 file string 101 container string 102 } 103 104 func (e containerFileNotFound) Error() string { 105 return "Could not find the file " + e.file + " in container " + e.container 106 } 107 108 func (containerFileNotFound) NotFound() {} 109 110 type startInvalidConfigError string 111 112 func (e startInvalidConfigError) Error() string { 113 return string(e) 114 } 115 116 func (e startInvalidConfigError) InvalidParameter() {} // Is this right??? 117 118 // exitStatus is the exit-code as set by setExitCodeFromError 119 type exitStatus = int 120 121 const ( 122 exitEaccess exitStatus = 126 // container cmd can't be invoked (permission denied) 123 exitCmdNotFound exitStatus = 127 // container cmd not found/does not exist or invalid bind-mount 124 exitUnknown exitStatus = 128 // unknown error 125 ) 126 127 // setExitCodeFromError converts the error returned by containerd 128 // when starting a container, and applies the corresponding exitStatus to the 129 // container. It returns an errdefs error (either errdefs.ErrInvalidParameter 130 // or errdefs.ErrUnknown). 131 func setExitCodeFromError(setExitCode func(exitStatus), err error) error { 132 if err == nil { 133 return nil 134 } 135 errDesc := status.Convert(err).Message() 136 contains := func(s1, s2 string) bool { 137 return strings.Contains(strings.ToLower(s1), s2) 138 } 139 140 // set to 126 for container cmd can't be invoked errors 141 if contains(errDesc, syscall.EACCES.Error()) { 142 setExitCode(exitEaccess) 143 return startInvalidConfigError(errDesc) 144 } 145 146 // Go 1.20 changed the error for attempting to execute a directory from 147 // syscall.EACCESS to syscall.EISDIR. Unfortunately docker/cli checks 148 // whether the error message contains syscall.EACCESS.Error() to 149 // determine whether to exit with code 126 or 125, so we have little 150 // choice but to fudge the error string. 151 if contains(errDesc, syscall.EISDIR.Error()) { 152 errDesc += ": " + syscall.EACCES.Error() 153 setExitCode(exitEaccess) 154 return startInvalidConfigError(errDesc) 155 } 156 157 // attempted to mount a file onto a directory, or a directory onto a file, maybe from user specified bind mounts 158 if contains(errDesc, syscall.ENOTDIR.Error()) { 159 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" 160 setExitCode(exitCmdNotFound) 161 return startInvalidConfigError(errDesc) 162 } 163 164 // if we receive an internal error from the initial start of a container then lets 165 // return it instead of entering the restart loop 166 // set to 127 for container cmd not found/does not exist. 167 if isInvalidCommand(errDesc) { 168 setExitCode(exitCmdNotFound) 169 return startInvalidConfigError(errDesc) 170 } 171 172 // TODO: it would be nice to get some better errors from containerd so we can return better errors here 173 setExitCode(exitUnknown) 174 return errdefs.Unknown(errors.New(errDesc)) 175 } 176 177 // isInvalidCommand tries to detect if the reason the container failed to start 178 // was due to an invalid command for the container (command not found, or not 179 // a valid executable). 180 func isInvalidCommand(errMessage string) bool { 181 errMessage = strings.ToLower(errMessage) 182 errMessages := []string{ 183 "executable file not found", 184 "no such file or directory", 185 "system cannot find the file specified", 186 "failed to run runc create/exec call", 187 } 188 189 for _, msg := range errMessages { 190 if strings.Contains(errMessage, msg) { 191 return true 192 } 193 } 194 return false 195 }