@@ -23,24 +23,48 @@ import (
2323// defaultRegistryFn is variable overwritten in tests to check for behaviour with different default values.
2424var defaultRegistryFn = defaultRegistry
2525
26- // getRegistryCredentials is a variable overwritten in tests to mock the dockercfg.GetRegistryCredentials function.
27- var getRegistryCredentials = dockercfg .GetRegistryCredentials
26+ // getRegistryCredentials is a variable overwritten in tests to mock the credentials lookup.
27+ // It resolves against the config already loaded, which honours DOCKER_AUTH_CONFIG, rather than
28+ // reloading the default config file.
29+ var getRegistryCredentials = func (cfg * dockercfg.Config , hostname string ) (string , string , error ) {
30+ return cfg .GetRegistryCredentials (hostname )
31+ }
2832
2933// DockerImageAuth returns the auth config for the given Docker image, extracting first its Docker registry.
3034// Finally, it will use the credential helpers to extract the information from the docker config file
3135// for that registry, if it exists.
3236func DockerImageAuth (ctx context.Context , image string ) (string , registry.AuthConfig , error ) {
33- configs , err := getDockerAuthConfigs ()
37+ cfg , configs , err := loadDockerAuth ()
3438 if err != nil {
3539 reg := core .ExtractRegistry (image , defaultRegistryFn (ctx ))
3640 return reg , registry.AuthConfig {}, err
3741 }
3842
39- return dockerImageAuth (ctx , image , configs )
43+ return dockerImageAuth (ctx , image , cfg , configs )
44+ }
45+
46+ // loadDockerAuth loads the docker config once and the auth configs derived from it,
47+ // so that both the map lookup and the credentials store lookup see the same config.
48+ // A missing config file is not an error: it yields an empty config.
49+ func loadDockerAuth () (* dockercfg.Config , map [string ]registry.AuthConfig , error ) {
50+ cfg , err := getDockerConfig ()
51+ if err != nil {
52+ if ! errors .Is (err , os .ErrNotExist ) {
53+ return nil , nil , err
54+ }
55+ cfg = & dockercfg.Config {}
56+ }
57+
58+ configs , err := getDockerAuthConfigsFromConfig (cfg )
59+ if err != nil {
60+ return nil , nil , err
61+ }
62+
63+ return cfg , configs , nil
4064}
4165
4266// dockerImageAuth returns the auth config for the given Docker image.
43- func dockerImageAuth (ctx context.Context , image string , configs map [string ]registry.AuthConfig ) (string , registry.AuthConfig , error ) {
67+ func dockerImageAuth (ctx context.Context , image string , cfg * dockercfg. Config , configs map [string ]registry.AuthConfig ) (string , registry.AuthConfig , error ) {
4468 defaultRegistry := defaultRegistryFn (ctx )
4569 reg := core .ExtractRegistry (image , defaultRegistry )
4670
@@ -51,12 +75,16 @@ func dockerImageAuth(ctx context.Context, image string, configs map[string]regis
5175 reg = defaultRegistry // This is https://index.docker.io/v1/
5276 }
5377
54- if cfg , ok := getRegistryAuth (reg , configs ); ok {
55- return reg , cfg , nil
78+ if ac , ok := getRegistryAuth (reg , configs ); ok {
79+ return reg , ac , nil
5680 }
5781
58- if cfg , ok := credentialsStoreAuth (reg ); ok {
59- return reg , cfg , nil
82+ ac , ok , err := credentialsStoreAuth (cfg , reg )
83+ if err != nil {
84+ return reg , registry.AuthConfig {}, err
85+ }
86+ if ok {
87+ return reg , ac , nil
6088 }
6189
6290 return reg , registry.AuthConfig {}, dockercfg .ErrCredentialsNotFound
@@ -68,28 +96,29 @@ func dockerImageAuth(ctx context.Context, image string, configs map[string]regis
6896// A credentials store serves every registry, so unlike auths and credHelpers it has
6997// no entries to enumerate up front: it can only be asked once the registry is known.
7098// See https://docs.docker.com/reference/cli/docker/login/#credential-stores
71- func credentialsStoreAuth (reg string ) (registry.AuthConfig , bool ) {
72- cfg , err := getDockerConfig ()
73- if err != nil || cfg .CredentialsStore == "" {
74- return registry.AuthConfig {}, false
99+ func credentialsStoreAuth (cfg * dockercfg.Config , reg string ) (registry.AuthConfig , bool , error ) {
100+ // A store cannot be asked about an empty host, and a missing helper binary
101+ // already reads as "no credentials" further down.
102+ if cfg .CredentialsStore == "" || reg == "" {
103+ return registry.AuthConfig {}, false , nil
75104 }
76105
77106 key , err := configKey (cfg )
78107 if err != nil {
79- return registry.AuthConfig {}, false
108+ return registry.AuthConfig {}, false , err
80109 }
81110
82111 var ac registry.AuthConfig
83- if err := creds .AuthConfig (reg , key , & ac ); err != nil {
84- return registry.AuthConfig {}, false
112+ if err := creds .AuthConfig (cfg , reg , key , & ac ); err != nil {
113+ return registry.AuthConfig {}, false , err
85114 }
86115
87116 // The store reports an unknown registry as empty credentials rather than an error.
88117 if ac .Username == "" && ac .Password == "" && ac .IdentityToken == "" {
89- return registry.AuthConfig {}, false
118+ return registry.AuthConfig {}, false , nil
90119 }
91120
92- return ac , true
121+ return ac , true , nil
93122}
94123
95124func getRegistryAuth (reg string , cfgs map [string ]registry.AuthConfig ) (registry.AuthConfig , bool ) {
@@ -159,8 +188,8 @@ var creds = &credentialsCache{entries: map[string]credentials{}}
159188
160189// AuthConfig updates the details in authConfig for the given hostname
161190// as determined by the details in configKey.
162- func (c * credentialsCache ) AuthConfig (hostname , configKey string , authConfig * registry.AuthConfig ) error {
163- u , p , err := creds .get (hostname , configKey )
191+ func (c * credentialsCache ) AuthConfig (cfg * dockercfg. Config , hostname , configKey string , authConfig * registry.AuthConfig ) error {
192+ u , p , err := creds .get (cfg , hostname , configKey )
164193 if err != nil {
165194 return err
166195 }
@@ -178,7 +207,7 @@ func (c *credentialsCache) AuthConfig(hostname, configKey string, authConfig *re
178207// get returns the username and password for the given hostname
179208// as determined by the details in configPath.
180209// If the username is empty, the password is an identity token.
181- func (c * credentialsCache ) get (hostname , configKey string ) (string , string , error ) {
210+ func (c * credentialsCache ) get (cfg * dockercfg. Config , hostname , configKey string ) (string , string , error ) {
182211 key := configKey + ":" + hostname
183212 c .mtx .RLock ()
184213 entry , ok := c .entries [key ]
@@ -189,7 +218,7 @@ func (c *credentialsCache) get(hostname, configKey string) (string, string, erro
189218 }
190219
191220 // No entry found, request and cache.
192- user , password , err := getRegistryCredentials (hostname )
221+ user , password , err := getRegistryCredentials (cfg , hostname )
193222 if err != nil {
194223 return "" , "" , fmt .Errorf ("getting credentials for %s: %w" , hostname , err )
195224 }
@@ -224,6 +253,12 @@ func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
224253 return nil , err
225254 }
226255
256+ return getDockerAuthConfigsFromConfig (cfg )
257+ }
258+
259+ // getDockerAuthConfigsFromConfig returns a map with the auth configs from the given docker config
260+ // using the registry as the key
261+ func getDockerAuthConfigsFromConfig (cfg * dockercfg.Config ) (map [string ]registry.AuthConfig , error ) {
227262 key , err := configKey (cfg )
228263 if err != nil {
229264 return nil , err
@@ -250,7 +285,7 @@ func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
250285 switch {
251286 case ac .Username == "" && ac .Password == "" :
252287 // Look up credentials from the credential store.
253- if err := creds .AuthConfig (k , key , & ac ); err != nil {
288+ if err := creds .AuthConfig (cfg , k , key , & ac ); err != nil {
254289 results <- authConfigResult {err : err }
255290 return
256291 }
@@ -270,7 +305,7 @@ func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
270305 defer wg .Done ()
271306
272307 var ac registry.AuthConfig
273- if err := creds .AuthConfig (k , key , & ac ); err != nil {
308+ if err := creds .AuthConfig (cfg , k , key , & ac ); err != nil {
274309 results <- authConfigResult {err : err }
275310 return
276311 }
0 commit comments