Call execution process for the Docker CLI

Source: Internet
Author: User

Normally we enter a command, such as Docker info, that uses the CLI to send an HTTP request to daemon, returning some container-related parameters

When entering Docker INFO, if Daemon is not running in the background, there will be info[0007] get/v1.19/info output, and get is equivalent to the method get in Get/post in HTTP, v1.19 equivalent to the version number, info is the name of the command

Next we analyze the source code:

SERVERRESP, err: = Cli.call ("GET", "/containers/" +cmd. ARG (0) + "/json", nil, nil)//attach command request//Once Cli.call call execution Serverresp, err: = Cli.call ("GET", "/info", nil, nil)//info command request

Next we analyze the Cli.call execution process:

corresponding to the attach request, method= "GET"  path= "/containers/" +cmd. ARG (0) + "/json"  data=nil headers=nilfunc  (CLI *DOCKERCLI)  call (method, path  string, data interface{}, headers map[string][]string)   (*serverresponse, error) The  {params, err := cli.encodedata (data)  //is encoded in interface{} and can be compatible with all types of dataif  err != nil {sr := &serverresponse{body:        nil,header:     nil,statuscode: -1,}return sr, nil}         //to set HTTP headersif data != nil {if headers ==  nil {headers = make (map[string][]string)}headers["Content-type"] = []string{" Application/json "}}        //Next is it serverresp, err :=  Cli.clientrequest (method, path, params, headers) return  Serverresp, err} 

Take a look at Cli.clientrequest execution

func  (CLI&NBSP;*DOCKERCLI)  clientrequest (Method, path string, in io. reader, headers map[string][]string)   (*serverresponse, error)  {                 // Daemon The return data of the struct is serverrespserverresp := &serverresponse{body:        nil,statuscode: -1,}        //This is expected post and put method, Seems to be writing xxexpectedpayload :=  (method ==  "POST"  | |  method ==  "PUT") if expectedpayload && in == nil {in  = bytes. Newreader ([]byte{})}//here is the focus, this is to return a Req object req, err := http with the Golang default HTTP packet. Newrequest (method, fmt. Sprintf ("%s/v%s%s",  cli.basepath, api. Version, path),  in) If err != nil {return serverresp, err}// add  cli config ' S http heAders before we set the docker headers// then the user can ' t  change our headers//some of the data that is set when the CLI is initialized, such as the following cli.addr,cli.scheme are set at initialization time For k, v  := range cli.configfile.httpheaders {req. Header.set (k, v)}req. Header.set ("User-agent",  "docker-client/" +dockerversion. version+ "  (" +runtime. goos+ ")") req. Url. host = cli.addrreq.url.scheme = cli.schemeif headers != nil {for  K, v := range headers {req. Header[k] = v}}if expectedpayload && req. Header.get ("Content-type")  ==  ""  {req. Header.set ("Content-type",  "Text/plain")}        // The request to execute HTTP is here, CLI. HttpClient returns &AMP;HTTP. client{transport: cli.transport}        // Cli.transport is also set when the CLI is initialized, calling Golang's HTTP packet to make a request to the server resp, err := cli. HTTPClient (). Do (req) if resp != nil {serverresp.statuscode = resp. statuscode}                // The next step is a handyman's return value error and HTTP error code judgment if err != nil {if types. Istimeout (err)  | |  strings. Contains (Err. Error (),  "connection refused")  | |  strings. Contains (Err. Error (),  "Dial unix")  {return serverresp, errconnectionfailed}if cli.tlsconfig  == nil && strings. Contains (Err. Error (),  "Malformed http response")  {return serverresp, fmt. Errorf ("%v.\n* are you trying to connect to a tls-enabled daemon  without tls? ",  err)}if cli.tlsconfig != nil && strings. Contains (Err. Error (),  "Remote error: bad certificate")  {return serverresp, fmt. ErroRF ("the server probably has client authentication  (--tlsverify)  enabled.  please check your tls client certification settings: %v ",  err)} Return serverresp, fmt. Errorf ("An error occurred trying to connect: %v",  err)}if  serverresp.statuscode < 200 | |  serverresp.statuscode >= 400 {body, err := ioutil. ReadAll (resp. Body) If err != nil {return serverresp, err}if len (body)  == 0  {return serverresp, fmt. Errorf ("error: request returned %s for api route and version %s,  check if the server supports the requested api version ",  http. StatusText (Serverresp.statuscode),  req. URL)}return serverresp, fmt. Errorf ("Error response from daemon: %s ",  bytes. Trimspace (body))}serverresp.body = resp. Bodyserverresp.header = resp. Headerreturn serverresp, nil}

Combing, in fact, two steps is very important, one step is HTTP. Newrequest, one step is the CLI. HTTPClient (). Do (req)

Remember that there are a bunch of parameters that were created when the CLI was initialized, and look at the code of the CLI initialization

FUNC&NBSP;NEWDOCKERCLI (In io. Readcloser, out, err io. Writer, clientflags *cli. Clientflags)  *dockercli {cli := &dockercli{in:      in, out:     out,err:     err,keyfile:  clientflags.common.trustkey,} //input, output, error IO, and TLS keyfilecli.init = func ()  error { Clientflags.postparse () Hosts := clientflags.common.hostsswitch len (hosts)  {case 0: Defaulthost := os. Getenv ("Docker_host") if defaulthost ==  ""  {defaulthost = opts. defaulthost}//See how much this defaulthost is//defaulthttphost =  "127.0.0.1"   defaulthttpport =  2375 // defaulthost = fmt. Sprintf ("tcp://%s:%d",  defaulthttphost, defaulthttpport)//actually local use is defaultunixsocket =  "/ Var/run/docker.sock "defaulthost, err := opts. Validatehost (defaulthost) if  err != nil {return err}hosts = []string{defaulthost}case 1:// only  accept one host to talk todefault:return errors. New ("Please specify only one -h")}protoaddrparts := strings. SPLITN (hosts[0],  "://",  2) cli.proto, cli.addr = protoaddrparts[0],  protoaddrparts[1]//protocol address is initialized here, proto= "tcp/http/..."  addr= "127.0.0.1/...."                if cli.proto ==  "TCP"  {// error  is checked in pkg/parsers alreadyparsed, _ := url. Parse ("tcp://"  + cli.addr) cli.addr = parsed. Hostcli.basepath = parsed. path}if clientflags.common.tlsoptions != nil {cli.scheme =  "https" var e  errorcli.tlsconfig, e = tlsconfig. Client (*clientflags.common.tlsoptions) if e != nil {return e}} else {cli.scheme =  "http"}if cli.in !=  Nil {cli.infd, cli.isterminalin = term. Getfdinfo (cli.in)}if cli.out != nil {cli.outfd, cli.isterminalout = term. Getfdinfo (cli.out)}// the transport is created here for reuse during  the client session.cli.transport = &http. transport{tlsclientconfig: cli.tlsconfig,}//This very critical cli.transport setting Sockets.configuretcptransport ( CLI.TRANSPORT,&NBSP;CLI.PROTO,&NBSP;CLI.ADDR) Configfile, e := cliconfig. Load (Cliconfig. Configdir ()) if e != nil {fmt. fprintf (cli.err,  "warning: error loading config file:%v\n",  e)}cli.configFile &NBSP;=&NBSP;CONFIGFILERETURN&NBSP;NIL}RETURN&NBSP;CLI}

Finally, let's see how Clientflags got here, right?

The var clientflags = &cli initialized in Docker/docker/client.go. Clientflags{flagset:new (flag. Flagset), common:commonflags}

//------------------------------------------------------------------------------------------------------

Apply your own version of the CLI to request Docker's daemon

package mainimport  (        _ "bytes"           "FMT"          "Io/ioutil"           "Net/http"          "OS"           "Runtime"          "Time"           "NET") Func main () {   httppost ()}func  Configuretcptransport (tr *http. transport, proto, addr string)  {        timeout  := 32 * time. second        if proto ==  "UNIX"  {                 // No need  for compression in local communications.                tr. disablecompression = true                 tr. Dial = func (_, _ string)   (net. Conn, error)  {                         return net. Dialtimeout (proto, addr, timeout)                  }        } else {                 tr. Proxy = http. proxyfromenvironment                 tr. dial =  (&net. Dialer{timeout: timeout}). dial      &Nbsp; }}func httppost ()  {       tr := &http. Transport{}       configuretcptransport (tr, "UNIX",  "/var/run/ Docker.sock ")         client := &http. Client{transport:tr}        req, err := http. Newrequest ("GET",  "/v1.20/info",  nil)  //v1.20 This is my own docker api version  .       if err != nil {                 fmt. Println (111111111111111111)         }         req. Header.set ("Content-type",  "Application/json")         req. Header.set ("User-agent",  "docker-client/" + "1.8.0" + "  (" +runtime. goos+ ")") &NBSP;&NBsp;      //1.8.0 is also my own version of Docker version          req. Url. host =  "/var/run/docker.sock"         req. Url. scheme =  "http"         resp, err := client. Do (req)         fmt. Printf ("%v ---- %v ---------- %v\n ", Resp,err,os. Getenv ("Docker_host"))         if err ==nil {           defer resp. Body.close ()         }        / /defer resp. Body.close ()         body, err := ioutil. ReadAll (resp. Body)         if err != nil {                 // handle error         }        fmt. Println (String (body))}

In summary, this request for Docker is still using the HTTP package inside the Golang, the HTTP packet execution process is too large and complex, this article is not outlined.

Call execution process for the Docker CLI

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.