github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/cli/command/container/exec.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"golang.org/x/net/context"
     8  
     9  //	"github.com/docker/docker/api/types/versions"
    10  //	"github.com/docker/docker/cli/command/commands"
    11  //	cliflags "github.com/docker/docker/cli/flags"
    12  //	"github.com/docker/docker/cliconfig"
    13  //	"github.com/docker/docker/dockerversion"
    14  //	"github.com/docker/docker/pkg/term"
    15  //  "github.com/docker/docker/utils"
    16  //	"github.com/spf13/pflag"
    17  
    18  	"github.com/Sirupsen/logrus"
    19  	"github.com/docker/docker/api/types"
    20  	"github.com/docker/docker/cli"
    21  	"github.com/docker/docker/cli/command"
    22  	apiclient "github.com/docker/docker/client"
    23  	options "github.com/docker/docker/opts"
    24  	"github.com/docker/docker/pkg/promise"
    25  	runconfigopts "github.com/docker/docker/runconfig/opts"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  type execOptions struct {
    30  	detachKeys  string
    31  	interactive bool
    32  	tty         bool
    33  	detach      bool
    34  	user        string
    35  	privileged  bool
    36  	env         *options.ListOpts
    37  }
    38  
    39  func newExecOptions() *execOptions {
    40  	var values []string
    41  	return &execOptions{
    42  		env: options.NewListOptsRef(&values, runconfigopts.ValidateEnv),
    43  	}
    44  }
    45  
    46  // NewExecCommand creats a new cobra.Command for `docker exec`
    47  func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command {
    48      fmt.Println("cli/command/container/exec.go  NewExecCommand()")
    49  
    50  	opts := newExecOptions()
    51  
    52  	cmd := &cobra.Command{
    53  		Use:   "exec [OPTIONS] CONTAINER COMMAND [ARG...]",
    54  		Short: "Run a command in a running container",
    55  		Args:  cli.RequiresMinArgs(2),
    56  		RunE: func(cmd *cobra.Command, args []string) error {
    57  			container := args[0]
    58  			execCmd := args[1:]
    59              fmt.Println("cli/command/container/exec.go  NewExecCommand() container : ", container)
    60              fmt.Println("cli/command/container/exec.go  NewExecCommand() execCmd : ", execCmd)
    61              fmt.Println("cli/command/container/exec.go  NewExecCommand() before runExec()")
    62  			return runExec(dockerCli, opts, container, execCmd)
    63  		},
    64  	}
    65      
    66      fmt.Println("cli/command/container/exec.go  NewExecCommand() after runExec()")
    67  
    68  	flags := cmd.Flags()
    69  	flags.SetInterspersed(false)
    70  
    71  	flags.StringVarP(&opts.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container")
    72  	flags.BoolVarP(&opts.interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
    73  	flags.BoolVarP(&opts.tty, "tty", "t", false, "Allocate a pseudo-TTY")
    74  	flags.BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background")
    75  	flags.StringVarP(&opts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
    76  	flags.BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the command")
    77  	flags.VarP(opts.env, "env", "e", "Set environment variables")
    78  	flags.SetAnnotation("env", "version", []string{"1.25"})
    79  
    80  	return cmd
    81  }
    82  
    83  func runExec(dockerCli *command.DockerCli, opts *execOptions, container string, execCmd []string) error {
    84  	fmt.Println("cli/command/container/exec.go  runExec()")
    85      
    86      execConfig, err := parseExec(opts, execCmd)
    87  	// just in case the ParseExec does not exit
    88  	if container == "" || err != nil {
    89  		return cli.StatusError{StatusCode: 1}
    90  	}
    91  
    92  	if opts.detachKeys != "" {
    93  		fmt.Println("cli/command/container/exec.go  runExec() opts.detachKeys!=null")
    94          dockerCli.ConfigFile().DetachKeys = opts.detachKeys
    95  	}
    96  
    97  	// Send client escape keys
    98  	execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys
    99  
   100  	ctx := context.Background()
   101  	client := dockerCli.Client()
   102  	fmt.Println("cli/command/container/exec.go  runExec() Client : ", client)
   103  	fmt.Println("cli/command/container/exec.go  runExec() ClientVersion : ", client.ClientVersion())
   104  
   105  	response, err := client.ContainerExecCreate(ctx, container, *execConfig)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	execID := response.ID
   111  	if execID == "" {
   112  		fmt.Fprintf(dockerCli.Out(), "exec ID empty")
   113  		return nil
   114  	}
   115      
   116      fmt.Println("cli/command/container/exec.go  runExec() execConfig.Detach : ", execConfig.Detach)
   117  	//Temp struct for execStart so that we don't need to transfer all the execConfig
   118  	if !execConfig.Detach {
   119  		if err := dockerCli.In().CheckTty(execConfig.AttachStdin, execConfig.Tty); err != nil {
   120  			return err
   121  		}
   122  	} else {
   123  		execStartCheck := types.ExecStartCheck{
   124  			Detach: execConfig.Detach,
   125  			Tty:    execConfig.Tty,
   126          }
   127  
   128        	fmt.Println("cli/command/container/exec.go  runExec() ctx : ", ctx)
   129  	    fmt.Println("cli/command/container/exec.go  runExec() execConfig : ", execConfig)
   130       	fmt.Println("cli/command/container/exec.go  runExec() execStartCheck : ", execStartCheck)
   131  	    fmt.Println("cli/command/container/exec.go  runExec() client : ", client)
   132  	    fmt.Println("cli/command/container/exec.go  runExec() response : ", response)
   133  
   134  		if err := client.ContainerExecStart(ctx, execID, execStartCheck); err != nil {
   135  			return err
   136  		}
   137  		// For now don't print this - wait for when we support exec wait()
   138  		// fmt.Fprintf(dockerCli.Out(), "%s\n", execID)
   139  		return nil
   140  	}
   141  
   142  	// Interactive exec requested.
   143  	var (
   144  		out, stderr io.Writer
   145  		in          io.ReadCloser
   146  		errCh       chan error
   147  	)
   148  
   149  	if execConfig.AttachStdin {
   150  		in = dockerCli.In()
   151  	}
   152  	if execConfig.AttachStdout {
   153  		out = dockerCli.Out()
   154  	}
   155  	if execConfig.AttachStderr {
   156  		if execConfig.Tty {
   157  			stderr = dockerCli.Out()
   158  		} else {
   159  			stderr = dockerCli.Err()
   160  		}
   161  	}
   162  
   163  	resp, err := client.ContainerExecAttach(ctx, execID, *execConfig)
   164  	if err != nil {
   165  		return err
   166  	}
   167  	defer resp.Close()
   168  	errCh = promise.Go(func() error {
   169  		return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, in, out, stderr, resp)
   170  	})
   171  
   172  	if execConfig.Tty && dockerCli.In().IsTerminal() {
   173  		if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil {
   174  			fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
   175  		}
   176  	}
   177  
   178  	if err := <-errCh; err != nil {
   179  		logrus.Debugf("Error hijack: %s", err)
   180  		return err
   181  	}
   182  
   183  	var status int
   184  	if _, status, err = getExecExitCode(ctx, client, execID); err != nil {
   185  		return err
   186  	}
   187  
   188  	if status != 0 {
   189  		return cli.StatusError{StatusCode: status}
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  
   196  
   197  func RunExecInFirstContainer(dockerCli *command.DockerCli) error {
   198  	fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer()")
   199  
   200      //execConfig, err := parseExec(opts, execCmd)
   201  	// just in case the ParseExec does not exit
   202  	//if container == "" || err != nil {
   203  	//	return cli.StatusError{StatusCode: 1}
   204  	//}
   205  
   206      opts := newExecOptions()
   207  	if opts.detachKeys != "" {
   208  		dockerCli.ConfigFile().DetachKeys = opts.detachKeys
   209  	}
   210  
   211  	// Send client escape keys
   212      execConfig := dockerCli.GetCliexecconfig()
   213  //	execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys
   214  
   215      container := dockerCli.GetClicontainer()
   216  
   217  	ctx := context.Background()
   218  
   219  /*    var flags *pflag.FlagSet
   220      cliopts := cliflags.NewClientOptions()
   221      if dockerCli.Client() == nil {
   222         fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() dockerCli.Client() is null!!!")
   223         cliopts.Common.SetDefaultOptions(flags)
   224         dockerPreRun(cliopts)
   225         if err := dockerCli.Initialize(cliopts); err != nil {
   226            fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() dockerCli.Initialize is err!!!")
   227         }
   228     }
   229  */
   230  
   231  //    client := dockerCli.Client()
   232      tmpclient, err := apiclient.NewEnvClient()
   233      if err != nil {
   234         fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() NewEnvClient() is err!!!")
   235      }
   236      headers := map[string]string{"User-Agent":"Docker-Client/1.14.0-dev (linux)"}
   237      tmpclient.SetCustomHTTPHeaders(headers)
   238      if error := dockerCli.SetCliclient(tmpclient); error != nil {
   239         fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() dockerCli.SetClient is err!!!")
   240      }
   241  
   242      client := dockerCli.Client()
   243      fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() NewEnvClient() client : ", client)
   244  
   245  /*		client = &http.Client{
   246  			Transport: &http.Transport{
   247  				TLSClientConfig: tlsc,
   248  			},
   249  		}
   250  	}
   251  
   252  	host := os.Getenv("DOCKER_HOST")
   253  	if host == "" {
   254  		host = DefaultDockerHost
   255  	}
   256  	version := os.Getenv("DOCKER_API_VERSION")
   257  	if version == "" {
   258  		version = DefaultVersion
   259  	}
   260  
   261  	cli, err := NewClient(host, version, client, nil)
   262  */
   263      fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() *execConfig : ", execConfig)
   264  	response, err := client.ContainerExecCreate(ctx, container, *execConfig)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	execID := response.ID
   270  	if execID == "" {
   271  		fmt.Fprintf(dockerCli.Out(), "exec ID empty")
   272  		return nil
   273  	}
   274      fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() after ContainerExecCreate()")
   275      
   276      fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() execConfig.Detach : ", execConfig.Detach)
   277  	//Temp struct for execStart so that we don't need to transfer all the execConfig
   278  	if !execConfig.Detach {
   279  		if err := dockerCli.In().CheckTty(execConfig.AttachStdin, execConfig.Tty); err != nil {
   280  			return err
   281  		}
   282  	} else {
   283  		execStartCheck := types.ExecStartCheck{
   284  			Detach: execConfig.Detach,
   285  			Tty:    execConfig.Tty,
   286  		}
   287  
   288          fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() execStarCheck() : ", execStartCheck)
   289          fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() response : ", response)
   290          fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() ctx : ", ctx)
   291  
   292          fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() before ContainerExecStart()")
   293  		if err := client.ContainerExecStart(ctx, execID, execStartCheck); err != nil {
   294              fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() ContainerExecStart()i is err!!!!")
   295  			return err
   296  		}
   297  		// For now don't print this - wait for when we support exec wait()
   298  		// fmt.Fprintf(dockerCli.Out(), "%s\n", execID)
   299  		return nil
   300  	}
   301      fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() after ContainerExecStart()")
   302  
   303  	// Interactive exec requested.
   304  	var (
   305  		out, stderr io.Writer
   306  		in          io.ReadCloser
   307  		errCh       chan error
   308  	)
   309  
   310  	if execConfig.AttachStdin {
   311  		in = dockerCli.In()
   312  	}
   313  	if execConfig.AttachStdout {
   314  		out = dockerCli.Out()
   315  	}
   316  	if execConfig.AttachStderr {
   317  		if execConfig.Tty {
   318  			stderr = dockerCli.Out()
   319  		} else {
   320  			stderr = dockerCli.Err()
   321  		}
   322  	}
   323  
   324  	resp, err := client.ContainerExecAttach(ctx, execID, *execConfig)
   325  	if err != nil {
   326  		return err
   327  	}
   328  	defer resp.Close()
   329  	errCh = promise.Go(func() error {
   330  		return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, in, out, stderr, resp)
   331  	})
   332  
   333  	if execConfig.Tty && dockerCli.In().IsTerminal() {
   334  		if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil {
   335  			fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
   336  		}
   337  	}
   338  
   339  	if err := <-errCh; err != nil {
   340  		logrus.Debugf("Error hijack: %s", err)
   341  		return err
   342  	}
   343  
   344  	var status int
   345  	if _, status, err = getExecExitCode(ctx, client, execID); err != nil {
   346  		return err
   347  	}
   348  
   349  	if status != 0 {
   350  		return cli.StatusError{StatusCode: status}
   351  	}
   352      
   353      fmt.Println("cli/command/container/exec.go  RunExecInFirstContainer() end")
   354  
   355  	return nil
   356  }
   357  
   358  
   359  
   360  
   361  
   362  
   363  
   364  // getExecExitCode perform an inspect on the exec command. It returns
   365  // the running state and the exit code.
   366  func getExecExitCode(ctx context.Context, client apiclient.ContainerAPIClient, execID string) (bool, int, error) {
   367  	resp, err := client.ContainerExecInspect(ctx, execID)
   368  	if err != nil {
   369  		// If we can't connect, then the daemon probably died.
   370  		if !apiclient.IsErrConnectionFailed(err) {
   371  			return false, -1, err
   372  		}
   373  		return false, -1, nil
   374  	}
   375  
   376  	return resp.Running, resp.ExitCode, nil
   377  }
   378  
   379  // parseExec parses the specified args for the specified command and generates
   380  // an ExecConfig from it.
   381  func parseExec(opts *execOptions, execCmd []string) (*types.ExecConfig, error) {
   382  	execConfig := &types.ExecConfig{
   383  		User:       opts.user,
   384  		Privileged: opts.privileged,
   385  		Tty:        opts.tty,
   386  		Cmd:        execCmd,
   387  		Detach:     opts.detach,
   388  	}
   389  
   390  	// If -d is not set, attach to everything by default
   391  	if !opts.detach {
   392  		execConfig.AttachStdout = true
   393  		execConfig.AttachStderr = true
   394  		if opts.interactive {
   395  			execConfig.AttachStdin = true
   396  		}
   397  	}
   398  
   399  	if opts.env != nil {
   400  		execConfig.Env = opts.env.GetAll()
   401  	}
   402  
   403  	return execConfig, nil
   404  }
   405  
   406  
   407  
   408  
   409  
   410  
   411  
   412  
   413  
   414  
   415  
   416  
   417  
   418  
   419  
   420  
   421  /*
   422  func newFirstDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
   423      fmt.Println("cli/command/container/exec.go  newFirstDockerCommand()")
   424  	opts := cliflags.NewClientOptions()
   425  	var flags *pflag.FlagSet
   426  
   427  	cmd := &cobra.Command{
   428  		Use:              "docker [OPTIONS] COMMAND [ARG...]",
   429  		Short:            "A self-sufficient runtime for containers",
   430  		SilenceUsage:     true,
   431  		SilenceErrors:    true,
   432  		TraverseChildren: true,
   433  		Args:             noArgs,
   434  		RunE: func(cmd *cobra.Command, args []string) error {
   435  			if opts.Version {
   436  				showVersion()
   437  				return nil
   438  			}
   439  			return dockerCli.ShowHelp(cmd, args)
   440  		},
   441  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   442  			// daemon command is special, we redirect directly to another binary
   443  			if cmd.Name() == "daemon" {
   444  				return nil
   445  			}
   446  			// flags must be the top-level command flags, not cmd.Flags()
   447  			opts.Common.SetDefaultOptions(flags)
   448  			dockerPreRun(opts)
   449  			if err := dockerCli.Initialize(opts); err != nil {
   450  				return err
   451  			}
   452  			return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
   453  		},
   454  	}
   455  	cli.SetupRootCommand(cmd)
   456  
   457  	cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
   458  		if dockerCli.Client() == nil { // when using --help, PersistenPreRun is not called, so initialization is needed.
   459  			// flags must be the top-level command flags, not cmd.Flags()
   460  			opts.Common.SetDefaultOptions(flags)
   461  			dockerPreRun(opts)
   462  			dockerCli.Initialize(opts)
   463  		}
   464  
   465  		if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
   466  			ccmd.Println(err)
   467  			return
   468  		}
   469  
   470  		hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
   471  
   472  		if err := ccmd.Help(); err != nil {
   473  			ccmd.Println(err)
   474  		}
   475  	})
   476  
   477  	flags = cmd.Flags()
   478  	flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")
   479  	flags.StringVar(&opts.ConfigDir, "config", cliconfig.ConfigDir(), "Location of client config files")
   480  	opts.Common.InstallFlags(flags)
   481  
   482  	cmd.SetOutput(dockerCli.Out())
   483  	cmd.AddCommand(newDaemonCommand())
   484  	commands.AddCommands(cmd, dockerCli)
   485      
   486      fmt.Println("cli/command/container/exec.go  newFirstDockerCommand() end ")
   487  
   488  	return cmd
   489  }
   490  
   491  func noArgs(cmd *cobra.Command, args []string) error {
   492  	if len(args) == 0 {
   493  		return nil
   494  	}
   495      fmt.Println("cli/command/container/exec.go noArgs() err : is not a docker cmmand")
   496  	return fmt.Errorf(
   497  		"docker: '%s' is not a docker command.\nSee 'docker --help'", args[0])
   498  }
   499  
   500  
   501  func showVersion() {
   502  	fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
   503  }
   504  
   505  func dockerPreRun(opts *cliflags.ClientOptions) {
   506  	cliflags.SetLogLevel(opts.Common.LogLevel)
   507  
   508  	if opts.ConfigDir != "" {
   509  		cliconfig.SetConfigDir(opts.ConfigDir)
   510  	}
   511  
   512  	if opts.Common.Debug {
   513  		utils.EnableDebug()
   514  	}
   515  }
   516  
   517  func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperimental bool) {
   518  	cmd.Flags().VisitAll(func(f *pflag.Flag) {
   519  		// hide experimental flags
   520  		if !hasExperimental {
   521  			if _, ok := f.Annotations["experimental"]; ok {
   522  				f.Hidden = true
   523  			}
   524  		}
   525  
   526  		// hide flags not supported by the server
   527  		if flagVersion, ok := f.Annotations["version"]; ok && len(flagVersion) == 1 && versions.LessThan(clientVersion, flagVersion[0]) {
   528  			f.Hidden = true
   529  		}
   530  
   531  	})
   532  
   533  	for _, subcmd := range cmd.Commands() {
   534  		// hide experimental subcommands
   535  		if !hasExperimental {
   536  			if _, ok := subcmd.Tags["experimental"]; ok {
   537  				subcmd.Hidden = true
   538  			}
   539  		}
   540  
   541  		// hide subcommands not supported by the server
   542  		if subcmdVersion, ok := subcmd.Tags["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) {
   543  			subcmd.Hidden = true
   544  		}
   545  	}
   546  }
   547  
   548  func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) error {
   549  	if !hasExperimental {
   550  		if _, ok := cmd.Tags["experimental"]; ok {
   551  			return errors.New("only supported with experimental daemon")
   552  		}
   553  	}
   554  
   555  	if cmdVersion, ok := cmd.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
   556  		return fmt.Errorf("only supported with daemon version >= %s", cmdVersion)
   557  	}
   558  
   559  	return nil
   560  }
   561  */