github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/cli/command/cli.go (about) 1 package command 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "path/filepath" 10 "runtime" 11 12 "github.com/docker/docker/api" 13 cliflags "github.com/docker/docker/cli/flags" 14 "github.com/docker/docker/cliconfig" 15 "github.com/docker/docker/cliconfig/configfile" 16 "github.com/docker/docker/cliconfig/credentials" 17 "github.com/docker/docker/client" 18 "github.com/docker/docker/dockerversion" 19 dopts "github.com/docker/docker/opts" 20 "github.com/docker/go-connections/sockets" 21 "github.com/docker/go-connections/tlsconfig" 22 "golang.org/x/net/context" 23 ) 24 25 // Streams is an interface which exposes the standard input and output streams 26 type Streams interface { 27 In() *InStream 28 Out() *OutStream 29 Err() io.Writer 30 } 31 32 // DockerCli represents the docker command line client. 33 // Instances of the client can be returned from NewDockerCli. 34 type DockerCli struct { 35 configFile *configfile.ConfigFile 36 in *InStream 37 out *OutStream 38 err io.Writer 39 keyFile string 40 client client.APIClient 41 hasExperimental *bool 42 } 43 44 // HasExperimental returns true if experimental features are accessible 45 func (cli *DockerCli) HasExperimental() bool { 46 if cli.hasExperimental == nil { 47 if cli.client == nil { 48 cli.Initialize(cliflags.NewClientOptions()) 49 } 50 enabled := false 51 cli.hasExperimental = &enabled 52 enabled, _ = cli.client.Ping(context.Background()) 53 } 54 55 return *cli.hasExperimental 56 } 57 58 // Client returns the APIClient 59 func (cli *DockerCli) Client() client.APIClient { 60 return cli.client 61 } 62 63 // Out returns the writer used for stdout 64 func (cli *DockerCli) Out() *OutStream { 65 return cli.out 66 } 67 68 // Err returns the writer used for stderr 69 func (cli *DockerCli) Err() io.Writer { 70 return cli.err 71 } 72 73 // In returns the reader used for stdin 74 func (cli *DockerCli) In() *InStream { 75 return cli.in 76 } 77 78 // ConfigFile returns the ConfigFile 79 func (cli *DockerCli) ConfigFile() *configfile.ConfigFile { 80 return cli.configFile 81 } 82 83 // CredentialsStore returns a new credentials store based 84 // on the settings provided in the configuration file. 85 func (cli *DockerCli) CredentialsStore() credentials.Store { 86 if cli.configFile.CredentialsStore != "" { 87 return credentials.NewNativeStore(cli.configFile) 88 } 89 return credentials.NewFileStore(cli.configFile) 90 } 91 92 // Initialize the dockerCli runs initialization that must happen after command 93 // line flags are parsed. 94 func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { 95 cli.configFile = LoadDefaultConfigFile(cli.err) 96 97 var err error 98 cli.client, err = NewAPIClientFromFlags(opts.Common, cli.configFile) 99 if err != nil { 100 return err 101 } 102 if opts.Common.TrustKey == "" { 103 cli.keyFile = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile) 104 } else { 105 cli.keyFile = opts.Common.TrustKey 106 } 107 108 return nil 109 } 110 111 // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. 112 func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli { 113 return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err} 114 } 115 116 // LoadDefaultConfigFile attempts to load the default config file and returns 117 // an initialized ConfigFile struct if none is found. 118 func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile { 119 configFile, e := cliconfig.Load(cliconfig.ConfigDir()) 120 if e != nil { 121 fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) 122 } 123 if !configFile.ContainsAuth() { 124 credentials.DetectDefaultStore(configFile) 125 } 126 return configFile 127 } 128 129 // NewAPIClientFromFlags creates a new APIClient from command line flags 130 func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { 131 host, err := getServerHost(opts.Hosts, opts.TLSOptions) 132 if err != nil { 133 return &client.Client{}, err 134 } 135 136 customHeaders := configFile.HTTPHeaders 137 if customHeaders == nil { 138 customHeaders = map[string]string{} 139 } 140 customHeaders["User-Agent"] = UserAgent() 141 142 verStr := api.DefaultVersion 143 if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" { 144 verStr = tmpStr 145 } 146 147 httpClient, err := newHTTPClient(host, opts.TLSOptions) 148 if err != nil { 149 return &client.Client{}, err 150 } 151 152 return client.NewClient(host, verStr, httpClient, customHeaders) 153 } 154 155 func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string, err error) { 156 switch len(hosts) { 157 case 0: 158 host = os.Getenv("DOCKER_HOST") 159 case 1: 160 host = hosts[0] 161 default: 162 return "", errors.New("Please specify only one -H") 163 } 164 165 host, err = dopts.ParseHost(tlsOptions != nil, host) 166 return 167 } 168 169 func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) { 170 if tlsOptions == nil { 171 // let the api client configure the default transport. 172 return nil, nil 173 } 174 175 config, err := tlsconfig.Client(*tlsOptions) 176 if err != nil { 177 return nil, err 178 } 179 tr := &http.Transport{ 180 TLSClientConfig: config, 181 } 182 proto, addr, _, err := client.ParseHost(host) 183 if err != nil { 184 return nil, err 185 } 186 187 sockets.ConfigureTransport(tr, proto, addr) 188 189 return &http.Client{ 190 Transport: tr, 191 }, nil 192 } 193 194 // UserAgent returns the user agent string used for making API requests 195 func UserAgent() string { 196 return "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")" 197 }