github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/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 } 48 49 // Initialize calls the init function that will setup the configuration for the client 50 // such as the TLS, tcp and other parameters used to run the client. 51 func (cli *DockerCli) Initialize() error { 52 if cli.init == nil { 53 return nil 54 } 55 return cli.init() 56 } 57 58 // CheckTtyInput checks if we are trying to attach to a container tty 59 // from a non-tty client input stream, and if so, returns an error. 60 func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error { 61 // In order to attach to a container tty, input stream for the client must 62 // be a tty itself: redirecting or piping the client standard input is 63 // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. 64 if ttyMode && attachStdin && !cli.isTerminalIn { 65 return errors.New("cannot enable tty mode on non tty input") 66 } 67 return nil 68 } 69 70 // PsFormat returns the format string specified in the configuration. 71 // String contains columns and format specification, for example {{ID}}\t{{Name}}. 72 func (cli *DockerCli) PsFormat() string { 73 return cli.configFile.PsFormat 74 } 75 76 // ImagesFormat returns the format string specified in the configuration. 77 // String contains columns and format specification, for example {{ID}}\t{{Name}}. 78 func (cli *DockerCli) ImagesFormat() string { 79 return cli.configFile.ImagesFormat 80 } 81 82 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. 83 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config 84 // is set the client scheme will be set to https. 85 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035). 86 func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli { 87 cli := &DockerCli{ 88 in: in, 89 out: out, 90 err: err, 91 keyFile: clientFlags.Common.TrustKey, 92 } 93 94 cli.init = func() error { 95 clientFlags.PostParse() 96 configFile, e := cliconfig.Load(cliconfig.ConfigDir()) 97 if e != nil { 98 fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e) 99 } 100 cli.configFile = configFile 101 102 host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions) 103 if err != nil { 104 return err 105 } 106 107 customHeaders := cli.configFile.HTTPHeaders 108 if customHeaders == nil { 109 customHeaders = map[string]string{} 110 } 111 customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")" 112 113 verStr := api.DefaultVersion.String() 114 if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" { 115 verStr = tmpStr 116 } 117 118 clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions) 119 if err != nil { 120 return err 121 } 122 123 client, err := client.NewClient(host, verStr, clientTransport, customHeaders) 124 if err != nil { 125 return err 126 } 127 cli.client = client 128 129 if cli.in != nil { 130 cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in) 131 } 132 if cli.out != nil { 133 cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out) 134 } 135 136 return nil 137 } 138 139 return cli 140 } 141 142 func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) { 143 switch len(hosts) { 144 case 0: 145 host = os.Getenv("DOCKER_HOST") 146 case 1: 147 host = hosts[0] 148 default: 149 return "", errors.New("Please specify only one -H") 150 } 151 152 defaultHost := opts.DefaultTCPHost 153 if tlsOptions != nil { 154 defaultHost = opts.DefaultTLSHost 155 } 156 157 host, err = opts.ParseHost(defaultHost, host) 158 return 159 } 160 161 func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) { 162 if tlsOptions == nil { 163 return &http.Transport{}, nil 164 } 165 166 config, err := tlsconfig.Client(*tlsOptions) 167 if err != nil { 168 return nil, err 169 } 170 return &http.Transport{ 171 TLSClientConfig: config, 172 }, nil 173 }