88 "path/filepath"
99 "runtime"
1010 "strconv"
11+ "strings"
12+ "sync"
13+ "time"
1114
1215 "github.com/docker/cli/cli/config"
1316 cliconfig "github.com/docker/cli/cli/config"
@@ -25,6 +28,7 @@ import (
2528 "github.com/docker/cli/internal/containerizedengine"
2629 dopts "github.com/docker/cli/opts"
2730 clitypes "github.com/docker/cli/types"
31+ "github.com/docker/docker/api"
2832 "github.com/docker/docker/api/types"
2933 registrytypes "github.com/docker/docker/api/types/registry"
3034 "github.com/docker/docker/client"
@@ -76,7 +80,7 @@ type DockerCli struct {
7680 err io.Writer
7781 client client.APIClient
7882 serverInfo ServerInfo
79- clientInfo ClientInfo
83+ clientInfo * ClientInfo
8084 contentTrust bool
8185 newContainerizeClient func (string ) (clitypes.ContainerizedClient , error )
8286 contextStore store.Store
@@ -87,7 +91,7 @@ type DockerCli struct {
8791
8892// DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified.
8993func (cli * DockerCli ) DefaultVersion () string {
90- return cli .clientInfo .DefaultVersion
94+ return cli .ClientInfo () .DefaultVersion
9195}
9296
9397// Client returns the APIClient
@@ -126,18 +130,55 @@ func ShowHelp(err io.Writer) func(*cobra.Command, []string) error {
126130
127131// ConfigFile returns the ConfigFile
128132func (cli * DockerCli ) ConfigFile () * configfile.ConfigFile {
133+ if cli .configFile == nil {
134+ cli .loadConfigFile ()
135+ }
129136 return cli .configFile
130137}
131138
139+ func (cli * DockerCli ) loadConfigFile () {
140+ cli .configFile = cliconfig .LoadDefaultConfigFile (cli .err )
141+ }
142+
143+ var fetchServerInfo sync.Once
144+
132145// ServerInfo returns the server version details for the host this client is
133146// connected to
134147func (cli * DockerCli ) ServerInfo () ServerInfo {
148+ fetchServerInfo .Do (cli .initializeFromClient )
135149 return cli .serverInfo
136150}
137151
138152// ClientInfo returns the client details for the cli
139153func (cli * DockerCli ) ClientInfo () ClientInfo {
140- return cli .clientInfo
154+ if cli .clientInfo == nil {
155+ _ = cli .loadClientInfo ()
156+ }
157+ return * cli .clientInfo
158+ }
159+
160+ func (cli * DockerCli ) loadClientInfo () error {
161+ var experimentalValue string
162+ // Environment variable always overrides configuration
163+ if experimentalValue = os .Getenv ("DOCKER_CLI_EXPERIMENTAL" ); experimentalValue == "" {
164+ experimentalValue = cli .ConfigFile ().Experimental
165+ }
166+ hasExperimental , err := isEnabled (experimentalValue )
167+ if err != nil {
168+ return errors .Wrap (err , "Experimental field" )
169+ }
170+
171+ var v string
172+ if cli .client != nil {
173+ v = cli .client .ClientVersion ()
174+ } else {
175+ v = api .DefaultVersion
176+ }
177+ cli .clientInfo = & ClientInfo {
178+ DefaultVersion : v ,
179+ HasExperimental : hasExperimental ,
180+ }
181+ return nil
141182}
142183
143184// ContentTrustEnabled returns whether content trust has been enabled by an
@@ -207,7 +248,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
207248 debug .Enable ()
208249 }
209250
210- cli .configFile = cliconfig . LoadDefaultConfigFile ( cli . err )
251+ cli .loadConfigFile ( )
211252
212253 baseContextStore := store .New (cliconfig .ContextStoreDir (), cli .contextStoreConfig )
213254 cli .contextStore = & ContextStoreWithDefault {
@@ -239,20 +280,6 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
239280 return err
240281 }
241282 }
242- var experimentalValue string
243- // Environment variable always overrides configuration
244- if experimentalValue = os .Getenv ("DOCKER_CLI_EXPERIMENTAL" ); experimentalValue == "" {
245- experimentalValue = cli .configFile .Experimental
246- }
247- hasExperimental , err := isEnabled (experimentalValue )
248- if err != nil {
249- return errors .Wrap (err , "Experimental field" )
250- }
251- cli .clientInfo = ClientInfo {
252- DefaultVersion : cli .client .ClientVersion (),
253- HasExperimental : hasExperimental ,
254- }
255- cli .initializeFromClient ()
256283 return nil
257284}
258285
@@ -343,7 +370,16 @@ func isEnabled(value string) (bool, error) {
343370}
344371
345372func (cli * DockerCli ) initializeFromClient () {
346- ping , err := cli .client .Ping (context .Background ())
373+ ctx := context .Background ()
374+ if strings .HasPrefix (cli .DockerEndpoint ().Host , "tcp://" ) {
375+ // @FIXME context.WithTimeout doesn't work with connhelper / ssh connections
376+ // time="2020-04-10T10:16:26Z" level=warning msg="commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
377+ var cancel func ()
378+ ctx , cancel = context .WithTimeout (ctx , 2 * time .Second )
379+ defer cancel ()
380+ }
381+
382+ ping , err := cli .client .Ping (ctx )
347383 if err != nil {
348384 // Default to true if we fail to connect to daemon
349385 cli .serverInfo = ServerInfo {HasExperimental : true }
0 commit comments