Skip to content

Commit 7c3ec3e

Browse files
Merge pull request #6428 from thaJeztah/28.x_backport_avoid_client_types_in_opts
[28.x backport] don't wrap client options
2 parents 0ac676b + aa97619 commit 7c3ec3e

File tree

3 files changed

+71
-70
lines changed

3 files changed

+71
-70
lines changed

cli/command/cli.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,14 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
324324
if len(configFile.HTTPHeaders) > 0 {
325325
opts = append(opts, client.WithHTTPHeaders(configFile.HTTPHeaders))
326326
}
327-
opts = append(opts, withCustomHeadersFromEnv(), client.WithUserAgent(UserAgent()))
327+
withCustomHeaders, err := withCustomHeadersFromEnv()
328+
if err != nil {
329+
return nil, err
330+
}
331+
if withCustomHeaders != nil {
332+
opts = append(opts, withCustomHeaders)
333+
}
334+
opts = append(opts, client.WithUserAgent(UserAgent()))
328335
return client.NewClientWithOpts(opts...)
329336
}
330337

cli/command/cli_options.go

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -180,61 +180,59 @@ const envOverrideHTTPHeaders = "DOCKER_CUSTOM_HEADERS"
180180
// override headers with the same name).
181181
//
182182
// TODO(thaJeztah): this is a client Option, and should be moved to the client. It is non-exported for that reason.
183-
func withCustomHeadersFromEnv() client.Opt {
184-
return func(apiClient *client.Client) error {
185-
value := os.Getenv(envOverrideHTTPHeaders)
186-
if value == "" {
187-
return nil
188-
}
189-
csvReader := csv.NewReader(strings.NewReader(value))
190-
fields, err := csvReader.Read()
191-
if err != nil {
192-
return invalidParameter(errors.Errorf(
193-
"failed to parse custom headers from %s environment variable: value must be formatted as comma-separated key=value pairs",
194-
envOverrideHTTPHeaders,
195-
))
196-
}
197-
if len(fields) == 0 {
198-
return nil
199-
}
200-
201-
env := map[string]string{}
202-
for _, kv := range fields {
203-
k, v, hasValue := strings.Cut(kv, "=")
204-
205-
// Only strip whitespace in keys; preserve whitespace in values.
206-
k = strings.TrimSpace(k)
183+
func withCustomHeadersFromEnv() (client.Opt, error) {
184+
value := os.Getenv(envOverrideHTTPHeaders)
185+
if value == "" {
186+
return nil, nil
187+
}
188+
csvReader := csv.NewReader(strings.NewReader(value))
189+
fields, err := csvReader.Read()
190+
if err != nil {
191+
return nil, invalidParameter(errors.Errorf(
192+
"failed to parse custom headers from %s environment variable: value must be formatted as comma-separated key=value pairs",
193+
envOverrideHTTPHeaders,
194+
))
195+
}
196+
if len(fields) == 0 {
197+
return nil, nil
198+
}
207199

208-
if k == "" {
209-
return invalidParameter(errors.Errorf(
210-
`failed to set custom headers from %s environment variable: value contains a key=value pair with an empty key: '%s'`,
211-
envOverrideHTTPHeaders, kv,
212-
))
213-
}
200+
env := map[string]string{}
201+
for _, kv := range fields {
202+
k, v, hasValue := strings.Cut(kv, "=")
214203

215-
// We don't currently allow empty key=value pairs, and produce an error.
216-
// This is something we could allow in future (e.g. to read value
217-
// from an environment variable with the same name). In the meantime,
218-
// produce an error to prevent users from depending on this.
219-
if !hasValue {
220-
return invalidParameter(errors.Errorf(
221-
`failed to set custom headers from %s environment variable: missing "=" in key=value pair: '%s'`,
222-
envOverrideHTTPHeaders, kv,
223-
))
224-
}
204+
// Only strip whitespace in keys; preserve whitespace in values.
205+
k = strings.TrimSpace(k)
225206

226-
env[http.CanonicalHeaderKey(k)] = v
207+
if k == "" {
208+
return nil, invalidParameter(errors.Errorf(
209+
`failed to set custom headers from %s environment variable: value contains a key=value pair with an empty key: '%s'`,
210+
envOverrideHTTPHeaders, kv,
211+
))
227212
}
228213

229-
if len(env) == 0 {
230-
// We should probably not hit this case, as we don't skip values
231-
// (only return errors), but we don't want to discard existing
232-
// headers with an empty set.
233-
return nil
214+
// We don't currently allow empty key=value pairs, and produce an error.
215+
// This is something we could allow in future (e.g. to read value
216+
// from an environment variable with the same name). In the meantime,
217+
// produce an error to prevent users from depending on this.
218+
if !hasValue {
219+
return nil, invalidParameter(errors.Errorf(
220+
`failed to set custom headers from %s environment variable: missing "=" in key=value pair: '%s'`,
221+
envOverrideHTTPHeaders, kv,
222+
))
234223
}
235224

236-
// TODO(thaJeztah): add a client.WithExtraHTTPHeaders() function to allow these headers to be _added_ to existing ones, instead of _replacing_
237-
// see https://github.com/docker/cli/pull/5098#issuecomment-2147403871 (when updating, also update the WARNING in the function and env-var GoDoc)
238-
return client.WithHTTPHeaders(env)(apiClient)
225+
env[http.CanonicalHeaderKey(k)] = v
226+
}
227+
228+
if len(env) == 0 {
229+
// We should probably not hit this case, as we don't skip values
230+
// (only return errors), but we don't want to discard existing
231+
// headers with an empty set.
232+
return nil, nil
239233
}
234+
235+
// TODO(thaJeztah): add a client.WithExtraHTTPHeaders() function to allow these headers to be _added_ to existing ones, instead of _replacing_
236+
// see https://github.com/docker/cli/pull/5098#issuecomment-2147403871 (when updating, also update the WARNING in the function and env-var GoDoc)
237+
return client.WithHTTPHeaders(env), nil
240238
}

cli/context/docker/load.go

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,22 @@ func (ep *Endpoint) ClientOpts() ([]client.Opt, error) {
101101
if err != nil {
102102
return nil, err
103103
}
104-
result = append(result, withHTTPClient(tlsConfig))
104+
105+
// If there's no tlsConfig available, we use the default HTTPClient.
106+
if tlsConfig != nil {
107+
result = append(result,
108+
client.WithHTTPClient(&http.Client{
109+
Transport: &http.Transport{
110+
TLSClientConfig: tlsConfig,
111+
DialContext: (&net.Dialer{
112+
KeepAlive: 30 * time.Second,
113+
Timeout: 30 * time.Second,
114+
}).DialContext,
115+
},
116+
CheckRedirect: client.CheckRedirect,
117+
}),
118+
)
119+
}
105120
}
106121
result = append(result, client.WithHost(ep.Host))
107122
} else {
@@ -133,25 +148,6 @@ func isSocket(addr string) bool {
133148
}
134149
}
135150

136-
func withHTTPClient(tlsConfig *tls.Config) func(*client.Client) error {
137-
return func(c *client.Client) error {
138-
if tlsConfig == nil {
139-
// Use the default HTTPClient
140-
return nil
141-
}
142-
return client.WithHTTPClient(&http.Client{
143-
Transport: &http.Transport{
144-
TLSClientConfig: tlsConfig,
145-
DialContext: (&net.Dialer{
146-
KeepAlive: 30 * time.Second,
147-
Timeout: 30 * time.Second,
148-
}).DialContext,
149-
},
150-
CheckRedirect: client.CheckRedirect,
151-
})(c)
152-
}
153-
}
154-
155151
// EndpointFromContext parses a context docker endpoint metadata into a typed EndpointMeta structure
156152
func EndpointFromContext(metadata store.Metadata) (EndpointMeta, error) {
157153
ep, ok := metadata.Endpoints[DockerEndpoint]

0 commit comments

Comments
 (0)