Add .docker/config.json and support for HTTP Headers
This PR does the following: - migrated ~/.dockerfg to ~/.docker/config.json. The data is migrated but the old file remains in case its needed - moves the auth json in that fie into an "auth" property so we can add new top-level properties w/o messing with the auth stuff - adds support for an HttpHeaders property in ~/.docker/config.json which adds these http headers to all msgs from the cli In a follow-on PR I'll move the config file process out from under "registry" since it not specific to that any more. I didn't do it here because I wanted the diff to be smaller so people can make sure I didn't break/miss any auth code during my edits. Signed-off-by: Doug Davis <dug@us.ibm.com>master
							parent
							
								
									94e2413ec0
								
							
						
					
					
						commit
						7b8b61bda1
					
				
							
								
								
									
										105
									
								
								docs/auth.go
								
								
								
								
							
							
						
						
									
										105
									
								
								docs/auth.go
								
								
								
								
							| 
						 | 
					@ -8,24 +8,27 @@ import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Sirupsen/logrus"
 | 
						"github.com/Sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/docker/docker/pkg/homedir"
 | 
				
			||||||
	"github.com/docker/docker/pkg/requestdecorator"
 | 
						"github.com/docker/docker/pkg/requestdecorator"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	// Where we store the config file
 | 
						// Where we store the config file
 | 
				
			||||||
	CONFIGFILE = ".dockercfg"
 | 
						CONFIGFILE     = "config.json"
 | 
				
			||||||
 | 
						OLD_CONFIGFILE = ".dockercfg"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	ErrConfigFileMissing = errors.New("The Auth config file is missing")
 | 
						ErrConfigFileMissing = errors.New("The Auth config file is missing")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Registry Auth Info
 | 
				
			||||||
