github.com/a4a881d4/docker@v1.9.0-rc2/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 indicates 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 // Initialize calls the init function that will setup the configuration for the client 62 // such as the TLS, tcp and other parameters used to run the client. 63 func (cli *DockerCli) Initialize() error { 64 if cli.init == nil { 65 return nil 66 } 67 return cli.init() 68 } 69 70 // CheckTtyInput checks if we are trying to attach to a container tty 71 // from a non-tty client input stream, and if so, returns an error. 72 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 73 // In order to attach to a container tty, input stream for the client must 74 // be a tty itself: redirecting or piping the client standard input is 75 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 76 if ttyMode && attachStdin && !cli.isTerminalIn { 77 return errors.New("cannot enable tty mode on non tty input") 78 } 79 return nil 80 } 81 82 // PsFormat returns the format string specified in the configuration. 83 // String contains columns and format specification, for example {{ID}\t{{Name}}. 84 func (cli *DockerCli) PsFormat() string { 85 return cli.configFile.PsFormat 86 } 87 88 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. 89 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config 90 // is set the client scheme will be set to https. 91 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035). 92 func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli { 93 cli := &DockerCli{ 94 in: in, 95 out: out, 96 err: err, 97 keyFile: clientFlags.Common.TrustKey, 98 } 99 100 cli.init = func() error { 101 102 clientFlags.PostParse() 103 104 hosts := clientFlags.Common.Hosts 105 106 switch len(hosts) { 107 case 0: 108 hosts = []string{os.Getenv("DOCKER_HOST")} 109 case 1: 110 // only accept one host to talk to 111 default: 112 return errors.New("Please specify only one -H") 113 } 114 115 defaultHost := opts.DefaultTCPHost 116 if clientFlags.Common.TLSOptions != nil { 117 defaultHost = opts.DefaultTLSHost 118 } 119 120 var e error 121 if hosts[0], e = opts.ParseHost(defaultHost, hosts[0]); e != nil { 122 return e 123 } 124 125 protoAddrParts := strings.SplitN(hosts[0], "://", 2) 126 cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1] 127 128 if cli.proto == "tcp" { 129 // error is checked in pkg/parsers already 130 parsed, _ := url.Parse("tcp://" + cli.addr) 131 cli.addr = parsed.Host 132 cli.basePath = parsed.Path 133 } 134 135 if clientFlags.Common.TLSOptions != nil { 136 cli.scheme = "https" 137 var e error 138 cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions) 139 if e != nil { 140 return e 141 } 142 } else { 143 cli.scheme = "http" 144 } 145 146 if cli.in != nil { 147 cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in) 148 } 149 if cli.out != nil { 150 cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out) 151 } 152 153 // The transport is created here for reuse during the client session. 154 cli.transport = &http.Transport{ 155 TLSClientConfig: cli.tlsConfig, 156 } 157 sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr) 158 159 configFile, e := cliconfig.Load(cliconfig.ConfigDir()) 160 if e != nil { 161 fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e) 162 } 163 cli.configFile = configFile 164 165 return nil 166 } 167 168 return cli 169 }