github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/api/client/cli.go (about) 1 package client 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "runtime" 10 11 "github.com/docker/docker/api" 12 "github.com/docker/docker/cli" 13 "github.com/docker/docker/cliconfig" 14 "github.com/docker/docker/dockerversion" 15 "github.com/docker/docker/opts" 16 "github.com/docker/docker/pkg/term" 17 "github.com/docker/engine-api/client" 18 "github.com/docker/go-connections/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 // configFile has the client configuration file 28 configFile *cliconfig.ConfigFile 29 // in holds the input stream and closer (io.ReadCloser) for the client. 30 in io.ReadCloser 31 // out holds the output stream (io.Writer) for the client. 32 out io.Writer 33 // err holds the error stream (io.Writer) for the client. 34 err io.Writer 35 // keyFile holds the key file as a string. 36 keyFile string 37 // inFd holds the file descriptor of the client's STDIN (if valid). 38 inFd uintptr 39 // outFd holds file descriptor of the client's STDOUT (if valid). 40 outFd uintptr 41 // isTerminalIn indicates whether the client's STDIN is a TTY 42 isTerminalIn bool 43 // isTerminalOut indicates whether the client's STDOUT is a TTY 44 isTerminalOut bool 45 // client is the http client that performs all API operations 46 client client.APIClient 47 // state holds the terminal state 48 state *term.State 49 } 50 51 // Initialize calls the init function that will setup the configuration for the client 52 // such as the TLS, tcp and other parameters used to run the client. 53 func (cli *DockerCli) Initialize() error { 54 if cli.init == nil { 55 return nil 56 } 57 return cli.init() 58 } 59 60 // CheckTtyInput checks if we are trying to attach to a container tty 61 // from a non-tty client input stream, and if so, returns an error. 62 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 63 // In order to attach to a container tty, input stream for the client must 64 // be a tty itself: redirecting or piping the client standard input is 65 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 66 if ttyMode && attachStdin && !cli.isTerminalIn { 67 return errors.New("cannot enable tty mode on non tty input") 68 } 69 return nil 70 } 71 72 // PsFormat returns the format string specified in the configuration. 73 // String contains columns and format specification, for example {{ID}}\t{{Name}}. 74 func (cli *DockerCli) PsFormat() string { 75 return cli.configFile.PsFormat 76 } 77 78 // ImagesFormat returns the format string specified in the configuration. 79 // String contains columns and format specification, for example {{ID}}\t{{Name}}. 80 func (cli *DockerCli) ImagesFormat() string { 81 return cli.configFile.ImagesFormat 82 } 83 84 func (cli *DockerCli) setRawTerminal() error { 85 if cli.isTerminalIn && os.Getenv("NORAW") == "" { 86 state, err := term.SetRawTerminal(cli.inFd) 87 if err != nil { 88 return err 89 } 90 cli.state = state 91 } 92 return nil 93 } 94 95 func (cli *DockerCli) restoreTerminal(in io.Closer) error { 96 if cli.state != nil { 97 term.RestoreTerminal(cli.inFd, cli.state) 98 } 99 if in != nil { 100 return in.Close() 101 } 102 return nil 103 } 104 105 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. 106 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config 107 // is set the client scheme will be set to https. 108 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035). 109 func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli { 110 cli := &DockerCli{ 111 in: in, 112 out: out, 113 err: err, 114 keyFile: clientFlags.Common.TrustKey, 115 } 116 117 cli.init = func() error { 118 clientFlags.PostParse() 119 configFile, e := cliconfig.Load(cliconfig.ConfigDir()) 120 if e != nil { 121 fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e) 122 } 123 cli.configFile = configFile 124 125 host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions) 126 if err != nil { 127 return err 128 } 129 130 customHeaders := cli.configFile.HTTPHeaders 131 if customHeaders == nil { 132 customHeaders = map[string]string{} 133 } 134 customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")" 135 136 verStr := api.DefaultVersion.String() 137 if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" { 138 verStr = tmpStr 139 } 140 141 clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions) 142 if err != nil { 143 return err 144 } 145 146 client, err := client.NewClient(host, verStr, clientTransport, customHeaders) 147 if err != nil { 148 return err 149 } 150 cli.client = client 151 152 if cli.in != nil { 153 cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in) 154 } 155 if cli.out != nil { 156 cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out) 157 } 158 159 return nil 160 } 161 162 return cli 163 } 164 165 func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) { 166 switch len(hosts) { 167 case 0: 168 host = os.Getenv("DOCKER_HOST") 169 case 1: 170 host = hosts[0] 171 default: 172 return "", errors.New("Please specify only one -H") 173 } 174 175 defaultHost := opts.DefaultTCPHost 176 if tlsOptions != nil { 177 defaultHost = opts.DefaultTLSHost 178 } 179 180 host, err = opts.ParseHost(defaultHost, host) 181 return 182 } 183 184 func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) { 185 if tlsOptions == nil { 186 return &http.Transport{}, nil 187 } 188 189 config, err := tlsconfig.Client(*tlsOptions) 190 if err != nil { 191 return nil, err 192 } 193 return &http.Transport{ 194 TLSClientConfig: config, 195 }, nil 196 }