forked from testcontainers/testcontainers-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
docker_auth.go
119 lines (98 loc) · 3.32 KB
/
docker_auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package testcontainers
import (
"context"
"encoding/base64"
"encoding/json"
"os"
"github.com/cpuguy83/dockercfg"
"github.com/docker/docker/api/types/registry"
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
)
// DockerImageAuth returns the auth config for the given Docker image, extracting first its Docker registry.
// Finally, it will use the credential helpers to extract the information from the docker config file
// for that registry, if it exists.
func DockerImageAuth(ctx context.Context, image string) (string, registry.AuthConfig, error) {
defaultRegistry := defaultRegistry(ctx)
reg := testcontainersdocker.ExtractRegistry(image, defaultRegistry)
cfgs, err := getDockerAuthConfigs()
if err != nil {
return reg, registry.AuthConfig{}, err
}
if cfg, ok := cfgs[reg]; ok {
return reg, cfg, nil
}
return reg, registry.AuthConfig{}, dockercfg.ErrCredentialsNotFound
}
// defaultRegistry returns the default registry to use when pulling images
// It will use the docker daemon to get the default registry, returning "https://index.docker.io/v1/" if
// it fails to get the information from the daemon
func defaultRegistry(ctx context.Context) string {
client, err := NewDockerClientWithOpts(ctx)
if err != nil {
return testcontainersdocker.IndexDockerIO
}
defer client.Close()
info, err := client.Info(ctx)
if err != nil {
return testcontainersdocker.IndexDockerIO
}
return info.IndexServerAddress
}
// getDockerAuthConfigs returns a map with the auth configs from the docker config file
// using the registry as the key
func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
cfg, err := getDockerConfig()
if err != nil {
return nil, err
}
cfgs := map[string]registry.AuthConfig{}
for k, v := range cfg.AuthConfigs {
ac := registry.AuthConfig{
Auth: v.Auth,
Email: v.Email,
IdentityToken: v.IdentityToken,
Password: v.Password,
RegistryToken: v.RegistryToken,
ServerAddress: v.ServerAddress,
Username: v.Username,
}
if v.Username == "" && v.Password == "" {
u, p, _ := dockercfg.GetRegistryCredentials(k)
ac.Username = u
ac.Password = p
}
if v.Auth == "" {
ac.Auth = base64.StdEncoding.EncodeToString([]byte(ac.Username + ":" + ac.Password))
}
cfgs[k] = ac
}
// in the case where the auth field in the .docker/conf.json is empty, and the user has credential helpers registered
// the auth comes from there
for k := range cfg.CredentialHelpers {
ac := registry.AuthConfig{}
u, p, _ := dockercfg.GetRegistryCredentials(k)
ac.Username = u
ac.Password = p
cfgs[k] = ac
}
return cfgs, nil
}
// getDockerConfig returns the docker config file. It will internally check, in this particular order:
// 1. the DOCKER_AUTH_CONFIG environment variable, unmarshalling it into a dockercfg.Config
// 2. the DOCKER_CONFIG environment variable, as the path to the config file
// 3. else it will load the default config file, which is ~/.docker/config.json
func getDockerConfig() (dockercfg.Config, error) {
dockerAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG")
if dockerAuthConfig != "" {
cfg := dockercfg.Config{}
err := json.Unmarshal([]byte(dockerAuthConfig), &cfg)
if err == nil {
return cfg, nil
}
}
cfg, err := dockercfg.LoadDefaultConfig()
if err != nil {
return cfg, err
}
return cfg, nil
}