diff --git a/auth.go b/auth.go index 27127001..a6e2fa94 100644 --- a/auth.go +++ b/auth.go @@ -55,10 +55,23 @@ func (c AuthConfigurations) isEmpty() bool { return len(c.Configs) == 0 } -func (c AuthConfigurations) headerKey() string { +func (AuthConfigurations) headerKey() string { return "X-Registry-Config" } +// merge updates the configuration. If a key is defined in both maps, the one +// in c.Configs takes precedence. +func (c *AuthConfigurations) merge(other AuthConfigurations) { + for k, v := range other.Configs { + if c.Configs == nil { + c.Configs = make(map[string]AuthConfiguration) + } + if _, ok := c.Configs[k]; !ok { + c.Configs[k] = v + } + } +} + // AuthConfigurations119 is used to serialize a set of AuthConfigurations // for Docker API >= 1.19. type AuthConfigurations119 map[string]AuthConfiguration @@ -119,6 +132,13 @@ func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) { var err error for _, path := range pathsToTry { auths, err = NewAuthConfigurationsFromFile(path) + if err != nil { + continue + } + + if !auths.isEmpty() { + return auths, nil + } if err == nil { return auths, nil } diff --git a/auth_test.go b/auth_test.go index 999fc185..bd1c4144 100644 --- a/auth_test.go +++ b/auth_test.go @@ -11,6 +11,7 @@ import ( "net/http" "os" "path" + "reflect" "strings" "testing" ) @@ -237,3 +238,95 @@ func TestAuthCheck(t *testing.T) { t.Fatal("expected failure from unauthorized auth") } } + +func TestAuthConfigurationsMerge(t *testing.T) { + t.Parallel() + var tests = []struct { + name string + left AuthConfigurations + right AuthConfigurations + expected AuthConfigurations + }{ + { + name: "empty configs", + expected: AuthConfigurations{}, + }, + { + name: "empty left config", + right: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + }, + }, + expected: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + }, + }, + }, + { + name: "empty right config", + left: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + }, + }, + expected: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + }, + }, + }, + { + name: "no conflicts", + left: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + }, + }, + right: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "us.gcr.io": {Email: "user@google.com"}, + }, + }, + expected: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + "us.gcr.io": {Email: "user@google.com"}, + }, + }, + }, + { + name: "no conflicts", + left: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + "us.gcr.io": {Email: "google-user@example.com"}, + }, + }, + right: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "us.gcr.io": {Email: "user@google.com"}, + }, + }, + expected: AuthConfigurations{ + Configs: map[string]AuthConfiguration{ + "docker.io": {Email: "user@example.com"}, + "us.gcr.io": {Email: "google-user@example.com"}, + }, + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + test.left.merge(test.right) + + if !reflect.DeepEqual(test.left, test.expected) { + t.Errorf("wrong configuration map after merge\nwant %#v\ngot %#v", test.expected, test.left) + } + }) + } +}