github.com/rish1988/moby@v25.0.2+incompatible/client/options.go (about) 1 package client 2 3 import ( 4 "context" 5 "net" 6 "net/http" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/docker/go-connections/sockets" 12 "github.com/docker/go-connections/tlsconfig" 13 "github.com/pkg/errors" 14 "go.opentelemetry.io/otel/trace" 15 ) 16 17 // Opt is a configuration option to initialize a [Client]. 18 type Opt func(*Client) error 19 20 // FromEnv configures the client with values from environment variables. It 21 // is the equivalent of using the [WithTLSClientConfigFromEnv], [WithHostFromEnv], 22 // and [WithVersionFromEnv] options. 23 // 24 // FromEnv uses the following environment variables: 25 // 26 // - DOCKER_HOST ([EnvOverrideHost]) to set the URL to the docker server. 27 // - DOCKER_API_VERSION ([EnvOverrideAPIVersion]) to set the version of the 28 // API to use, leave empty for latest. 29 // - DOCKER_CERT_PATH ([EnvOverrideCertPath]) to specify the directory from 30 // which to load the TLS certificates ("ca.pem", "cert.pem", "key.pem'). 31 // - DOCKER_TLS_VERIFY ([EnvTLSVerify]) to enable or disable TLS verification 32 // (off by default). 33 func FromEnv(c *Client) error { 34 ops := []Opt{ 35 WithTLSClientConfigFromEnv(), 36 WithHostFromEnv(), 37 WithVersionFromEnv(), 38 } 39 for _, op := range ops { 40 if err := op(c); err != nil { 41 return err 42 } 43 } 44 return nil 45 } 46 47 // WithDialContext applies the dialer to the client transport. This can be 48 // used to set the Timeout and KeepAlive settings of the client. It returns 49 // an error if the client does not have a [http.Transport] configured. 50 func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) Opt { 51 return func(c *Client) error { 52 if transport, ok := c.client.Transport.(*http.Transport); ok { 53 transport.DialContext = dialContext 54 return nil 55 } 56 return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport) 57 } 58 } 59 60 // WithHost overrides the client host with the specified one. 61 func WithHost(host string) Opt { 62 return func(c *Client) error { 63 hostURL, err := ParseHostURL(host) 64 if err != nil { 65 return err 66 } 67 c.host = host 68 c.proto = hostURL.Scheme 69 c.addr = hostURL.Host 70 c.basePath = hostURL.Path 71 if transport, ok := c.client.Transport.(*http.Transport); ok { 72 return sockets.ConfigureTransport(transport, c.proto, c.addr) 73 } 74 return errors.Errorf("cannot apply host to transport: %T", c.client.Transport) 75 } 76 } 77 78 // WithHostFromEnv overrides the client host with the host specified in the 79 // DOCKER_HOST ([EnvOverrideHost]) environment variable. If DOCKER_HOST is not set, 80 // or set to an empty value, the host is not modified. 81 func WithHostFromEnv() Opt { 82 return func(c *Client) error { 83 if host := os.Getenv(EnvOverrideHost); host != "" { 84 return WithHost(host)(c) 85 } 86 return nil 87 } 88 } 89 90 // WithHTTPClient overrides the client's HTTP client with the specified one. 91 func WithHTTPClient(client *http.Client) Opt { 92 return func(c *Client) error { 93 if client != nil { 94 c.client = client 95 } 96 return nil 97 } 98 } 99 100 // WithTimeout configures the time limit for requests made by the HTTP client. 101 func WithTimeout(timeout time.Duration) Opt { 102 return func(c *Client) error { 103 c.client.Timeout = timeout 104 return nil 105 } 106 } 107 108 // WithUserAgent configures the User-Agent header to use for HTTP requests. 109 // It overrides any User-Agent set in headers. When set to an empty string, 110 // the User-Agent header is removed, and no header is sent. 111 func WithUserAgent(ua string) Opt { 112 return func(c *Client) error { 113 c.userAgent = &ua 114 return nil 115 } 116 } 117 118 // WithHTTPHeaders appends custom HTTP headers to the client's default headers. 119 // It does not allow for built-in headers (such as "User-Agent", if set) to 120 // be overridden. Also see [WithUserAgent]. 121 func WithHTTPHeaders(headers map[string]string) Opt { 122 return func(c *Client) error { 123 c.customHTTPHeaders = headers 124 return nil 125 } 126 } 127 128 // WithScheme overrides the client scheme with the specified one. 129 func WithScheme(scheme string) Opt { 130 return func(c *Client) error { 131 c.scheme = scheme 132 return nil 133 } 134 } 135 136 // WithTLSClientConfig applies a TLS config to the client transport. 137 func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt { 138 return func(c *Client) error { 139 transport, ok := c.client.Transport.(*http.Transport) 140 if !ok { 141 return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport) 142 } 143 config, err := tlsconfig.Client(tlsconfig.Options{ 144 CAFile: cacertPath, 145 CertFile: certPath, 146 KeyFile: keyPath, 147 ExclusiveRootPools: true, 148 }) 149 if err != nil { 150 return errors.Wrap(err, "failed to create tls config") 151 } 152 transport.TLSClientConfig = config 153 return nil 154 } 155 } 156 157 // WithTLSClientConfigFromEnv configures the client's TLS settings with the 158 // settings in the DOCKER_CERT_PATH ([EnvOverrideCertPath]) and DOCKER_TLS_VERIFY 159 // ([EnvTLSVerify]) environment variables. If DOCKER_CERT_PATH is not set or empty, 160 // TLS configuration is not modified. 161 // 162 // WithTLSClientConfigFromEnv uses the following environment variables: 163 // 164 // - DOCKER_CERT_PATH ([EnvOverrideCertPath]) to specify the directory from 165 // which to load the TLS certificates ("ca.pem", "cert.pem", "key.pem"). 166 // - DOCKER_TLS_VERIFY ([EnvTLSVerify]) to enable or disable TLS verification 167 // (off by default). 168 func WithTLSClientConfigFromEnv() Opt { 169 return func(c *Client) error { 170 dockerCertPath := os.Getenv(EnvOverrideCertPath) 171 if dockerCertPath == "" { 172 return nil 173 } 174 tlsc, err := tlsconfig.Client(tlsconfig.Options{ 175 CAFile: filepath.Join(dockerCertPath, "ca.pem"), 176 CertFile: filepath.Join(dockerCertPath, "cert.pem"), 177 KeyFile: filepath.Join(dockerCertPath, "key.pem"), 178 InsecureSkipVerify: os.Getenv(EnvTLSVerify) == "", 179 }) 180 if err != nil { 181 return err 182 } 183 184 c.client = &http.Client{ 185 Transport: &http.Transport{TLSClientConfig: tlsc}, 186 CheckRedirect: CheckRedirect, 187 } 188 return nil 189 } 190 } 191 192 // WithVersion overrides the client version with the specified one. If an empty 193 // version is provided, the value is ignored to allow version negotiation 194 // (see [WithAPIVersionNegotiation]). 195 func WithVersion(version string) Opt { 196 return func(c *Client) error { 197 if version != "" { 198 c.version = version 199 c.manualOverride = true 200 } 201 return nil 202 } 203 } 204 205 // WithVersionFromEnv overrides the client version with the version specified in 206 // the DOCKER_API_VERSION ([EnvOverrideAPIVersion]) environment variable. 207 // If DOCKER_API_VERSION is not set, or set to an empty value, the version 208 // is not modified. 209 func WithVersionFromEnv() Opt { 210 return func(c *Client) error { 211 return WithVersion(os.Getenv(EnvOverrideAPIVersion))(c) 212 } 213 } 214 215 // WithAPIVersionNegotiation enables automatic API version negotiation for the client. 216 // With this option enabled, the client automatically negotiates the API version 217 // to use when making requests. API version negotiation is performed on the first 218 // request; subsequent requests do not re-negotiate. 219 func WithAPIVersionNegotiation() Opt { 220 return func(c *Client) error { 221 c.negotiateVersion = true 222 return nil 223 } 224 } 225 226 // WithTraceProvider sets the trace provider for the client. 227 // If this is not set then the global trace provider will be used. 228 func WithTraceProvider(provider trace.TracerProvider) Opt { 229 return func(c *Client) error { 230 c.tp = provider 231 return nil 232 } 233 }