github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/client/client.go (about) 1 /* 2 Package client is a Go client for the Docker Remote API. 3 4 The "docker" command uses this package to communicate with the daemon. It can also 5 be used by your own Go applications to do anything the command-line interface does 6 – running containers, pulling images, managing swarms, etc. 7 8 For more information about the Remote API, see the documentation: 9 https://docs.docker.com/engine/reference/api/docker_remote_api/ 10 11 Usage 12 13 You use the library by creating a client object and calling methods on it. The 14 client can be created either from environment variables with NewEnvClient, or 15 configured manually with NewClient. 16 17 For example, to list running containers (the equivalent of "docker ps"): 18 19 package main 20 21 import ( 22 "context" 23 "fmt" 24 25 "github.com/docker/docker/api/types" 26 "github.com/docker/docker/client" 27 ) 28 29 func main() { 30 cli, err := client.NewEnvClient() 31 if err != nil { 32 panic(err) 33 } 34 35 containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) 36 if err != nil { 37 panic(err) 38 } 39 40 for _, container := range containers { 41 fmt.Printf("%s %s\n", container.ID[:10], container.Image) 42 } 43 } 44 45 */ 46 package client 47 48 import ( 49 "fmt" 50 "net/http" 51 "net/url" 52 "os" 53 "path/filepath" 54 "strings" 55 56 "github.com/docker/go-connections/sockets" 57 "github.com/docker/go-connections/tlsconfig" 58 ) 59 60 // DefaultVersion is the version of the current stable API 61 const DefaultVersion string = "1.23" 62 63 // Client is the API client that performs all operations 64 // against a docker server. 65 type Client struct { 66 // scheme sets the scheme for the client 67 scheme string 68 // host holds the server address to connect to 69 host string 70 // proto holds the client protocol i.e. unix. 71 proto string 72 // addr holds the client address. 73 addr string 74 // basePath holds the path to prepend to the requests. 75 basePath string 76 // client used to send and receive http requests. 77 client *http.Client 78 // version of the server to talk to. 79 version string 80 // custom http headers configured by users. 81 customHTTPHeaders map[string]string 82 } 83 84 // NewEnvClient initializes a new API client based on environment variables. 85 // Use DOCKER_HOST to set the url to the docker server. 86 // Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. 87 // Use DOCKER_CERT_PATH to load the tls certificates from. 88 // Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. 89 func NewEnvClient() (*Client, error) { 90 var client *http.Client 91 if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { 92 options := tlsconfig.Options{ 93 CAFile: filepath.Join(dockerCertPath, "ca.pem"), 94 CertFile: filepath.Join(dockerCertPath, "cert.pem"), 95 KeyFile: filepath.Join(dockerCertPath, "key.pem"), 96 InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", 97 } 98 tlsc, err := tlsconfig.Client(options) 99 if err != nil { 100 return nil, err 101 } 102 103 client = &http.Client{ 104 Transport: &http.Transport{ 105 TLSClientConfig: tlsc, 106 }, 107 } 108 } 109 110 host := os.Getenv("DOCKER_HOST") 111 if host == "" { 112 host = DefaultDockerHost 113 } 114 115 version := os.Getenv("DOCKER_API_VERSION") 116 if version == "" { 117 version = DefaultVersion 118 } 119 120 return NewClient(host, version, client, nil) 121 } 122 123 // NewClient initializes a new API client for the given host and API version. 124 // It uses the given http client as transport. 125 // It also initializes the custom http headers to add to each request. 126 // 127 // It won't send any version information if the version number is empty. It is 128 // highly recommended that you set a version or your client may break if the 129 // server is upgraded. 130 func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { 131 proto, addr, basePath, err := ParseHost(host) 132 if err != nil { 133 return nil, err 134 } 135 136 if client != nil { 137 if _, ok := client.Transport.(*http.Transport); !ok { 138 return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", client.Transport) 139 } 140 } else { 141 transport := new(http.Transport) 142 sockets.ConfigureTransport(transport, proto, addr) 143 client = &http.Client{ 144 Transport: transport, 145 } 146 } 147 148 scheme := "http" 149 tlsConfig := resolveTLSConfig(client.Transport) 150 if tlsConfig != nil { 151 // TODO(stevvooe): This isn't really the right way to write clients in Go. 152 // `NewClient` should probably only take an `*http.Client` and work from there. 153 // Unfortunately, the model of having a host-ish/url-thingy as the connection 154 // string has us confusing protocol and transport layers. We continue doing 155 // this to avoid breaking existing clients but this should be addressed. 156 scheme = "https" 157 } 158 159 return &Client{ 160 scheme: scheme, 161 host: host, 162 proto: proto, 163 addr: addr, 164 basePath: basePath, 165 client: client, 166 version: version, 167 customHTTPHeaders: httpHeaders, 168 }, nil 169 } 170 171 // Close ensures that transport.Client is closed 172 // especially needed while using NewClient with *http.Client = nil 173 // for example 174 // client.NewClient("unix:///var/run/docker.sock", nil, "v1.18", map[string]string{"User-Agent": "engine-api-cli-1.0"}) 175 func (cli *Client) Close() error { 176 177 if t, ok := cli.client.Transport.(*http.Transport); ok { 178 t.CloseIdleConnections() 179 } 180 181 return nil 182 } 183 184 // getAPIPath returns the versioned request path to call the api. 185 // It appends the query parameters to the path if they are not empty. 186 func (cli *Client) getAPIPath(p string, query url.Values) string { 187 var apiPath string 188 if cli.version != "" { 189 v := strings.TrimPrefix(cli.version, "v") 190 apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p) 191 } else { 192 apiPath = fmt.Sprintf("%s%s", cli.basePath, p) 193 } 194 195 u := &url.URL{ 196 Path: apiPath, 197 } 198 if len(query) > 0 { 199 u.RawQuery = query.Encode() 200 } 201 return u.String() 202 } 203 204 // ClientVersion returns the version string associated with this 205 // instance of the Client. Note that this value can be changed 206 // via the DOCKER_API_VERSION env var. 207 func (cli *Client) ClientVersion() string { 208 return cli.version 209 } 210 211 // UpdateClientVersion updates the version string associated with this 212 // instance of the Client. 213 func (cli *Client) UpdateClientVersion(v string) { 214 cli.version = v 215 } 216 217 // ParseHost verifies that the given host strings is valid. 218 func ParseHost(host string) (string, string, string, error) { 219 protoAddrParts := strings.SplitN(host, "://", 2) 220 if len(protoAddrParts) == 1 { 221 return "", "", "", fmt.Errorf("unable to parse docker host `%s`", host) 222 } 223 224 var basePath string 225 proto, addr := protoAddrParts[0], protoAddrParts[1] 226 if proto == "tcp" { 227 parsed, err := url.Parse("tcp://" + addr) 228 if err != nil { 229 return "", "", "", err 230 } 231 addr = parsed.Host 232 basePath = parsed.Path 233 } 234 return proto, addr, basePath, nil 235 }