github.com/rentongzhang/docker@v1.8.2-rc1/api/client/cli.go (about) 1 package client 2 3 import ( 4 "crypto/tls" 5 "errors" 6 "fmt" 7 "io" 8 "net/http" 9 "net/url" 10 "os" 11 "strings" 12 13 "github.com/docker/docker/cli" 14 "github.com/docker/docker/cliconfig" 15 "github.com/docker/docker/opts" 16 "github.com/docker/docker/pkg/sockets" 17 "github.com/docker/docker/pkg/term" 18 "github.com/docker/docker/pkg/tlsconfig" 19 ) 20 21 // DockerCli represents the docker command line client. 22 // Instances of the client can be returned from NewDockerCli. 23 type DockerCli struct { 24 // initializing closure 25 init func() error 26 27 // proto holds the client protocol i.e. unix. 28 proto string 29 // addr holds the client address. 30 addr string 31 // basePath holds the path to prepend to the requests 32 basePath string 33 34 // configFile has the client configuration file 35 configFile *cliconfig.ConfigFile 36 // in holds the input stream and closer (io.ReadCloser) for the client. 37 in io.ReadCloser 38 // out holds the output stream (io.Writer) for the client. 39 out io.Writer 40 // err holds the error stream (io.Writer) for the client. 41 err io.Writer 42 // keyFile holds the key file as a string. 43 keyFile string 44 // tlsConfig holds the TLS configuration for the client, and will 45 // set the scheme to https in NewDockerCli if present. 46 tlsConfig *tls.Config 47 // scheme holds the scheme of the client i.e. https. 48 scheme string 49 // inFd holds the file descriptor of the client's STDIN (if valid). 50 inFd uintptr 51 // outFd holds file descriptor of the client's STDOUT (if valid). 52 outFd uintptr 53 // isTerminalIn indicates whether the client's STDIN is a TTY 54 isTerminalIn bool 55 // isTerminalOut dindicates whether the client's STDOUT is a TTY 56 isTerminalOut bool 57 // transport holds the client transport instance. 58 transport *http.Transport 59 } 60 61 func (cli *DockerCli) Initialize() error { 62 if cli.init == nil { 63 return nil 64 } 65 return cli.init() 66 } 67 68 // CheckTtyInput checks if we are trying to attach to a container tty 69 // from a non-tty client input stream, and if so, returns an error. 70 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 71 // In order to attach to a container tty, input stream for the client must 72 // be a tty itself: redirecting or piping the client standard input is 73 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 74 if ttyMode && attachStdin && !cli.isTerminalIn { 75 return errors.New("cannot enable tty mode on non tty input") 76 } 77 return nil 78 } 79 80 func (cli *DockerCli) PsFormat() string { 81 return cli.configFile.PsFormat 82 } 83 84 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. 85 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config 86 // is set the client scheme will be set to https. 87 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035). 88 func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli { 89 cli := &DockerCli{ 90 in: in, 91 out: out, 92 err: err, 93 keyFile: clientFlags.Common.TrustKey, 94 } 95 96 cli.init = func() error { 97 clientFlags.PostParse() 98 99 hosts := clientFlags.Common.Hosts 100 101 switch len(hosts) { 102 case 0: 103 defaultHost := os.Getenv("DOCKER_HOST") 104 if defaultHost == "" { 105 defaultHost = opts.DefaultHost 106 } 107 defaultHost, err := opts.ValidateHost(defaultHost) 108 if err != nil { 109 return err 110 } 111 hosts = []string{defaultHost} 112 case 1: 113 // only accept one host to talk to 114 default: 115 return errors.New("Please specify only one -H") 116 } 117 118 protoAddrParts := strings.SplitN(hosts[0], "://", 2) 119 cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1] 120 121 if cli.proto == "tcp" { 122 // error is checked in pkg/parsers already 123 parsed, _ := url.Parse("tcp://" + cli.addr) 124 cli.addr = parsed.Host 125 cli.basePath = parsed.Path 126 } 127 128 if clientFlags.Common.TLSOptions != nil { 129 cli.scheme = "https" 130 var e error 131 cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions) 132 if e != nil { 133 return e 134 } 135 } else { 136 cli.scheme = "http" 137 } 138 139 if cli.in != nil { 140 cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in) 141 } 142 if cli.out != nil { 143 cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out) 144 } 145 146 // The transport is created here for reuse during the client session. 147 cli.transport = &http.Transport{ 148 TLSClientConfig: cli.tlsConfig, 149 } 150 sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr) 151 152 configFile, e := cliconfig.Load(cliconfig.ConfigDir()) 153 if e != nil { 154 fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e) 155 } 156 cli.configFile = configFile 157 158 return nil 159 } 160 161 return cli 162 }