119 lines
3.3 KiB
Go
119 lines
3.3 KiB
Go
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 := testcontainersdocker.NewClient(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
|
|
}
|