Be benevolent to .docker/config.json file
Docker can store auth entries with schema prefix or even path suffix. See an example: { "auths": { "10.3.10.88:5000": { ... }, "http://10.3.10.88:5000/v2/": { ... }, "https://10.3.10.88:5000": { ... }, "https://index.docker.io/v1/": { ... } } } The entries were created using command `docker login` of upstream Docker 1.12. Let's normalize the auth keys before trying to match against hostname. Signed-off-by: Michal Minář <miminar@redhat.com>
This commit is contained in:
parent
6efd016e38
commit
e18b1afd04
|
@ -243,49 +243,58 @@ func (c *dockerClient) getBearerToken(realm, service, scope string) (string, err
|
||||||
return tokenStruct.Token, nil
|
return tokenStruct.Token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAuth(hostname string) (string, string, error) {
|
func getAuth(registry string) (string, string, error) {
|
||||||
// TODO(runcom): get this from *cli.Context somehow
|
// TODO(runcom): get this from *cli.Context somehow
|
||||||
//if username != "" && password != "" {
|
//if username != "" && password != "" {
|
||||||
//return username, password, nil
|
//return username, password, nil
|
||||||
//}
|
//}
|
||||||
if hostname == dockerHostname {
|
var dockerAuth dockerConfigFile
|
||||||
hostname = dockerAuthRegistry
|
|
||||||
}
|
|
||||||
dockerCfgPath := filepath.Join(getDefaultConfigDir(".docker"), dockerCfgFileName)
|
dockerCfgPath := filepath.Join(getDefaultConfigDir(".docker"), dockerCfgFileName)
|
||||||
if _, err := os.Stat(dockerCfgPath); err == nil {
|
if _, err := os.Stat(dockerCfgPath); err == nil {
|
||||||
j, err := ioutil.ReadFile(dockerCfgPath)
|
j, err := ioutil.ReadFile(dockerCfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
var dockerAuth dockerConfigFile
|
|
||||||
if err := json.Unmarshal(j, &dockerAuth); err != nil {
|
if err := json.Unmarshal(j, &dockerAuth); err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
// try the normal case
|
|
||||||
if c, ok := dockerAuth.AuthConfigs[hostname]; ok {
|
|
||||||
return decodeDockerAuth(c.Auth)
|
|
||||||
}
|
|
||||||
} else if os.IsNotExist(err) {
|
} else if os.IsNotExist(err) {
|
||||||
|
// try old config path
|
||||||
oldDockerCfgPath := filepath.Join(getDefaultConfigDir(dockerCfgObsolete))
|
oldDockerCfgPath := filepath.Join(getDefaultConfigDir(dockerCfgObsolete))
|
||||||
if _, err := os.Stat(oldDockerCfgPath); err != nil {
|
if _, err := os.Stat(oldDockerCfgPath); err != nil {
|
||||||
return "", "", nil //missing file is not an error
|
if os.IsNotExist(err) {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
return "", "", fmt.Errorf("%s - %v", oldDockerCfgPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := ioutil.ReadFile(oldDockerCfgPath)
|
j, err := ioutil.ReadFile(oldDockerCfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
var dockerAuthOld map[string]dockerAuthConfigObsolete
|
if err := json.Unmarshal(j, &dockerAuth.AuthConfigs); err != nil {
|
||||||
if err := json.Unmarshal(j, &dockerAuthOld); err != nil {
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
if c, ok := dockerAuthOld[hostname]; ok {
|
|
||||||
return decodeDockerAuth(c.Auth)
|
} else if err != nil {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if file is there but we can't stat it for any reason other
|
|
||||||
// than it doesn't exist then stop
|
|
||||||
return "", "", fmt.Errorf("%s - %v", dockerCfgPath, err)
|
return "", "", fmt.Errorf("%s - %v", dockerCfgPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I'm feeling lucky
|
||||||
|
if c, exists := dockerAuth.AuthConfigs[registry]; exists {
|
||||||
|
return decodeDockerAuth(c.Auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad luck; let's normalize the entries first
|
||||||
|
registry = normalizeRegistry(registry)
|
||||||
|
normalizedAuths := map[string]dockerAuthConfig{}
|
||||||
|
for k, v := range dockerAuth.AuthConfigs {
|
||||||
|
normalizedAuths[normalizeRegistry(k)] = v
|
||||||
|
}
|
||||||
|
if c, exists := normalizedAuths[registry]; exists {
|
||||||
|
return decodeDockerAuth(c.Auth)
|
||||||
|
}
|
||||||
return "", "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,10 +351,6 @@ func getDefaultConfigDir(confPath string) string {
|
||||||
return filepath.Join(homedir.Get(), confPath)
|
return filepath.Join(homedir.Get(), confPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
type dockerAuthConfigObsolete struct {
|
|
||||||
Auth string `json:"auth"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type dockerAuthConfig struct {
|
type dockerAuthConfig struct {
|
||||||
Auth string `json:"auth,omitempty"`
|
Auth string `json:"auth,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -368,3 +373,28 @@ func decodeDockerAuth(s string) (string, string, error) {
|
||||||
password := strings.Trim(parts[1], "\x00")
|
password := strings.Trim(parts[1], "\x00")
|
||||||
return user, password, nil
|
return user, password, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertToHostname converts a registry url which has http|https prepended
|
||||||
|
// to just an hostname.
|
||||||
|
// Copied from github.com/docker/docker/registry/auth.go
|
||||||
|
func convertToHostname(url string) string {
|
||||||
|
stripped := url
|
||||||
|
if strings.HasPrefix(url, "http://") {
|
||||||
|
stripped = strings.TrimPrefix(url, "http://")
|
||||||
|
} else if strings.HasPrefix(url, "https://") {
|
||||||
|
stripped = strings.TrimPrefix(url, "https://")
|
||||||
|
}
|
||||||
|
|
||||||
|
nameParts := strings.SplitN(stripped, "/", 2)
|
||||||
|
|
||||||
|
return nameParts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeRegistry(registry string) string {
|
||||||
|
normalized := convertToHostname(registry)
|
||||||
|
switch normalized {
|
||||||
|
case "registry-1.docker.io", "docker.io":
|
||||||
|
return "index.docker.io"
|
||||||
|
}
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,411 @@
|
||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
//"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetAuth(t *testing.T) {
|
||||||
|
origHomeDir := homedir.Get()
|
||||||
|
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("using temporary home directory: %q", tmpDir)
|
||||||
|
// override homedir
|
||||||
|
os.Setenv(homedir.Key(), tmpDir)
|
||||||
|
defer func() {
|
||||||
|
err := os.RemoveAll(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||||
|
}
|
||||||
|
os.Setenv(homedir.Key(), origHomeDir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
configDir := filepath.Join(tmpDir, ".docker")
|
||||||
|
if err := os.Mkdir(configDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
configPath := filepath.Join(configDir, "config.json")
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
hostname string
|
||||||
|
authConfig testAuthConfig
|
||||||
|
expectedUsername string
|
||||||
|
expectedPassword string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty hostname",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{"localhost:5000": testAuthConfigData{"bob", "password"}}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no auth config",
|
||||||
|
hostname: "index.docker.io",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match one",
|
||||||
|
hostname: "example.org",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{"example.org": testAuthConfigData{"joe", "mypass"}}),
|
||||||
|
expectedUsername: "joe",
|
||||||
|
expectedPassword: "mypass",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match none",
|
||||||
|
hostname: "registry.example.org",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{"example.org": testAuthConfigData{"joe", "mypass"}}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match docker.io",
|
||||||
|
hostname: "docker.io",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"example.org": testAuthConfigData{"example", "org"},
|
||||||
|
"index.docker.io": testAuthConfigData{"index", "docker.io"},
|
||||||
|
"docker.io": testAuthConfigData{"docker", "io"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "docker",
|
||||||
|
expectedPassword: "io",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match docker.io normalized",
|
||||||
|
hostname: "docker.io",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"example.org": testAuthConfigData{"bob", "pw"},
|
||||||
|
"https://index.docker.io/v1": testAuthConfigData{"alice", "wp"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "alice",
|
||||||
|
expectedPassword: "wp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "normalize registry",
|
||||||
|
hostname: "https://docker.io/v1",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"docker.io": testAuthConfigData{"user", "pw"},
|
||||||
|
"localhost:5000": testAuthConfigData{"joe", "pass"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "user",
|
||||||
|
expectedPassword: "pw",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match localhost",
|
||||||
|
hostname: "http://localhost",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"docker.io": testAuthConfigData{"user", "pw"},
|
||||||
|
"localhost": testAuthConfigData{"joe", "pass"},
|
||||||
|
"example.com": testAuthConfigData{"alice", "pwd"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "joe",
|
||||||
|
expectedPassword: "pass",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match ip",
|
||||||
|
hostname: "10.10.3.56:5000",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"10.10.30.45": testAuthConfigData{"user", "pw"},
|
||||||
|
"localhost": testAuthConfigData{"joe", "pass"},
|
||||||
|
"10.10.3.56": testAuthConfigData{"alice", "pwd"},
|
||||||
|
"10.10.3.56:5000": testAuthConfigData{"me", "mine"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "me",
|
||||||
|
expectedPassword: "mine",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match port",
|
||||||
|
hostname: "https://localhost:5000",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"https://127.0.0.1:5000": testAuthConfigData{"user", "pw"},
|
||||||
|
"http://localhost": testAuthConfigData{"joe", "pass"},
|
||||||
|
"https://localhost:5001": testAuthConfigData{"alice", "pwd"},
|
||||||
|
"localhost:5000": testAuthConfigData{"me", "mine"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "me",
|
||||||
|
expectedPassword: "mine",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
contents, err := json.MarshalIndent(&tc.authConfig, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("[%s] failed to marshal authConfig: %v", tc.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
|
||||||
|
t.Errorf("[%s] failed to write file %q: %v", tc.name, configPath, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, err := getAuth(tc.hostname)
|
||||||
|
if err == nil && tc.expectedError != nil {
|
||||||
|
t.Errorf("[%s] got unexpected non error and username=%q, password=%q", tc.name, username, password)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil && tc.expectedError == nil {
|
||||||
|
t.Errorf("[%s] got unexpected error: %#+v", tc.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(err, tc.expectedError) {
|
||||||
|
t.Errorf("[%s] got unexpected error: %#+v != %#+v", tc.name, err, tc.expectedError)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != tc.expectedUsername {
|
||||||
|
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, username, tc.expectedUsername)
|
||||||
|
}
|
||||||
|
if password != tc.expectedPassword {
|
||||||
|
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, password, tc.expectedPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAuthFromLegacyFile(t *testing.T) {
|
||||||
|
origHomeDir := homedir.Get()
|
||||||
|
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("using temporary home directory: %q", tmpDir)
|
||||||
|
// override homedir
|
||||||
|
os.Setenv(homedir.Key(), tmpDir)
|
||||||
|
defer func() {
|
||||||
|
err := os.RemoveAll(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||||
|
}
|
||||||
|
os.Setenv(homedir.Key(), origHomeDir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
configPath := filepath.Join(tmpDir, ".dockercfg")
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
hostname string
|
||||||
|
authConfig testAuthConfig
|
||||||
|
expectedUsername string
|
||||||
|
expectedPassword string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "normalize registry",
|
||||||
|
hostname: "https://docker.io/v1",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"docker.io": testAuthConfigData{"user", "pw"},
|
||||||
|
"localhost:5000": testAuthConfigData{"joe", "pass"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "user",
|
||||||
|
expectedPassword: "pw",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignore schema and path",
|
||||||
|
hostname: "http://index.docker.io/v1",
|
||||||
|
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"docker.io/v2": testAuthConfigData{"user", "pw"},
|
||||||
|
"https://localhost/v1": testAuthConfigData{"joe", "pwd"},
|
||||||
|
}),
|
||||||
|
expectedUsername: "user",
|
||||||
|
expectedPassword: "pw",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
contents, err := json.MarshalIndent(&tc.authConfig.Auths, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("[%s] failed to marshal authConfig: %v", tc.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
|
||||||
|
t.Errorf("[%s] failed to write file %q: %v", tc.name, configPath, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, err := getAuth(tc.hostname)
|
||||||
|
if err == nil && tc.expectedError != nil {
|
||||||
|
t.Errorf("[%s] got unexpected non error and username=%q, password=%q", tc.name, username, password)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil && tc.expectedError == nil {
|
||||||
|
t.Errorf("[%s] got unexpected error: %#+v", tc.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(err, tc.expectedError) {
|
||||||
|
t.Errorf("[%s] got unexpected error: %#+v != %#+v", tc.name, err, tc.expectedError)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != tc.expectedUsername {
|
||||||
|
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, username, tc.expectedUsername)
|
||||||
|
}
|
||||||
|
if password != tc.expectedPassword {
|
||||||
|
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, password, tc.expectedPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAuthPreferNewConfig(t *testing.T) {
|
||||||
|
origHomeDir := homedir.Get()
|
||||||
|
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("using temporary home directory: %q", tmpDir)
|
||||||
|
// override homedir
|
||||||
|
os.Setenv(homedir.Key(), tmpDir)
|
||||||
|
defer func() {
|
||||||
|
err := os.RemoveAll(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||||
|
}
|
||||||
|
os.Setenv(homedir.Key(), origHomeDir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
configDir := filepath.Join(tmpDir, ".docker")
|
||||||
|
if err := os.Mkdir(configDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, data := range []struct {
|
||||||
|
path string
|
||||||
|
ac interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
filepath.Join(configDir, "config.json"),
|
||||||
|
makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"https://index.docker.io/v1/": testAuthConfigData{"alice", "pass"},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filepath.Join(tmpDir, ".dockercfg"),
|
||||||
|
makeTestAuthConfig(testAuthConfigDataMap{
|
||||||
|
"https://index.docker.io/v1/": testAuthConfigData{"bob", "pw"},
|
||||||
|
}).Auths,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
contents, err := json.MarshalIndent(&data.ac, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal authConfig: %v", err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(data.path, contents, 0640); err != nil {
|
||||||
|
t.Fatalf("failed to write file %q: %v", data.path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, err := getAuth("index.docker.io")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got unexpected error: %#+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if username != "alice" {
|
||||||
|
t.Fatalf("got unexpected user name: %q != %q", username, "alice")
|
||||||
|
}
|
||||||
|
if password != "pass" {
|
||||||
|
t.Fatalf("got unexpected user name: %q != %q", password, "pass")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAuthFailsOnBadInput(t *testing.T) {
|
||||||
|
origHomeDir := homedir.Get()
|
||||||
|
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("using temporary home directory: %q", tmpDir)
|
||||||
|
// override homedir
|
||||||
|
os.Setenv(homedir.Key(), tmpDir)
|
||||||
|
defer func() {
|
||||||
|
err := os.RemoveAll(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||||
|
}
|
||||||
|
os.Setenv(homedir.Key(), origHomeDir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
configDir := filepath.Join(tmpDir, ".docker")
|
||||||
|
if err := os.Mkdir(configDir, 0750); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
configPath := filepath.Join(configDir, "config.json")
|
||||||
|
|
||||||
|
// no config file present
|
||||||
|
username, password, err := getAuth("index.docker.io")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got unexpected error: %#+v", err)
|
||||||
|
}
|
||||||
|
if len(username) > 0 || len(password) > 0 {
|
||||||
|
t.Fatalf("got unexpected not empty username/password: %q/%q", username, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(configPath, []byte("Json rocks! Unless it doesn't."), 0640); err != nil {
|
||||||
|
t.Fatalf("failed to write file %q: %v", configPath, err)
|
||||||
|
}
|
||||||
|
username, password, err = getAuth("index.docker.io")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("got unexpected non-error: username=%q, password=%q", username, password)
|
||||||
|
}
|
||||||
|
if _, ok := err.(*json.SyntaxError); !ok {
|
||||||
|
t.Fatalf("expected os.PathError, not: %#+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the invalid config file
|
||||||
|
os.RemoveAll(configPath)
|
||||||
|
// no config file present
|
||||||
|
username, password, err = getAuth("index.docker.io")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got unexpected error: %#+v", err)
|
||||||
|
}
|
||||||
|
if len(username) > 0 || len(password) > 0 {
|
||||||
|
t.Fatalf("got unexpected not empty username/password: %q/%q", username, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
configPath = filepath.Join(tmpDir, ".dockercfg")
|
||||||
|
if err := ioutil.WriteFile(configPath, []byte("I'm certainly not a json string."), 0640); err != nil {
|
||||||
|
t.Fatalf("failed to write file %q: %v", configPath, err)
|
||||||
|
}
|
||||||
|
username, password, err = getAuth("index.docker.io")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("got unexpected non-error: username=%q, password=%q", username, password)
|
||||||
|
}
|
||||||
|
if _, ok := err.(*json.SyntaxError); !ok {
|
||||||
|
t.Fatalf("expected os.PathError, not: %#+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testAuthConfigData struct {
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testAuthConfigDataMap map[string]testAuthConfigData
|
||||||
|
|
||||||
|
type testAuthConfigEntry struct {
|
||||||
|
Auth string `json:"auth,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type testAuthConfig struct {
|
||||||
|
Auths map[string]testAuthConfigEntry `json:"auths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeAuth creates an auth value from given authConfig data to be stored in auth config file.
|
||||||
|
// Inspired by github.com/docker/docker/cliconfig/config.go v1.10.3.
|
||||||
|
func encodeAuth(authConfig *testAuthConfigData) string {
|
||||||
|
authStr := authConfig.username + ":" + authConfig.password
|
||||||
|
msg := []byte(authStr)
|
||||||
|
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
|
||||||
|
base64.StdEncoding.Encode(encoded, msg)
|
||||||
|
return string(encoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTestAuthConfig(authConfigData map[string]testAuthConfigData) testAuthConfig {
|
||||||
|
ac := testAuthConfig{
|
||||||
|
Auths: make(map[string]testAuthConfigEntry),
|
||||||
|
}
|
||||||
|
for host, data := range authConfigData {
|
||||||
|
ac.Auths[host] = testAuthConfigEntry{
|
||||||
|
Auth: encodeAuth(&data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ac
|
||||||
|
}
|
Loading…
Reference in New Issue