Specific execution flow of Docker exec

Source: Internet
Author: User

First, make a request for Docker exec:

Docker exec-it 5504F937F7BB SH

The output of the corresponding docker-d (started Docker daemon) is :

INFO[0211] Post/v1.20/containers/5504f937f7bb/exec info[0211] post/v1.20/exec/ Fc9c11ae6ac4827ea507e885c888bdb37c8f7b906347b9272adf8d580a6417df/start info[0211] post/v1.20/exec/ Fc9c11ae6ac4827ea507e885c888bdb37c8f7b906347b9272adf8d580a6417df/resize?h=69&w=236//exit exiting Docker EXEC initiates a TTY after 2015/10/29 00:47:51 Http:response. Writeheader on hijacked connectioninfo[0294] get/v1.20/exec/ Fc9c11ae6ac4827ea507e885c888bdb37c8f7b906347b9272adf8d580a6417df/json


I created a main function of Docker exec myself, simulating the client execution process of Docker exec (Ingress execution):

Package Mainimport (_ "FMT" "Github.com/docker/docker/api/client") func main () {client. Doeexc ()}

Then is the process of simulating the Docker exec request, I have re-wrote one, skip the Flag module interpretation of parameters, directly on the static parameters

package clientimport  ("Encoding/json" "FMT" "io" "Github.com/sirupsen/logrus" "github.com/docker/docker/ Api/types "" Github.com/docker/docker/cli "flag " Github.com/docker/docker/pkg/mflag "" github.com/docker/ Docker/pkg/promise "" Github.com/docker/docker/pkg/term "_ " Github.com/docker/docker/runconfig ") type  execconfig struct {user         string    // User that will run the commandPrivileged   bool      // Is the container in privileged modeTty           bool     // attach standard  streams to a tty. container    string   // name of the container  (To  execute in) attachstdin  bool     // attach the standard input, makes possible user interactionattachstderr bool      // Attach the standard outputAttachStdout bool      // Attach the standard errorDetach        bool     // Execute in detach modeCmd           []string // execution commands and args}var  commonflags = &cli.commonflags{flagset: new (flag. Flagset)}var clientflags = &cli. Clientflags{flagset: new (flag. Flagset),  common: commonflags}func doeexc ()  {stdin, stdout, stderr :=  Term. Stdstreams () newcli := newdockercli (stdin, stdout, stderr, clientflags) If err  := newcli.init ();  err != nil {fmt. Printf ("docker&Nbsp;cli init err %s \n ",  err)}cmdexec (NEWCLI)}func cmdexec (newcli * DOCKERCLI)  error {execConfig := &ExecConfig{User:           "",privileged:   false,tty:           true,cmd:          []string{"sh"},Container:      "5504F937F7BB", Detach:       false,attachstdin: The   true,attachstderr: true,attachstdout: true,}//corresponds to the first output of the daemon serverresp, err :=  newcli.call ("POST",  "/containers/" +execconfig.container+ "/exec",  execconfig, nil) if  Err != nil {return err}defer serverresp.body.close () var response types. Containerexeccreateresponseif err := json. Newdecoder (Serverresp.body). Decode (&response);  err != nil {reTurn err}execid := response.id//daemon will assign a execidfmt.printf ("response id %d \n", &NBSP;EXECID) if execid ==  ""  {fmt. fprintf (newcli.out,  "Exec id empty") Return nil}//temp struct for execstart  so that we don ' t need to transfer all the  Execconfigexecstartcheck := &types. execstartcheck{detach: execconfig.detach,tty:    execconfig.tty,}if ! Execconfig.detach {if err := newcli. Checkttyinput (Execconfig.attachstdin, execconfig.tty);  err != nil {return err}}  else {if _, _, err := readbody (Newcli.call ("POST",  "/exec/" +execID+ "/ Start ",  execstartcheck, nil));  err != nil {return err}// for now  don ' T&NBSP;PRINT&NBSP;THIS&NBSP;-&NBSP;WAIT&NBSP;FOR&NBSP;WHEN&NBSP;WE&NBSP;SUPPORT&NBSP;EXEC&NBSp;wait ()// fmt. fprintf (cli.out,  "%s\n",  execid) return nil}//  This place is the process of IO interaction, that is, the binding TTY, output stream redirection processes var  ( Out, stderr io. Writerin          io. Readcloserhijacked    = make (Chan io. Closer) errch       chan error)// block the return  Until the chan gets closeddefer func ()  {logrus. DEBUGF ("End of cmdexec (),  waiting for hijack to finish.") If _, ok := <-hijacked; ok {fmt. Fprintln (newcli.err,  "hijack did not finish  (Chan still open)")}} () if  execconfig.attachstdin {in = newcli.in}if execconfig.attachstdout {out =  Newcli.out}if execconfig.attachstderr {if execconfig.tty {stderr = newcli.out}  else {stderr = newcli.err}}//This is the most critical execution function errch = promise. Go (func ()  error {return newcli.hijack ("POST",  "/exec/" +execid+ "/start",  Execconfig.tty, in, out, stderr, hijacked, execconfig)})// Acknowledge the  hijack before startingselect {case closer := <-hijacked:// make  sure that hijack gets closed when returning.  (result// in  Closing hijack chan and freeing server ' s goroutines.if closer !=  Nil {defer closer. Close ()}case err := <-errch:if err != nil {logrus. DEBUGF ("error hijack: %s",  err) return err}}if execconfig.tty &&  Newcli.isterminalin {if err := newcli.monitorttysize (execid, true);  err !=  nil {fmt. fprintf (newcli.err,  "Error monitoring tty sizE: %s\n ",  err)}}if err := <-errch; err != nil {logrus. DEBUGF ("error hijack: %s",  err) Return err}var status intif _, status,  err = getexecexitcode (newcli, execid);  err != nil {return err}if  status != 0 {return newcli. Statuserror{statuscode: status}}return nil}

to let us analyze the execution flow of the most critical newcli.hijack

func  (CLI&NBSP;*DOCKERCLI)  hijack (Method, path string, setrawterminal bool,  in io. Readcloser, stdout, stderr io. Writer, started chan io. closer, data interface{})  error {defer func ()  {if started != nil  {close (started)}} () Params, err := cli.encodedata (data) if err != nil { Return err}req, err := http. Newrequest (method, fmt. Sprintf ("%s/v%s%s",  cli.basepath, api. Version, path),  params) if err != nil {return err}// add cli  Config ' S http headers before we set the docker headers// then  the user can ' t change our headersfor k, v := range  Cli.configfile.httpheaders {req. Header.set (k, v)}//req. Header.set ("User-agent",  "docker-client/" +dockerversion. Version+ "  (" +runtime. goos+ ")") req. Header.set ("Content-type",  "Text/plain") req. Header.set ("Connection",  "Upgrade") req. Header.set ("Upgrade",  "TCP") req. Host = cli.addrdial, err := cli.dial ()  //newly established connection// when we set  up a TCP connection for hijack, there could be long  periods// of inactivity  (A long running command with no output)  that in certain// network setups may cause econntimeout, leaving  the client in an unknown// state. setting tcp keepalive on  the socket connection will prohibit// ECONNTIMEOUT unless the  Socket connection truly is brokenif tcpconn, ok := dial. (*net. Tcpconn);  ok {tcpconn.setkeepalive (True) tcpconn.setkeepaliveperiod (30&NBSp;* time. Second)}if err != nil {if strings. Contains (Err. Error (),  "connection refused")  {return fmt. Errorf ("cannot connect to the docker daemon. is  ' Docker daemon '   Running on this host ")} Return err}clientconn := httputil. Newclientconn (Dial, nil) defer clientconn. Close ()// server hijacks the connection, error  ' connection closed '   Expectedclientconn. Do (req)  //the second rwc, br := clientconn corresponding to the output of the daemon. Hijack ()  //clear out the buffer  clears the buffer data DEFER&NBSP;RWC. Close () If started != nil {started <- rwc}var receivestdout chan  errorvar oldstate *term. state        //if a pseudo terminal needs to be assigned if in != nil &&  setrawterminal && cli.isterminalin &&&Nbsp;os.getenv ("Noraw")  ==  ""  {oldstate, err = term. Setrawterminal (CLI.INFD) if err != nil {return err}defer term. Restoreterminal (cli.infd, oldstate)}if stdout != nil | |  stderr != nil {receivestdout = promise. Go (func ()   (Err error)  {defer func ()  {if in != nil {if  Setrawterminal && cli.isterminalin {term. Restoreterminal (cli.infd, oldstate)}// for some reason this close call  blocks on darwin.  As the client exists right after, simply discard the  Close// until we find a better solution.if runtime. goos !=  "Darwin"  {in. Close ()}}} ()                       &nbsRedirection of the P;  //io output stream is here// when tty is on, use regular copyif  Setrawterminal && stdout != nil {_, err = io. Copy (STDOUT,&NBSP;BR)} else {_, err = stdcopy. Stdcopy (STDOUT,&NBSP;STDERR,&NBSP;BR)}logrus. DEBUGF ("[Hijack] end of stdout") Return err})}sendstdin := promise. The redirect of the Go (func ()  error {       //io input stream is here If in != nil  {io. Copy (rwc, in) Logrus. DEBUGF ("[Hijack] end of stdin")}IF&NBSP;CONN,&NBSP;OK&NBSP;:=&NBSP;RWC. (Interface {closewrite ()  error});  ok {if err := conn. Closewrite ();  err != nil {logrus. DEBUGF ("Couldn ' t send eof: %s",  err)}}// discard errors due to  Pipe interruptionreturn nil})         //blocked here if stdout ! = nil | |  stderr != nil {if err := <-receivestdout; err != nil  {logrus. DEBUGF ("error receivestdout: %s",  err) return err}}if !cli.isterminalin {if  Err := <-sendstdin; err != nil {logrus. DEBUGF ("error sendstdin: %s",  err) Return err}}return nil}

Summary, after redirection, if it is not executed in the background, it is stuck there until the process is killed.

Specific execution flow of Docker exec

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.