github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/api/client/run.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http/httputil"
     7  	"os"
     8  	"runtime"
     9  	"strings"
    10  
    11  	"golang.org/x/net/context"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	Cli "github.com/docker/docker/cli"
    15  	"github.com/docker/docker/opts"
    16  	"github.com/docker/docker/pkg/promise"
    17  	"github.com/docker/docker/pkg/signal"
    18  	runconfigopts "github.com/docker/docker/runconfig/opts"
    19  	"github.com/docker/engine-api/types"
    20  	"github.com/docker/libnetwork/resolvconf/dns"
    21  )
    22  
    23  const (
    24  	errCmdNotFound          = "not found or does not exist"
    25  	errCmdCouldNotBeInvoked = "could not be invoked"
    26  )
    27  
    28  func (cid *cidFile) Close() error {
    29  	cid.file.Close()
    30  
    31  	if !cid.written {
    32  		if err := os.Remove(cid.path); err != nil {
    33  			return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
    34  		}
    35  	}
    36  
    37  	return nil
    38  }
    39  
    40  func (cid *cidFile) Write(id string) error {
    41  	if _, err := cid.file.Write([]byte(id)); err != nil {
    42  		return fmt.Errorf("Failed to write the container ID to the file: %s", err)
    43  	}
    44  	cid.written = true
    45  	return nil
    46  }
    47  
    48  // if container start fails with 'command not found' error, return 127
    49  // if container start fails with 'command cannot be invoked' error, return 126
    50  // return 125 for generic docker daemon failures
    51  func runStartContainerErr(err error) error {
    52  	trimmedErr := strings.TrimPrefix(err.Error(), "Error response from daemon: ")
    53  	statusError := Cli.StatusError{StatusCode: 125}
    54  	if strings.HasPrefix(trimmedErr, "Container command") {
    55  		if strings.Contains(trimmedErr, errCmdNotFound) {
    56  			statusError = Cli.StatusError{StatusCode: 127}
    57  		} else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) {
    58  			statusError = Cli.StatusError{StatusCode: 126}
    59  		}
    60  	}
    61  
    62  	return statusError
    63  }
    64  
    65  // CmdRun runs a command in a new container.
    66  //
    67  // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
    68  func (cli *DockerCli) CmdRun(args ...string) error {
    69  	cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["run"].Description, true)
    70  	addTrustedFlags(cmd, true)
    71  
    72  	// These are flags not stored in Config/HostConfig
    73  	var (
    74  		flAutoRemove = cmd.Bool([]string{"-rm"}, false, "Automatically remove the container when it exits")
    75  		flDetach     = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID")
    76  		flSigProxy   = cmd.Bool([]string{"-sig-proxy"}, true, "Proxy received signals to the process")
    77  		flName       = cmd.String([]string{"-name"}, "", "Assign a name to the container")
    78  		flDetachKeys = cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
    79  		flAttach     *opts.ListOpts
    80  
    81  		ErrConflictAttachDetach               = fmt.Errorf("Conflicting options: -a and -d")
    82  		ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
    83  		ErrConflictDetachAutoRemove           = fmt.Errorf("Conflicting options: --rm and -d")
    84  	)
    85  
    86  	config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
    87  
    88  	// just in case the Parse does not exit
    89  	if err != nil {
    90  		cmd.ReportError(err.Error(), true)
    91  		os.Exit(125)
    92  	}
    93  
    94  	if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 {
    95  		fmt.Fprintf(cli.err, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n")
    96  	}
    97  
    98  	if len(hostConfig.DNS) > 0 {
    99  		// check the DNS settings passed via --dns against
   100  		// localhost regexp to warn if they are trying to
   101  		// set a DNS to a localhost address
   102  		for _, dnsIP := range hostConfig.DNS {
   103  			if dns.IsLocalhost(dnsIP) {
   104  				fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP)
   105  				break
   106  			}
   107  		}
   108  	}
   109  	if config.Image == "" {
   110  		cmd.Usage()
   111  		return nil
   112  	}
   113  
   114  	config.ArgsEscaped = false
   115  
   116  	if !*flDetach {
   117  		if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil {
   118  			return err
   119  		}
   120  	} else {
   121  		if fl := cmd.Lookup("-attach"); fl != nil {
   122  			flAttach = fl.Value.(*opts.ListOpts)
   123  			if flAttach.Len() != 0 {
   124  				return ErrConflictAttachDetach
   125  			}
   126  		}
   127  		if *flAutoRemove {
   128  			return ErrConflictDetachAutoRemove
   129  		}
   130  
   131  		config.AttachStdin = false
   132  		config.AttachStdout = false
   133  		config.AttachStderr = false
   134  		config.StdinOnce = false
   135  	}
   136  
   137  	// Disable flSigProxy when in TTY mode
   138  	sigProxy := *flSigProxy
   139  	if config.Tty {
   140  		sigProxy = false
   141  	}
   142  
   143  	// Telling the Windows daemon the initial size of the tty during start makes
   144  	// a far better user experience rather than relying on subsequent resizes
   145  	// to cause things to catch up.
   146  	if runtime.GOOS == "windows" {
   147  		hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize()
   148  	}
   149  
   150  	createResponse, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
   151  	if err != nil {
   152  		cmd.ReportError(err.Error(), true)
   153  		return runStartContainerErr(err)
   154  	}
   155  	if sigProxy {
   156  		sigc := cli.forwardAllSignals(createResponse.ID)
   157  		defer signal.StopCatch(sigc)
   158  	}
   159  	var (
   160  		waitDisplayID chan struct{}
   161  		errCh         chan error
   162  		cancelFun     context.CancelFunc
   163  		ctx           context.Context
   164  	)
   165  	if !config.AttachStdout && !config.AttachStderr {
   166  		// Make this asynchronous to allow the client to write to stdin before having to read the ID
   167  		waitDisplayID = make(chan struct{})
   168  		go func() {
   169  			defer close(waitDisplayID)
   170  			fmt.Fprintf(cli.out, "%s\n", createResponse.ID)
   171  		}()
   172  	}
   173  	if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) {
   174  		return ErrConflictRestartPolicyAndAutoRemove
   175  	}
   176  	attach := config.AttachStdin || config.AttachStdout || config.AttachStderr
   177  	if attach {
   178  		var (
   179  			out, stderr io.Writer
   180  			in          io.ReadCloser
   181  		)
   182  		if config.AttachStdin {
   183  			in = cli.in
   184  		}
   185  		if config.AttachStdout {
   186  			out = cli.out
   187  		}
   188  		if config.AttachStderr {
   189  			if config.Tty {
   190  				stderr = cli.out
   191  			} else {
   192  				stderr = cli.err
   193  			}
   194  		}
   195  
   196  		if *flDetachKeys != "" {
   197  			cli.configFile.DetachKeys = *flDetachKeys
   198  		}
   199  
   200  		options := types.ContainerAttachOptions{
   201  			ContainerID: createResponse.ID,
   202  			Stream:      true,
   203  			Stdin:       config.AttachStdin,
   204  			Stdout:      config.AttachStdout,
   205  			Stderr:      config.AttachStderr,
   206  			DetachKeys:  cli.configFile.DetachKeys,
   207  		}
   208  
   209  		resp, errAttach := cli.client.ContainerAttach(context.Background(), options)
   210  		if errAttach != nil && errAttach != httputil.ErrPersistEOF {
   211  			// ContainerAttach returns an ErrPersistEOF (connection closed)
   212  			// means server met an error and put it in Hijacked connection
   213  			// keep the error and read detailed error message from hijacked connection later
   214  			return errAttach
   215  		}
   216  		ctx, cancelFun = context.WithCancel(context.Background())
   217  		errCh = promise.Go(func() error {
   218  			errHijack := cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp)
   219  			if errHijack == nil {
   220  				return errAttach
   221  			}
   222  			return errHijack
   223  		})
   224  	}
   225  
   226  	if *flAutoRemove {
   227  		defer func() {
   228  			if err := cli.removeContainer(createResponse.ID, true, false, true); err != nil {
   229  				fmt.Fprintf(cli.err, "%v\n", err)
   230  			}
   231  		}()
   232  	}
   233  
   234  	//start the container
   235  	if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil {
   236  		// If we have holdHijackedConnection, we should notify
   237  		// holdHijackedConnection we are going to exit and wait
   238  		// to avoid the terminal are not restored.
   239  		if attach {
   240  			cancelFun()
   241  			<-errCh
   242  		}
   243  
   244  		cmd.ReportError(err.Error(), false)
   245  		return runStartContainerErr(err)
   246  	}
   247  
   248  	if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
   249  		if err := cli.monitorTtySize(createResponse.ID, false); err != nil {
   250  			fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
   251  		}
   252  	}
   253  
   254  	if errCh != nil {
   255  		if err := <-errCh; err != nil {
   256  			logrus.Debugf("Error hijack: %s", err)
   257  			return err
   258  		}
   259  	}
   260  
   261  	// Detached mode: wait for the id to be displayed and return.
   262  	if !config.AttachStdout && !config.AttachStderr {
   263  		// Detached mode
   264  		<-waitDisplayID
   265  		return nil
   266  	}
   267  
   268  	var status int
   269  
   270  	// Attached mode
   271  	if *flAutoRemove {
   272  		// Autoremove: wait for the container to finish, retrieve
   273  		// the exit code and remove the container
   274  		if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
   275  			return runStartContainerErr(err)
   276  		}
   277  		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
   278  			return err
   279  		}
   280  	} else {
   281  		// No Autoremove: Simply retrieve the exit code
   282  		if !config.Tty {
   283  			// In non-TTY mode, we can't detach, so we must wait for container exit
   284  			if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
   285  				return err
   286  			}
   287  		} else {
   288  			// In TTY mode, there is a race: if the process dies too slowly, the state could
   289  			// be updated after the getExitCode call and result in the wrong exit code being reported
   290  			if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
   291  				return err
   292  			}
   293  		}
   294  	}
   295  	if status != 0 {
   296  		return Cli.StatusError{StatusCode: status}
   297  	}
   298  	return nil
   299  }