Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 6 additions & 23 deletions config/auth.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package config

import (
"encoding/base64"
"encoding/json"
"fmt"

"github.com/docker/docker/api/types/registry"
Expand All @@ -16,7 +14,7 @@ const tokenUsername = "<token>"
// AuthConfigs returns the auth configs for the given images.
// The images slice must contain images that are used in a Dockerfile.
// The returned map is keyed by the registry registry hostname for each image.
func AuthConfigs(images ...string) (map[string]AuthConfig, error) {
func AuthConfigs(images ...string) (map[string]registry.AuthConfig, error) {
cfg, err := Load()
if err != nil {
return nil, fmt.Errorf("load config: %w", err)
Expand All @@ -29,31 +27,16 @@ func AuthConfigs(images ...string) (map[string]AuthConfig, error) {
//
// This will use [Load] to read registry auth details from the config.
// If the config doesn't exist, it will attempt to load registry credentials using the default credential helper for the platform.
func AuthConfigForHostname(hostname string) (AuthConfig, error) {
func AuthConfigForHostname(hostname string) (registry.AuthConfig, error) {
cfg, err := Load()
if err != nil {
return AuthConfig{}, fmt.Errorf("load config: %w", err)
return registry.AuthConfig{}, fmt.Errorf("load config: %w", err)
}

return cfg.AuthConfigForHostname(hostname)
}

func (authConfig AuthConfig) EncodeBase64() (string, error) {
jsonAuth, err := json.Marshal(authConfig)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(jsonAuth), nil
}

func (authConfig AuthConfig) ToRegistryAuthConfig() registry.AuthConfig {
return registry.AuthConfig{
Username: authConfig.Username,
Password: authConfig.Password,
Auth: authConfig.Auth,
Email: "",
ServerAddress: authConfig.ServerAddress,
IdentityToken: authConfig.IdentityToken,
RegistryToken: authConfig.RegistryToken,
}
// EncodeBase64 encodes an AuthConfig into base64.
Comment thread
mdelapenya marked this conversation as resolved.
func EncodeBase64(authConfig registry.AuthConfig) (string, error) {
return registry.EncodeAuthConfig(authConfig)
}
20 changes: 11 additions & 9 deletions config/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/docker/docker/api/types/registry"
)

func TestDecodeBase64Auth(t *testing.T) {
for _, tc := range base64TestCases() {
t.Run(tc.name, testBase64Case(tc, func() (AuthConfig, error) {
t.Run(tc.name, testBase64Case(tc, func() (registry.AuthConfig, error) {
user, pass, err := decodeBase64Auth(tc.config)
return AuthConfig{
return registry.AuthConfig{
Username: user,
Password: pass,
}, err
Expand All @@ -29,11 +31,11 @@ func TestConfig_RegistryCredentialsForHostname(t *testing.T) {
for _, tc := range base64TestCases() {
t.Run(tc.name, func(t *testing.T) {
config := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"some.domain": tc.config,
},
}
testBase64Case(tc, func() (AuthConfig, error) {
testBase64Case(tc, func() (registry.AuthConfig, error) {
return config.AuthConfigForHostname("some.domain")
})(t)
})
Expand All @@ -43,7 +45,7 @@ func TestConfig_RegistryCredentialsForHostname(t *testing.T) {

type base64TestCase struct {
name string
config AuthConfig
config registry.AuthConfig
expUser string
expPass string
expErr bool
Expand All @@ -52,19 +54,19 @@ type base64TestCase struct {
func base64TestCases() []base64TestCase {
cases := []base64TestCase{
{name: "empty"},
{name: "not base64", expErr: true, config: AuthConfig{Auth: "not base64"}},
{name: "invalid format", expErr: true, config: AuthConfig{
{name: "not base64", expErr: true, config: registry.AuthConfig{Auth: "not base64"}},
{name: "invalid format", expErr: true, config: registry.AuthConfig{
Auth: base64.StdEncoding.EncodeToString([]byte("invalid format")),
}},
{name: "happy case", expUser: "user", expPass: "pass", config: AuthConfig{
{name: "happy case", expUser: "user", expPass: "pass", config: registry.AuthConfig{
Auth: base64.StdEncoding.EncodeToString([]byte("user:pass")),
}},
}

return cases
}

type testAuthFn func() (AuthConfig, error)
type testAuthFn func() (registry.AuthConfig, error)

func testBase64Case(tc base64TestCase, authFn testAuthFn) func(t *testing.T) {
return func(t *testing.T) {
Expand Down
35 changes: 18 additions & 17 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import (
"sync"
"time"

"github.com/docker/docker/api/types/registry"
"github.com/docker/go-sdk/config/auth"
)

var cacheInitMutex sync.Mutex

// authConfigCache holds the caching state for a Config instance
type authConfigCache struct {
entries map[string]AuthConfig
entries map[string]registry.AuthConfig
mutex sync.RWMutex
key string
}
Expand All @@ -30,7 +31,7 @@ func (c *Config) clearAuthCache() {
cache.mutex.Lock()
defer cache.mutex.Unlock()

cache.entries = make(map[string]AuthConfig)
cache.entries = make(map[string]registry.AuthConfig)
}

// cacheStats returns statistics about the auth config cache
Expand Down Expand Up @@ -72,7 +73,7 @@ func (c *Config) initCache() {
}

newCache := &authConfigCache{
entries: make(map[string]AuthConfig),
entries: make(map[string]registry.AuthConfig),
key: c.generateCacheKey(),
}

Expand All @@ -89,7 +90,7 @@ func (c *Config) generateCacheKey() string {
}

// AuthConfigForHostname returns the auth config for the given hostname with caching
func (c *Config) AuthConfigForHostname(hostname string) (AuthConfig, error) {
func (c *Config) AuthConfigForHostname(hostname string) (registry.AuthConfig, error) {
cache := c.getCache()

// Try cache first
Expand All @@ -103,7 +104,7 @@ func (c *Config) AuthConfigForHostname(hostname string) (AuthConfig, error) {
// Cache miss - resolve auth config
authConfig, err := c.resolveAuthConfigForHostname(hostname)
if err != nil {
return AuthConfig{}, err
return registry.AuthConfig{}, err
}

// Cache the result
Expand All @@ -115,8 +116,8 @@ func (c *Config) AuthConfigForHostname(hostname string) (AuthConfig, error) {
}

// AuthConfigsForImages returns auth configs for multiple images with caching
func (c *Config) AuthConfigsForImages(images []string) (map[string]AuthConfig, error) {
result := make(map[string]AuthConfig)
func (c *Config) AuthConfigsForImages(images []string) (map[string]registry.AuthConfig, error) {
result := make(map[string]registry.AuthConfig)
var errs []error

// Process each image
Expand All @@ -143,15 +144,15 @@ func (c *Config) AuthConfigsForImages(images []string) (map[string]AuthConfig, e
}

// AuthConfigForImage returns the auth config for a single image
func (c *Config) AuthConfigForImage(image string) (string, AuthConfig, error) {
func (c *Config) AuthConfigForImage(image string) (string, registry.AuthConfig, error) {
ref, err := auth.ParseImageRef(image)
if err != nil {
return "", AuthConfig{}, fmt.Errorf("parse image ref: %w", err)
return "", registry.AuthConfig{}, fmt.Errorf("parse image ref: %w", err)
}

authConfig, err := c.AuthConfigForHostname(ref.Registry)
if err != nil {
return ref.Registry, AuthConfig{}, err
return ref.Registry, registry.AuthConfig{}, err
}

authConfig.ServerAddress = ref.Registry
Expand Down Expand Up @@ -206,7 +207,7 @@ func (c *Config) Save() error {
}

// resolveAuthConfigForHostname performs the actual auth config resolution
func (c *Config) resolveAuthConfigForHostname(hostname string) (AuthConfig, error) {
func (c *Config) resolveAuthConfigForHostname(hostname string) (registry.AuthConfig, error) {
// Normalize Docker registry hostnames
hostname = auth.ResolveRegistryHost(hostname)

Expand Down Expand Up @@ -234,19 +235,19 @@ func (c *Config) resolveAuthConfigForHostname(hostname string) (AuthConfig, erro
}

// resolveFromCredentialHelper resolves credentials from a credential helper
func (c *Config) resolveFromCredentialHelper(helper, hostname string) (AuthConfig, error) {
func (c *Config) resolveFromCredentialHelper(helper, hostname string) (registry.AuthConfig, error) {
// Use existing credentialsFromHelper function but adapt to return AuthConfig
credentials, err := credentialsFromHelper(helper, hostname)
if err != nil {
return AuthConfig{}, err
return registry.AuthConfig{}, err
}

return credentials, nil
}

// processStoredAuthConfig processes auth config from stored configuration
func (c *Config) processStoredAuthConfig(stored AuthConfig, hostname string) (AuthConfig, error) {
authConfig := AuthConfig{
func (c *Config) processStoredAuthConfig(stored registry.AuthConfig, hostname string) (registry.AuthConfig, error) {
authConfig := registry.AuthConfig{
Auth: stored.Auth,
IdentityToken: stored.IdentityToken,
Password: stored.Password,
Expand All @@ -269,7 +270,7 @@ func (c *Config) processStoredAuthConfig(stored AuthConfig, hostname string) (Au
// Base64 auth case
user, pass, err := decodeBase64Auth(authConfig)
if err != nil {
return AuthConfig{}, fmt.Errorf("decode base64 auth: %w", err)
return registry.AuthConfig{}, fmt.Errorf("decode base64 auth: %w", err)
}
authConfig.Username = user
authConfig.Password = pass
Expand All @@ -286,7 +287,7 @@ func (c *Config) processStoredAuthConfig(stored AuthConfig, hostname string) (Au
// It takes the "Auth" filed from AuthConfig and decodes that into a username and password.
//
// If "Auth" is empty, an empty user/pass will be returned, but not an error.
func decodeBase64Auth(auth AuthConfig) (string, string, error) {
func decodeBase64Auth(auth registry.AuthConfig) (string, string, error) {
if auth.Auth == "" {
return "", "", nil
}
Expand Down
4 changes: 3 additions & 1 deletion config/config_benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/docker/docker/api/types/registry"
)

func BenchmarkAuthConfigCaching(b *testing.B) {
cfg := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"test.io": {Username: "user", Password: "pass"},
},
}
Expand Down
14 changes: 8 additions & 6 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/docker/docker/api/types/registry"
)

func TestConfig_AuthConfigsForImages(t *testing.T) {
config := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"registry1.io": {Username: "user1", Password: "pass1"},
"registry2.io": {Username: "user2", Password: "pass2"},
},
Expand All @@ -35,7 +37,7 @@ func TestConfig_AuthConfigsForImages(t *testing.T) {

func TestConfig_CacheManagement(t *testing.T) {
config := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"test.io": {Username: "user", Password: "pass"},
},
}
Expand Down Expand Up @@ -63,7 +65,7 @@ func TestConfig_CacheManagement(t *testing.T) {

func TestConfig_ConcurrentAccess(t *testing.T) {
config := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"test.io": {Username: "user", Password: "pass"},
},
}
Expand All @@ -87,13 +89,13 @@ func TestConfig_ConcurrentAccess(t *testing.T) {

func TestConfig_CacheKeyGeneration(t *testing.T) {
config1 := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"test.io": {Username: "user1", Password: "pass1"},
},
}

config2 := Config{
AuthConfigs: map[string]AuthConfig{
AuthConfigs: map[string]registry.AuthConfig{
"test.io": {Username: "user2", Password: "pass2"},
},
}
Expand All @@ -119,7 +121,7 @@ func TestConfigSave(t *testing.T) {
c := Config{
filepath: filepath.Join(dockerDir, FileName),
CurrentContext: "test",
AuthConfigs: map[string]AuthConfig{},
AuthConfigs: map[string]registry.AuthConfig{},
}

require.NoError(t, c.Save())
Expand Down
6 changes: 4 additions & 2 deletions config/credentials_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"os/exec"
"runtime"
"strings"

"github.com/docker/docker/api/types/registry"
)

// Errors from credential helpers.
Expand All @@ -34,8 +36,8 @@ var (
// Hostnames should already be resolved using [ResolveRegistryHost]
//
// If the username string is empty, the password string is an identity token.
func credentialsFromHelper(helper, hostname string) (AuthConfig, error) {
var creds AuthConfig
func credentialsFromHelper(helper, hostname string) (registry.AuthConfig, error) {
var creds registry.AuthConfig
credHelperName := helper
if helper == "" {
helper, helperErr := getCredentialHelper()
Expand Down
Loading
Loading