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