type AuthConfig struct {
 | 
					type AuthConfig struct {
 | 
				
			||||||
	Username      string `json:"username,omitempty"`
 | 
						Username      string `json:"username,omitempty"`
 | 
				
			||||||
	Password      string `json:"password,omitempty"`
 | 
						Password      string `json:"password,omitempty"`
 | 
				
			||||||
| 
						 | 
					@ -34,9 +37,11 @@ type AuthConfig struct {
 | 
				
			||||||
	ServerAddress string `json:"serveraddress,omitempty"`
 | 
						ServerAddress string `json:"serveraddress,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ~/.docker/config.json file info
 | 
				
			||||||
type ConfigFile struct {
 | 
					type ConfigFile struct {
 | 
				
			||||||
	Configs  map[string]AuthConfig `json:"configs,omitempty"`
 | 
						AuthConfigs map[string]AuthConfig `json:"auths"`
 | 
				
			||||||
	rootPath string
 | 
						HttpHeaders map[string]string     `json:"HttpHeaders,omitempty"`
 | 
				
			||||||
 | 
						filename    string                // Note: not serialized - for internal use only
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RequestAuthorization struct {
 | 
					type RequestAuthorization struct {
 | 
				
			||||||
| 
						 | 
					@ -147,18 +152,58 @@ func decodeAuth(authStr string) (string, string, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// load up the auth config information and return values
 | 
					// load up the auth config information and return values
 | 
				
			||||||
// FIXME: use the internal golang config parser
 | 
					// FIXME: use the internal golang config parser
 | 
				
			||||||
func LoadConfig(rootPath string) (*ConfigFile, error) {
 | 
					func LoadConfig(configDir string) (*ConfigFile, error) {
 | 
				
			||||||
	configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
 | 
						if configDir == "" {
 | 
				
			||||||
	confFile := path.Join(rootPath, CONFIGFILE)
 | 
							configDir = filepath.Join(homedir.Get(), ".docker")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configFile := ConfigFile{
 | 
				
			||||||
 | 
							AuthConfigs: make(map[string]AuthConfig),
 | 
				
			||||||
 | 
							filename:    filepath.Join(configDir, CONFIGFILE),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Try happy path first - latest config file
 | 
				
			||||||
 | 
						if _, err := os.Stat(configFile.filename); err == nil {
 | 
				
			||||||
 | 
							file, err := os.Open(configFile.filename)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return &configFile, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := json.NewDecoder(file).Decode(&configFile); err != nil {
 | 
				
			||||||
 | 
								return &configFile, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for addr, ac := range configFile.AuthConfigs {
 | 
				
			||||||
 | 
								ac.Username, ac.Password, err = decodeAuth(ac.Auth)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return &configFile, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ac.Auth = ""
 | 
				
			||||||
 | 
								ac.ServerAddress = addr
 | 
				
			||||||
 | 
								configFile.AuthConfigs[addr] = ac
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return &configFile, nil
 | 
				
			||||||
 | 
						} else if !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							// if file is there but we can't stat it for any reason other
 | 
				
			||||||
 | 
							// than it doesn't exist then stop
 | 
				
			||||||
 | 
							return &configFile, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Can't find latest config file so check for the old one
 | 
				
			||||||
 | 
						confFile := filepath.Join(homedir.Get(), OLD_CONFIGFILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := os.Stat(confFile); err != nil {
 | 
						if _, err := os.Stat(confFile); err != nil {
 | 
				
			||||||
		return &configFile, nil //missing file is not an error
 | 
							return &configFile, nil //missing file is not an error
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b, err := ioutil.ReadFile(confFile)
 | 
						b, err := ioutil.ReadFile(confFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return &configFile, err
 | 
							return &configFile, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := json.Unmarshal(b, &configFile.Configs); err != nil {
 | 
						if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
 | 
				
			||||||
		arr := strings.Split(string(b), "\n")
 | 
							arr := strings.Split(string(b), "\n")
 | 
				
			||||||
		if len(arr) < 2 {
 | 
							if len(arr) < 2 {
 | 
				
			||||||
			return &configFile, fmt.Errorf("The Auth config file is empty")
 | 
								return &configFile, fmt.Errorf("The Auth config file is empty")
 | 
				
			||||||
| 
						 | 
					@ -179,48 +224,52 @@ func LoadConfig(rootPath string) (*ConfigFile, error) {
 | 
				
			||||||
		authConfig.Email = origEmail[1]
 | 
							authConfig.Email = origEmail[1]
 | 
				
			||||||
		authConfig.ServerAddress = IndexServerAddress()
 | 
							authConfig.ServerAddress = IndexServerAddress()
 | 
				
			||||||
		// *TODO: Switch to using IndexServerName() instead?
 | 
							// *TODO: Switch to using IndexServerName() instead?
 | 
				
			||||||
		configFile.Configs[IndexServerAddress()] = authConfig
 | 
							configFile.AuthConfigs[IndexServerAddress()] = authConfig
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		for k, authConfig := range configFile.Configs {
 | 
							for k, authConfig := range configFile.AuthConfigs {
 | 
				
			||||||
			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
 | 
								authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return &configFile, err
 | 
									return &configFile, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			authConfig.Auth = ""
 | 
								authConfig.Auth = ""
 | 
				
			||||||
			authConfig.ServerAddress = k
 | 
								authConfig.ServerAddress = k
 | 
				
			||||||
			configFile.Configs[k] = authConfig
 | 
								configFile.AuthConfigs[k] = authConfig
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &configFile, nil
 | 
						return &configFile, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// save the auth config
 | 
					func (configFile *ConfigFile) Save() error {
 | 
				
			||||||
func SaveConfig(configFile *ConfigFile) error {
 | 
						// Encode sensitive data into a new/temp struct
 | 
				
			||||||
	confFile := path.Join(configFile.rootPath, CONFIGFILE)
 | 
						tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
 | 
				
			||||||
	if len(configFile.Configs) == 0 {
 | 
						for k, authConfig := range configFile.AuthConfigs {
 | 
				
			||||||
		os.Remove(confFile)
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	configs := make(map[string]AuthConfig, len(configFile.Configs))
 | 
					 | 
				
			||||||
	for k, authConfig := range configFile.Configs {
 | 
					 | 
				
			||||||
		authCopy := authConfig
 | 
							authCopy := authConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		authCopy.Auth = encodeAuth(&authCopy)
 | 
							authCopy.Auth = encodeAuth(&authCopy)
 | 
				
			||||||
		authCopy.Username = ""
 | 
							authCopy.Username = ""
 | 
				
			||||||
		authCopy.Password = ""
 | 
							authCopy.Password = ""
 | 
				
			||||||
		authCopy.ServerAddress = ""
 | 
							authCopy.ServerAddress = ""
 | 
				
			||||||
		configs[k] = authCopy
 | 
							tmpAuthConfigs[k] = authCopy
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b, err := json.MarshalIndent(configs, "", "\t")
 | 
						saveAuthConfigs := configFile.AuthConfigs
 | 
				
			||||||
 | 
						configFile.AuthConfigs = tmpAuthConfigs
 | 
				
			||||||
 | 
						defer func() { configFile.AuthConfigs = saveAuthConfigs }()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := json.MarshalIndent(configFile, "", "\t")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = ioutil.WriteFile(confFile, b, 0600)
 | 
					
 | 
				
			||||||
 | 
						if err := os.MkdirAll(filepath.Dir(configFile.filename), 0600); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = ioutil.WriteFile(configFile.filename, data, 0600)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -431,7 +480,7 @@ func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, regis
 | 
				
			||||||
func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
 | 
					func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
 | 
				
			||||||
	configKey := index.GetAuthConfigKey()
 | 
						configKey := index.GetAuthConfigKey()
 | 
				
			||||||
	// First try the happy case
 | 
						// First try the happy case
 | 
				
			||||||
	if c, found := config.Configs[configKey]; found || index.Official {
 | 
						if c, found := config.AuthConfigs[configKey]; found || index.Official {
 | 
				
			||||||
		return c
 | 
							return c
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -450,7 +499,7 @@ func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Maybe they have a legacy config file, we will iterate the keys converting
 | 
						// Maybe they have a legacy config file, we will iterate the keys converting
 | 
				
			||||||
	// them to the new format and testing
 | 
						// them to the new format and testing
 | 
				
			||||||
	for registry, config := range config.Configs {
 | 
						for registry, config := range config.AuthConfigs {
 | 
				
			||||||
		if configKey == convertToHostname(registry) {
 | 
							if configKey == convertToHostname(registry) {
 | 
				
			||||||
			return config
 | 
								return config
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -459,3 +508,7 @@ func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
 | 
				
			||||||
	// When all else fails, return an empty auth config
 | 
						// When all else fails, return an empty auth config
 | 
				
			||||||
	return AuthConfig{}
 | 
						return AuthConfig{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config *ConfigFile) Filename() string {
 | 
				
			||||||
 | 
						return config.filename
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ package registry
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,13 +32,14 @@ func setupTempConfigFile() (*ConfigFile, error) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						root = filepath.Join(root, CONFIGFILE)
 | 
				
			||||||
	configFile := &ConfigFile{
 | 
						configFile := &ConfigFile{
 | 
				
			||||||
		rootPath: root,
 | 
							AuthConfigs: make(map[string]AuthConfig),
 | 
				
			||||||
		Configs:  make(map[string]AuthConfig),
 | 
							filename:    root,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, registry := range []string{"testIndex", IndexServerAddress()} {
 | 
						for _, registry := range []string{"testIndex", IndexServerAddress()} {
 | 
				
			||||||
		configFile.Configs[registry] = AuthConfig{
 | 
							configFile.AuthConfigs[registry] = AuthConfig{
 | 
				
			||||||
			Username: "docker-user",
 | 
								Username: "docker-user",
 | 
				
			||||||
			Password: "docker-pass",
 | 
								Password: "docker-pass",
 | 
				
			||||||
			Email:    "docker@docker.io",
 | 
								Email:    "docker@docker.io",
 | 
				
			||||||
| 
						 | 
					@ -52,14 +54,14 @@ func TestSameAuthDataPostSave(t *testing.T) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer os.RemoveAll(configFile.rootPath)
 | 
						defer os.RemoveAll(configFile.filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = SaveConfig(configFile)
 | 
						err = configFile.Save()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authConfig := configFile.Configs["testIndex"]
 | 
						authConfig := configFile.AuthConfigs["testIndex"]
 | 
				
			||||||
	if authConfig.Username != "docker-user" {
 | 
						if authConfig.Username != "docker-user" {
 | 
				
			||||||
		t.Fail()
 | 
							t.Fail()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -79,9 +81,9 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer os.RemoveAll(configFile.rootPath)
 | 
						defer os.RemoveAll(configFile.filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	indexConfig := configFile.Configs[IndexServerAddress()]
 | 
						indexConfig := configFile.AuthConfigs[IndexServerAddress()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	officialIndex := &IndexInfo{
 | 
						officialIndex := &IndexInfo{
 | 
				
			||||||
		Official: true,
 | 
							Official: true,
 | 
				
			||||||
| 
						 | 
					@ -102,7 +104,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer os.RemoveAll(configFile.rootPath)
 | 
						defer os.RemoveAll(configFile.filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	registryAuth := AuthConfig{
 | 
						registryAuth := AuthConfig{
 | 
				
			||||||
		Username: "foo-user",
 | 
							Username: "foo-user",
 | 
				
			||||||
| 
						 | 
					@ -119,7 +121,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 | 
				
			||||||
		Password: "baz-pass",
 | 
							Password: "baz-pass",
 | 
				
			||||||
		Email:    "baz@example.com",
 | 
							Email:    "baz@example.com",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	configFile.Configs[IndexServerAddress()] = officialAuth
 | 
						configFile.AuthConfigs[IndexServerAddress()] = officialAuth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	expectedAuths := map[string]AuthConfig{
 | 
						expectedAuths := map[string]AuthConfig{
 | 
				
			||||||
		"registry.example.com": registryAuth,
 | 
							"registry.example.com": registryAuth,
 | 
				
			||||||
| 
						 | 
					@ -157,12 +159,12 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 | 
				
			||||||
			Name: configKey,
 | 
								Name: configKey,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		for _, registry := range registries {
 | 
							for _, registry := range registries {
 | 
				
			||||||
			configFile.Configs[registry] = configured
 | 
								configFile.AuthConfigs[registry] = configured
 | 
				
			||||||
			resolved := configFile.ResolveAuthConfig(index)
 | 
								resolved := configFile.ResolveAuthConfig(index)
 | 
				
			||||||
			if resolved.Email != configured.Email {
 | 
								if resolved.Email != configured.Email {
 | 
				
			||||||
				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
 | 
									t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			delete(configFile.Configs, registry)
 | 
								delete(configFile.AuthConfigs, registry)
 | 
				
			||||||
			resolved = configFile.ResolveAuthConfig(index)
 | 
								resolved = configFile.ResolveAuthConfig(index)
 | 
				
			||||||
			if resolved.Email == configured.Email {
 | 
								if resolved.Email == configured.Email {
 | 
				
			||||||
				t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)
 | 
									t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					package registry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker/pkg/homedir"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMissingFile(t *testing.T) {
 | 
				
			||||||
 | 
						tmpHome, _ := ioutil.TempDir("", "config-test")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config, err := LoadConfig(tmpHome)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed loading on missing file: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now save it and make sure it shows up in new form
 | 
				
			||||||
 | 
						err = config.Save()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to save: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
 | 
				
			||||||
 | 
						if !strings.Contains(string(buf), `"auths":`) {
 | 
				
			||||||
 | 
							t.Fatalf("Should have save in new form: %s", string(buf))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEmptyFile(t *testing.T) {
 | 
				
			||||||
 | 
						tmpHome, _ := ioutil.TempDir("", "config-test")
 | 
				
			||||||
 | 
						fn := filepath.Join(tmpHome, CONFIGFILE)
 | 
				
			||||||
 | 
						ioutil.WriteFile(fn, []byte(""), 0600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := LoadConfig(tmpHome)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							t.Fatalf("Was supposed to fail")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEmptyJson(t *testing.T) {
 | 
				
			||||||
 | 
						tmpHome, _ := ioutil.TempDir("", "config-test")
 | 
				
			||||||
 | 
						fn := filepath.Join(tmpHome, CONFIGFILE)
 | 
				
			||||||
 | 
						ioutil.WriteFile(fn, []byte("{}"), 0600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config, err := LoadConfig(tmpHome)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed loading on empty json file: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now save it and make sure it shows up in new form
 | 
				
			||||||
 | 
						err = config.Save()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to save: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
 | 
				
			||||||
 | 
						if !strings.Contains(string(buf), `"auths":`) {
 | 
				
			||||||
 | 
							t.Fatalf("Should have save in new form: %s", string(buf))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestOldJson(t *testing.T) {
 | 
				
			||||||
 | 
						if runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tmpHome, _ := ioutil.TempDir("", "config-test")
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpHome)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						homeKey := homedir.Key()
 | 
				
			||||||
 | 
						homeVal := homedir.Get()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() { os.Setenv(homeKey, homeVal) }()
 | 
				
			||||||
 | 
						os.Setenv(homeKey, tmpHome)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn := filepath.Join(tmpHome, OLD_CONFIGFILE)
 | 
				
			||||||
 | 
						js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
 | 
				
			||||||
 | 
						ioutil.WriteFile(fn, []byte(js), 0600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config, err := LoadConfig(tmpHome)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed loading on empty json file: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ac := config.AuthConfigs["https://index.docker.io/v1/"]
 | 
				
			||||||
 | 
						if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
 | 
				
			||||||
 | 
							t.Fatalf("Missing data from parsing:\n%q", config)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now save it and make sure it shows up in new form
 | 
				
			||||||
 | 
						err = config.Save()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to save: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
 | 
				
			||||||
 | 
						if !strings.Contains(string(buf), `"auths":`) ||
 | 
				
			||||||
 | 
							!strings.Contains(string(buf), "user@example.com") {
 | 
				
			||||||
 | 
							t.Fatalf("Should have save in new form: %s", string(buf))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNewJson(t *testing.T) {
 | 
				
			||||||
 | 
						tmpHome, _ := ioutil.TempDir("", "config-test")
 | 
				
			||||||
 | 
						fn := filepath.Join(tmpHome, CONFIGFILE)
 | 
				
			||||||
 | 
						js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
 | 
				
			||||||
 | 
						ioutil.WriteFile(fn, []byte(js), 0600)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config, err := LoadConfig(tmpHome)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed loading on empty json file: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ac := config.AuthConfigs["https://index.docker.io/v1/"]
 | 
				
			||||||
 | 
						if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
 | 
				
			||||||
 | 
							t.Fatalf("Missing data from parsing:\n%q", config)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now save it and make sure it shows up in new form
 | 
				
			||||||
 | 
						err = config.Save()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Failed to save: %q", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
 | 
				
			||||||
 | 
						if !strings.Contains(string(buf), `"auths":`) ||
 | 
				
			||||||
 | 
							!strings.Contains(string(buf), "user@example.com") {
 | 
				
			||||||
 | 
							t.Fatalf("Should have save in new form: %s", string(buf))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue