Browse code

Move CLI config processing out from under registry dir

No logic changes should be in here, just moving things around.

Signed-off-by: Doug Davis <dug@us.ibm.com>

Doug Davis authored on 2015/04/22 21:06:58
Showing 20 changed files
... ...
@@ -15,10 +15,10 @@ import (
15 15
 	"text/template"
16 16
 	"time"
17 17
 
18
+	"github.com/docker/docker/cliconfig"
18 19
 	"github.com/docker/docker/pkg/homedir"
19 20
 	flag "github.com/docker/docker/pkg/mflag"
20 21
 	"github.com/docker/docker/pkg/term"
21
-	"github.com/docker/docker/registry"
22 22
 )
23 23
 
24 24
 // DockerCli represents the docker command line client.
... ...
@@ -28,8 +28,9 @@ type DockerCli struct {
28 28
 	proto string
29 29
 	// addr holds the client address.
30 30
 	addr string
31
-	// configFile holds the configuration file (instance of registry.ConfigFile).
32
-	configFile *registry.ConfigFile
31
+
32
+	// configFile has the client configuration file
33
+	configFile *cliconfig.ConfigFile
33 34
 	// in holds the input stream and closer (io.ReadCloser) for the client.
34 35
 	in io.ReadCloser
35 36
 	// out holds the output stream (io.Writer) for the client.
... ...
@@ -184,7 +185,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, a
184 184
 		tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
185 185
 	}
186 186
 
187
-	configFile, e := registry.LoadConfig(filepath.Join(homedir.Get(), ".docker"))
187
+	configFile, e := cliconfig.Load(filepath.Join(homedir.Get(), ".docker"))
188 188
 	if e != nil {
189 189
 		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
190 190
 	}
... ...
@@ -38,7 +38,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
38 38
 	}
39 39
 
40 40
 	// Resolve the Auth config relevant for this server
41
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
41
+	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
42 42
 	buf, err := json.Marshal(authConfig)
43 43
 	if err != nil {
44 44
 		return err
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
+	"github.com/docker/docker/cliconfig"
12 13
 	flag "github.com/docker/docker/pkg/mflag"
13 14
 	"github.com/docker/docker/pkg/term"
14 15
 	"github.com/docker/docker/registry"
... ...
@@ -56,7 +57,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
56 56
 
57 57
 	authconfig, ok := cli.configFile.AuthConfigs[serverAddress]
58 58
 	if !ok {
59
-		authconfig = registry.AuthConfig{}
59
+		authconfig = cliconfig.AuthConfig{}
60 60
 	}
61 61
 
62 62
 	if username == "" {
... ...
@@ -28,7 +28,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
28 28
 		return err
29 29
 	}
30 30
 	// Resolve the Auth config relevant for this server
31
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
31
+	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
32 32
 	// If we're not using a custom registry, we know the restrictions
33 33
 	// applied to repository names and can warn the user in advance.
34 34
 	// Custom repositories can have different rules, and we must also
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	"github.com/docker/docker/api"
22 22
 	"github.com/docker/docker/api/types"
23 23
 	"github.com/docker/docker/autogen/dockerversion"
24
+	"github.com/docker/docker/cliconfig"
24 25
 	"github.com/docker/docker/engine"
25 26
 	"github.com/docker/docker/pkg/jsonmessage"
26 27
 	"github.com/docker/docker/pkg/signal"
... ...
@@ -119,7 +120,7 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
119 119
 }
120 120
 
121 121
 func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
122
-	cmdAttempt := func(authConfig registry.AuthConfig) (io.ReadCloser, int, error) {
122
+	cmdAttempt := func(authConfig cliconfig.AuthConfig) (io.ReadCloser, int, error) {
123 123
 		buf, err := json.Marshal(authConfig)
124 124
 		if err != nil {
125 125
 			return nil, -1, err
... ...
@@ -150,14 +151,14 @@ func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reade
150 150
 	}
151 151
 
152 152
 	// Resolve the Auth config relevant for this server
153
-	authConfig := cli.configFile.ResolveAuthConfig(index)
153
+	authConfig := registry.ResolveAuthConfig(cli.configFile, index)
154 154
 	body, statusCode, err := cmdAttempt(authConfig)
155 155
 	if statusCode == http.StatusUnauthorized {
156 156
 		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
157 157
 		if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
158 158
 			return nil, -1, err
159 159
 		}
160
-		authConfig = cli.configFile.ResolveAuthConfig(index)
160
+		authConfig = registry.ResolveAuthConfig(cli.configFile, index)
161 161
 		return cmdAttempt(authConfig)
162 162
 	}
163 163
 	return body, statusCode, err
... ...
@@ -22,6 +22,7 @@ import (
22 22
 	"github.com/docker/docker/api/types"
23 23
 	"github.com/docker/docker/autogen/dockerversion"
24 24
 	"github.com/docker/docker/builder"
25
+	"github.com/docker/docker/cliconfig"
25 26
 	"github.com/docker/docker/daemon"
26 27
 	"github.com/docker/docker/daemon/networkdriver/bridge"
27 28
 	"github.com/docker/docker/engine"
... ...
@@ -34,7 +35,6 @@ import (
34 34
 	"github.com/docker/docker/pkg/stdcopy"
35 35
 	"github.com/docker/docker/pkg/streamformatter"
36 36
 	"github.com/docker/docker/pkg/version"
37
-	"github.com/docker/docker/registry"
38 37
 	"github.com/docker/docker/runconfig"
39 38
 	"github.com/docker/docker/utils"
40 39
 )
... ...
@@ -239,7 +239,7 @@ func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) {
239 239
 }
240 240
 
241 241
 func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
242
-	var config *registry.AuthConfig
242
+	var config *cliconfig.AuthConfig
243 243
 	err := json.NewDecoder(r.Body).Decode(&config)
244 244
 	r.Body.Close()
245 245
 	if err != nil {
... ...
@@ -728,13 +728,13 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w
728 728
 		tag   = r.Form.Get("tag")
729 729
 	)
730 730
 	authEncoded := r.Header.Get("X-Registry-Auth")
731
-	authConfig := &registry.AuthConfig{}
731
+	authConfig := &cliconfig.AuthConfig{}
732 732
 	if authEncoded != "" {
733 733
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
734 734
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
735 735
 			// for a pull it is not an error if no auth was given
736 736
 			// to increase compatibility with the existing api it is defaulting to be empty
737
-			authConfig = &registry.AuthConfig{}
737
+			authConfig = &cliconfig.AuthConfig{}
738 738
 		}
739 739
 	}
740 740
 
... ...
@@ -802,7 +802,7 @@ func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w
802 802
 		return err
803 803
 	}
804 804
 	var (
805
-		config      *registry.AuthConfig
805
+		config      *cliconfig.AuthConfig
806 806
 		authEncoded = r.Header.Get("X-Registry-Auth")
807 807
 		headers     = map[string][]string{}
808 808
 	)
... ...
@@ -812,7 +812,7 @@ func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w
812 812
 		if err := json.NewDecoder(authJson).Decode(&config); err != nil {
813 813
 			// for a search it is not an error if no auth was given
814 814
 			// to increase compatibility with the existing api it is defaulting to be empty
815
-			config = &registry.AuthConfig{}
815
+			config = &cliconfig.AuthConfig{}
816 816
 		}
817 817
 	}
818 818
 	for k, v := range r.Header {
... ...
@@ -841,7 +841,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
841 841
 	if err := parseForm(r); err != nil {
842 842
 		return err
843 843
 	}
844
-	authConfig := &registry.AuthConfig{}
844
+	authConfig := &cliconfig.AuthConfig{}
845 845
 
846 846
 	authEncoded := r.Header.Get("X-Registry-Auth")
847 847
 	if authEncoded != "" {
... ...
@@ -849,7 +849,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
849 849
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
850 850
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
851 851
 			// to increase compatibility to existing api it is defaulting to be empty
852
-			authConfig = &registry.AuthConfig{}
852
+			authConfig = &cliconfig.AuthConfig{}
853 853
 		}
854 854
 	} else {
855 855
 		// the old format is supported for compatibility if there was no authConfig header
... ...
@@ -1263,9 +1263,9 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
1263 1263
 	}
1264 1264
 	var (
1265 1265
 		authEncoded       = r.Header.Get("X-Registry-Auth")
1266
-		authConfig        = &registry.AuthConfig{}
1266
+		authConfig        = &cliconfig.AuthConfig{}
1267 1267
 		configFileEncoded = r.Header.Get("X-Registry-Config")
1268
-		configFile        = &registry.ConfigFile{}
1268
+		configFile        = &cliconfig.ConfigFile{}
1269 1269
 		buildConfig       = builder.NewBuildConfig()
1270 1270
 	)
1271 1271
 
... ...
@@ -1278,7 +1278,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
1278 1278
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
1279 1279
 			// for a pull it is not an error if no auth was given
1280 1280
 			// to increase compatibility with the existing api it is defaulting to be empty
1281
-			authConfig = &registry.AuthConfig{}
1281
+			authConfig = &cliconfig.AuthConfig{}
1282 1282
 		}
1283 1283
 	}
1284 1284
 
... ...
@@ -1287,7 +1287,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
1287 1287
 		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
1288 1288
 			// for a pull it is not an error if no auth was given
1289 1289
 			// to increase compatibility with the existing api it is defaulting to be empty
1290
-			configFile = &registry.ConfigFile{}
1290
+			configFile = &cliconfig.ConfigFile{}
1291 1291
 		}
1292 1292
 	}
1293 1293
 
... ...
@@ -30,13 +30,13 @@ import (
30 30
 	"github.com/docker/docker/api"
31 31
 	"github.com/docker/docker/builder/command"
32 32
 	"github.com/docker/docker/builder/parser"
33
+	"github.com/docker/docker/cliconfig"
33 34
 	"github.com/docker/docker/daemon"
34 35
 	"github.com/docker/docker/pkg/fileutils"
35 36
 	"github.com/docker/docker/pkg/streamformatter"
36 37
 	"github.com/docker/docker/pkg/stringid"
37 38
 	"github.com/docker/docker/pkg/symlink"
38 39
 	"github.com/docker/docker/pkg/tarsum"
39
-	"github.com/docker/docker/registry"
40 40
 	"github.com/docker/docker/runconfig"
41 41
 	"github.com/docker/docker/utils"
42 42
 )
... ...
@@ -99,8 +99,8 @@ type Builder struct {
99 99
 	// the final configs of the Dockerfile but dont want the layers
100 100
 	disableCommit bool
101 101
 
102
-	AuthConfig *registry.AuthConfig
103
-	ConfigFile *registry.ConfigFile
102
+	AuthConfig *cliconfig.AuthConfig
103
+	ConfigFile *cliconfig.ConfigFile
104 104
 
105 105
 	// Deprecated, original writer used for ImagePull. To be removed.
106 106
 	OutOld          io.Writer
... ...
@@ -36,6 +36,7 @@ import (
36 36
 	"github.com/docker/docker/pkg/system"
37 37
 	"github.com/docker/docker/pkg/tarsum"
38 38
 	"github.com/docker/docker/pkg/urlutil"
39
+	"github.com/docker/docker/registry"
39 40
 	"github.com/docker/docker/runconfig"
40 41
 )
41 42
 
... ...
@@ -443,7 +444,7 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
443 443
 		if err != nil {
444 444
 			return nil, err
445 445
 		}
446
-		resolvedAuth := b.ConfigFile.ResolveAuthConfig(repoInfo.Index)
446
+		resolvedAuth := registry.ResolveAuthConfig(b.ConfigFile, repoInfo.Index)
447 447
 		pullRegistryAuth = &resolvedAuth
448 448
 	}
449 449
 
... ...
@@ -12,6 +12,7 @@ import (
12 12
 
13 13
 	"github.com/docker/docker/api"
14 14
 	"github.com/docker/docker/builder/parser"
15
+	"github.com/docker/docker/cliconfig"
15 16
 	"github.com/docker/docker/daemon"
16 17
 	"github.com/docker/docker/graph"
17 18
 	"github.com/docker/docker/pkg/archive"
... ...
@@ -50,8 +51,8 @@ type Config struct {
50 50
 	CpuShares      int64
51 51
 	CpuSetCpus     string
52 52
 	CpuSetMems     string
53
-	AuthConfig     *registry.AuthConfig
54
-	ConfigFile     *registry.ConfigFile
53
+	AuthConfig     *cliconfig.AuthConfig
54
+	ConfigFile     *cliconfig.ConfigFile
55 55
 
56 56
 	Stdout  io.Writer
57 57
 	Context io.ReadCloser
... ...
@@ -76,8 +77,8 @@ func (b *Config) WaitCancelled() <-chan struct{} {
76 76
 
77 77
 func NewBuildConfig() *Config {
78 78
 	return &Config{
79
-		AuthConfig: &registry.AuthConfig{},
80
-		ConfigFile: &registry.ConfigFile{},
79
+		AuthConfig: &cliconfig.AuthConfig{},
80
+		ConfigFile: &cliconfig.ConfigFile{},
81 81
 		cancelled:  make(chan struct{}),
82 82
 	}
83 83
 }
84 84
new file mode 100644
... ...
@@ -0,0 +1,208 @@
0
+package cliconfig
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"errors"
6
+	"fmt"
7
+	"io/ioutil"
8
+	"os"
9
+	"path/filepath"
10
+	"strings"
11
+
12
+	"github.com/docker/docker/pkg/homedir"
13
+)
14
+
15
+const (
16
+	// Where we store the config file
17
+	CONFIGFILE     = "config.json"
18
+	OLD_CONFIGFILE = ".dockercfg"
19
+
20
+	// This constant is only used for really old config files when the
21
+	// URL wasn't saved as part of the config file and it was just
22
+	// assumed to be this value.
23
+	DEFAULT_INDEXSERVER = "https://index.docker.io/v1/"
24
+)
25
+
26
+var (
27
+	ErrConfigFileMissing = errors.New("The Auth config file is missing")
28
+)
29
+
30
+// Registry Auth Info
31
+type AuthConfig struct {
32
+	Username      string `json:"username,omitempty"`
33
+	Password      string `json:"password,omitempty"`
34
+	Auth          string `json:"auth"`
35
+	Email         string `json:"email"`
36
+	ServerAddress string `json:"serveraddress,omitempty"`
37
+}
38
+
39
+// ~/.docker/config.json file info
40
+type ConfigFile struct {
41
+	AuthConfigs map[string]AuthConfig `json:"auths"`
42
+	HttpHeaders map[string]string     `json:"HttpHeaders,omitempty"`
43
+	filename    string                // Note: not serialized - for internal use only
44
+}
45
+
46
+func NewConfigFile(fn string) *ConfigFile {
47
+	return &ConfigFile{
48
+		AuthConfigs: make(map[string]AuthConfig),
49
+		HttpHeaders: make(map[string]string),
50
+		filename:    fn,
51
+	}
52
+}
53
+
54
+// load up the auth config information and return values
55
+// FIXME: use the internal golang config parser
56
+func Load(configDir string) (*ConfigFile, error) {
57
+	if configDir == "" {
58
+		configDir = filepath.Join(homedir.Get(), ".docker")
59
+	}
60
+
61
+	configFile := ConfigFile{
62
+		AuthConfigs: make(map[string]AuthConfig),
63
+		filename:    filepath.Join(configDir, CONFIGFILE),
64
+	}
65
+
66
+	// Try happy path first - latest config file
67
+	if _, err := os.Stat(configFile.filename); err == nil {
68
+		file, err := os.Open(configFile.filename)
69
+		if err != nil {
70
+			return &configFile, err
71
+		}
72
+		defer file.Close()
73
+
74
+		if err := json.NewDecoder(file).Decode(&configFile); err != nil {
75
+			return &configFile, err
76
+		}
77
+
78
+		for addr, ac := range configFile.AuthConfigs {
79
+			ac.Username, ac.Password, err = DecodeAuth(ac.Auth)
80
+			if err != nil {
81
+				return &configFile, err
82
+			}
83
+			ac.Auth = ""
84
+			ac.ServerAddress = addr
85
+			configFile.AuthConfigs[addr] = ac
86
+		}
87
+
88
+		return &configFile, nil
89
+	} else if !os.IsNotExist(err) {
90
+		// if file is there but we can't stat it for any reason other
91
+		// than it doesn't exist then stop
92
+		return &configFile, err
93
+	}
94
+
95
+	// Can't find latest config file so check for the old one
96
+	confFile := filepath.Join(homedir.Get(), OLD_CONFIGFILE)
97
+
98
+	if _, err := os.Stat(confFile); err != nil {
99
+		return &configFile, nil //missing file is not an error
100
+	}
101
+
102
+	b, err := ioutil.ReadFile(confFile)
103
+	if err != nil {
104
+		return &configFile, err
105
+	}
106
+
107
+	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
108
+		arr := strings.Split(string(b), "\n")
109
+		if len(arr) < 2 {
110
+			return &configFile, fmt.Errorf("The Auth config file is empty")
111
+		}
112
+		authConfig := AuthConfig{}
113
+		origAuth := strings.Split(arr[0], " = ")
114
+		if len(origAuth) != 2 {
115
+			return &configFile, fmt.Errorf("Invalid Auth config file")
116
+		}
117
+		authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
118
+		if err != nil {
119
+			return &configFile, err
120
+		}
121
+		origEmail := strings.Split(arr[1], " = ")
122
+		if len(origEmail) != 2 {
123
+			return &configFile, fmt.Errorf("Invalid Auth config file")
124
+		}
125
+		authConfig.Email = origEmail[1]
126
+		authConfig.ServerAddress = DEFAULT_INDEXSERVER
127
+		configFile.AuthConfigs[DEFAULT_INDEXSERVER] = authConfig
128
+	} else {
129
+		for k, authConfig := range configFile.AuthConfigs {
130
+			authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth)
131
+			if err != nil {
132
+				return &configFile, err
133
+			}
134
+			authConfig.Auth = ""
135
+			authConfig.ServerAddress = k
136
+			configFile.AuthConfigs[k] = authConfig
137
+		}
138
+	}
139
+	return &configFile, nil
140
+}
141
+
142
+func (configFile *ConfigFile) Save() error {
143
+	// Encode sensitive data into a new/temp struct
144
+	tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
145
+	for k, authConfig := range configFile.AuthConfigs {
146
+		authCopy := authConfig
147
+
148
+		authCopy.Auth = EncodeAuth(&authCopy)
149
+		authCopy.Username = ""
150
+		authCopy.Password = ""
151
+		authCopy.ServerAddress = ""
152
+		tmpAuthConfigs[k] = authCopy
153
+	}
154
+
155
+	saveAuthConfigs := configFile.AuthConfigs
156
+	configFile.AuthConfigs = tmpAuthConfigs
157
+	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
158
+
159
+	data, err := json.MarshalIndent(configFile, "", "\t")
160
+	if err != nil {
161
+		return err
162
+	}
163
+
164
+	if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
165
+		return err
166
+	}
167
+
168
+	err = ioutil.WriteFile(configFile.filename, data, 0600)
169
+	if err != nil {
170
+		return err
171
+	}
172
+
173
+	return nil
174
+}
175
+
176
+func (config *ConfigFile) Filename() string {
177
+	return config.filename
178
+}
179
+
180
+// create a base64 encoded auth string to store in config
181
+func EncodeAuth(authConfig *AuthConfig) string {
182
+	authStr := authConfig.Username + ":" + authConfig.Password
183
+	msg := []byte(authStr)
184
+	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
185
+	base64.StdEncoding.Encode(encoded, msg)
186
+	return string(encoded)
187
+}
188
+
189
+// decode the auth string
190
+func DecodeAuth(authStr string) (string, string, error) {
191
+	decLen := base64.StdEncoding.DecodedLen(len(authStr))
192
+	decoded := make([]byte, decLen)
193
+	authByte := []byte(authStr)
194
+	n, err := base64.StdEncoding.Decode(decoded, authByte)
195
+	if err != nil {
196
+		return "", "", err
197
+	}
198
+	if n > decLen {
199
+		return "", "", fmt.Errorf("Something went wrong decoding auth config")
200
+	}
201
+	arr := strings.SplitN(string(decoded), ":", 2)
202
+	if len(arr) != 2 {
203
+		return "", "", fmt.Errorf("Invalid auth configuration file")
204
+	}
205
+	password := strings.Trim(arr[1], "\x00")
206
+	return arr[0], password, nil
207
+}
0 208
new file mode 100644
... ...
@@ -0,0 +1,157 @@
0
+package cliconfig
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"path/filepath"
6
+	"runtime"
7
+	"strings"
8
+	"testing"
9
+
10
+	"github.com/docker/docker/pkg/homedir"
11
+)
12
+
13
+func TestMissingFile(t *testing.T) {
14
+	tmpHome, _ := ioutil.TempDir("", "config-test")
15
+
16
+	config, err := Load(tmpHome)
17
+	if err != nil {
18
+		t.Fatalf("Failed loading on missing file: %q", err)
19
+	}
20
+
21
+	// Now save it and make sure it shows up in new form
22
+	err = config.Save()
23
+	if err != nil {
24
+		t.Fatalf("Failed to save: %q", err)
25
+	}
26
+
27
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
28
+	if !strings.Contains(string(buf), `"auths":`) {
29
+		t.Fatalf("Should have save in new form: %s", string(buf))
30
+	}
31
+}
32
+
33
+func TestSaveFileToDirs(t *testing.T) {
34
+	tmpHome, _ := ioutil.TempDir("", "config-test")
35
+
36
+	tmpHome += "/.docker"
37
+
38
+	config, err := Load(tmpHome)
39
+	if err != nil {
40
+		t.Fatalf("Failed loading on missing file: %q", err)
41
+	}
42
+
43
+	// Now save it and make sure it shows up in new form
44
+	err = config.Save()
45
+	if err != nil {
46
+		t.Fatalf("Failed to save: %q", err)
47
+	}
48
+
49
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
50
+	if !strings.Contains(string(buf), `"auths":`) {
51
+		t.Fatalf("Should have save in new form: %s", string(buf))
52
+	}
53
+}
54
+
55
+func TestEmptyFile(t *testing.T) {
56
+	tmpHome, _ := ioutil.TempDir("", "config-test")
57
+	fn := filepath.Join(tmpHome, CONFIGFILE)
58
+	ioutil.WriteFile(fn, []byte(""), 0600)
59
+
60
+	_, err := Load(tmpHome)
61
+	if err == nil {
62
+		t.Fatalf("Was supposed to fail")
63
+	}
64
+}
65
+
66
+func TestEmptyJson(t *testing.T) {
67
+	tmpHome, _ := ioutil.TempDir("", "config-test")
68
+	fn := filepath.Join(tmpHome, CONFIGFILE)
69
+	ioutil.WriteFile(fn, []byte("{}"), 0600)
70
+
71
+	config, err := Load(tmpHome)
72
+	if err != nil {
73
+		t.Fatalf("Failed loading on empty json file: %q", err)
74
+	}
75
+
76
+	// Now save it and make sure it shows up in new form
77
+	err = config.Save()
78
+	if err != nil {
79
+		t.Fatalf("Failed to save: %q", err)
80
+	}
81
+
82
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
83
+	if !strings.Contains(string(buf), `"auths":`) {
84
+		t.Fatalf("Should have save in new form: %s", string(buf))
85
+	}
86
+}
87
+
88
+func TestOldJson(t *testing.T) {
89
+	if runtime.GOOS == "windows" {
90
+		return
91
+	}
92
+
93
+	tmpHome, _ := ioutil.TempDir("", "config-test")
94
+	defer os.RemoveAll(tmpHome)
95
+
96
+	homeKey := homedir.Key()
97
+	homeVal := homedir.Get()
98
+
99
+	defer func() { os.Setenv(homeKey, homeVal) }()
100
+	os.Setenv(homeKey, tmpHome)
101
+
102
+	fn := filepath.Join(tmpHome, OLD_CONFIGFILE)
103
+	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
104
+	ioutil.WriteFile(fn, []byte(js), 0600)
105
+
106
+	config, err := Load(tmpHome)
107
+	if err != nil {
108
+		t.Fatalf("Failed loading on empty json file: %q", err)
109
+	}
110
+
111
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
112
+	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
113
+		t.Fatalf("Missing data from parsing:\n%q", config)
114
+	}
115
+
116
+	// Now save it and make sure it shows up in new form
117
+	err = config.Save()
118
+	if err != nil {
119
+		t.Fatalf("Failed to save: %q", err)
120
+	}
121
+
122
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
123
+	if !strings.Contains(string(buf), `"auths":`) ||
124
+		!strings.Contains(string(buf), "user@example.com") {
125
+		t.Fatalf("Should have save in new form: %s", string(buf))
126
+	}
127
+}
128
+
129
+func TestNewJson(t *testing.T) {
130
+	tmpHome, _ := ioutil.TempDir("", "config-test")
131
+	fn := filepath.Join(tmpHome, CONFIGFILE)
132
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
133
+	ioutil.WriteFile(fn, []byte(js), 0600)
134
+
135
+	config, err := Load(tmpHome)
136
+	if err != nil {
137
+		t.Fatalf("Failed loading on empty json file: %q", err)
138
+	}
139
+
140
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
141
+	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
142
+		t.Fatalf("Missing data from parsing:\n%q", config)
143
+	}
144
+
145
+	// Now save it and make sure it shows up in new form
146
+	err = config.Save()
147
+	if err != nil {
148
+		t.Fatalf("Failed to save: %q", err)
149
+	}
150
+
151
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
152
+	if !strings.Contains(string(buf), `"auths":`) ||
153
+		!strings.Contains(string(buf), "user@example.com") {
154
+		t.Fatalf("Should have save in new form: %s", string(buf))
155
+	}
156
+}
... ...
@@ -12,6 +12,7 @@ import (
12 12
 
13 13
 	"github.com/Sirupsen/logrus"
14 14
 	"github.com/docker/distribution/digest"
15
+	"github.com/docker/docker/cliconfig"
15 16
 	"github.com/docker/docker/image"
16 17
 	"github.com/docker/docker/pkg/progressreader"
17 18
 	"github.com/docker/docker/pkg/streamformatter"
... ...
@@ -23,7 +24,7 @@ import (
23 23
 type ImagePullConfig struct {
24 24
 	Parallel    bool
25 25
 	MetaHeaders map[string][]string
26
-	AuthConfig  *registry.AuthConfig
26
+	AuthConfig  *cliconfig.AuthConfig
27 27
 	Json        bool
28 28
 	OutStream   io.Writer
29 29
 }
... ...
@@ -12,6 +12,7 @@ import (
12 12
 
13 13
 	"github.com/Sirupsen/logrus"
14 14
 	"github.com/docker/distribution/digest"
15
+	"github.com/docker/docker/cliconfig"
15 16
 	"github.com/docker/docker/image"
16 17
 	"github.com/docker/docker/pkg/progressreader"
17 18
 	"github.com/docker/docker/pkg/streamformatter"
... ...
@@ -26,7 +27,7 @@ var ErrV2RegistryUnavailable = errors.New("error v2 registry unavailable")
26 26
 
27 27
 type ImagePushConfig struct {
28 28
 	MetaHeaders map[string][]string
29
-	AuthConfig  *registry.AuthConfig
29
+	AuthConfig  *cliconfig.AuthConfig
30 30
 	Tag         string
31 31
 	Json        bool
32 32
 	OutStream   io.Writer
... ...
@@ -18,6 +18,7 @@ import (
18 18
 
19 19
 	"github.com/Sirupsen/logrus"
20 20
 	apiserver "github.com/docker/docker/api/server"
21
+	"github.com/docker/docker/cliconfig"
21 22
 	"github.com/docker/docker/daemon"
22 23
 	"github.com/docker/docker/daemon/execdriver"
23 24
 	"github.com/docker/docker/engine"
... ...
@@ -28,7 +29,6 @@ import (
28 28
 	"github.com/docker/docker/pkg/ioutils"
29 29
 	"github.com/docker/docker/pkg/reexec"
30 30
 	"github.com/docker/docker/pkg/stringid"
31
-	"github.com/docker/docker/registry"
32 31
 	"github.com/docker/docker/runconfig"
33 32
 	"github.com/docker/docker/utils"
34 33
 )
... ...
@@ -135,7 +135,7 @@ func setupBaseImage() {
135 135
 		imagePullConfig := &graph.ImagePullConfig{
136 136
 			Parallel:   true,
137 137
 			OutStream:  ioutils.NopWriteCloser(os.Stdout),
138
-			AuthConfig: &registry.AuthConfig{},
138
+			AuthConfig: &cliconfig.AuthConfig{},
139 139
 		}
140 140
 		d := getDaemon(eng)
141 141
 		if err := d.Repositories().Pull(unitTestImageName, "", imagePullConfig); err != nil {
... ...
@@ -1,51 +1,21 @@
1 1
 package registry
2 2
 
3 3
 import (
4
-	"encoding/base64"
5 4
 	"encoding/json"
6
-	"errors"
7 5
 	"fmt"
8 6
 	"io/ioutil"
9 7
 	"net/http"
10
-	"os"
11
-	"path/filepath"
12 8
 	"strings"
13 9
 	"sync"
14 10
 	"time"
15 11
 
16 12
 	"github.com/Sirupsen/logrus"
17
-	"github.com/docker/docker/pkg/homedir"
13
+	"github.com/docker/docker/cliconfig"
18 14
 	"github.com/docker/docker/pkg/requestdecorator"
19 15
 )
20 16
 
21
-const (
22
-	// Where we store the config file
23
-	CONFIGFILE     = "config.json"
24
-	OLD_CONFIGFILE = ".dockercfg"
25
-)
26
-
27
-var (
28
-	ErrConfigFileMissing = errors.New("The Auth config file is missing")
29
-)
30
-
31
-// Registry Auth Info
32
-type AuthConfig struct {
33
-	Username      string `json:"username,omitempty"`
34
-	Password      string `json:"password,omitempty"`
35
-	Auth          string `json:"auth"`
36
-	Email         string `json:"email"`
37
-	ServerAddress string `json:"serveraddress,omitempty"`
38
-}
39
-
40
-// ~/.docker/config.json file info
41
-type ConfigFile struct {
42
-	AuthConfigs map[string]AuthConfig `json:"auths"`
43
-	HttpHeaders map[string]string     `json:"HttpHeaders,omitempty"`
44
-	filename    string                // Note: not serialized - for internal use only
45
-}
46
-
47 17
 type RequestAuthorization struct {
48
-	authConfig       *AuthConfig
18
+	authConfig       *cliconfig.AuthConfig
49 19
 	registryEndpoint *Endpoint
50 20
 	resource         string
51 21
 	scope            string
... ...
@@ -56,7 +26,7 @@ type RequestAuthorization struct {
56 56
 	tokenExpiration time.Time
57 57
 }
58 58
 
59
-func NewRequestAuthorization(authConfig *AuthConfig, registryEndpoint *Endpoint, resource, scope string, actions []string) *RequestAuthorization {
59
+func NewRequestAuthorization(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, resource, scope string, actions []string) *RequestAuthorization {
60 60
 	return &RequestAuthorization{
61 61
 		authConfig:       authConfig,
62 62
 		registryEndpoint: registryEndpoint,
... ...
@@ -121,160 +91,8 @@ func (auth *RequestAuthorization) Authorize(req *http.Request) error {
121 121
 	return nil
122 122
 }
123 123
 
124
-// create a base64 encoded auth string to store in config
125
-func encodeAuth(authConfig *AuthConfig) string {
126
-	authStr := authConfig.Username + ":" + authConfig.Password
127
-	msg := []byte(authStr)
128
-	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
129
-	base64.StdEncoding.Encode(encoded, msg)
130
-	return string(encoded)
131
-}
132
-
133
-// decode the auth string
134
-func decodeAuth(authStr string) (string, string, error) {
135
-	decLen := base64.StdEncoding.DecodedLen(len(authStr))
136
-	decoded := make([]byte, decLen)
137
-	authByte := []byte(authStr)
138
-	n, err := base64.StdEncoding.Decode(decoded, authByte)
139
-	if err != nil {
140
-		return "", "", err
141
-	}
142
-	if n > decLen {
143
-		return "", "", fmt.Errorf("Something went wrong decoding auth config")
144
-	}
145
-	arr := strings.SplitN(string(decoded), ":", 2)
146
-	if len(arr) != 2 {
147
-		return "", "", fmt.Errorf("Invalid auth configuration file")
148
-	}
149
-	password := strings.Trim(arr[1], "\x00")
150
-	return arr[0], password, nil
151
-}
152
-
153
-// load up the auth config information and return values
154
-// FIXME: use the internal golang config parser
155
-func LoadConfig(configDir string) (*ConfigFile, error) {
156
-	if configDir == "" {
157
-		configDir = filepath.Join(homedir.Get(), ".docker")
158
-	}
159
-
160
-	configFile := ConfigFile{
161
-		AuthConfigs: make(map[string]AuthConfig),
162
-		filename:    filepath.Join(configDir, CONFIGFILE),
163
-	}
164
-
165
-	// Try happy path first - latest config file
166
-	if _, err := os.Stat(configFile.filename); err == nil {
167
-		file, err := os.Open(configFile.filename)
168
-		if err != nil {
169
-			return &configFile, err
170
-		}
171
-		defer file.Close()
172
-
173
-		if err := json.NewDecoder(file).Decode(&configFile); err != nil {
174
-			return &configFile, err
175
-		}
176
-
177
-		for addr, ac := range configFile.AuthConfigs {
178
-			ac.Username, ac.Password, err = decodeAuth(ac.Auth)
179
-			if err != nil {
180
-				return &configFile, err
181
-			}
182
-			ac.Auth = ""
183
-			ac.ServerAddress = addr
184
-			configFile.AuthConfigs[addr] = ac
185
-		}
186
-
187
-		return &configFile, nil
188
-	} else if !os.IsNotExist(err) {
189
-		// if file is there but we can't stat it for any reason other
190
-		// than it doesn't exist then stop
191
-		return &configFile, err
192
-	}
193
-
194
-	// Can't find latest config file so check for the old one
195
-	confFile := filepath.Join(homedir.Get(), OLD_CONFIGFILE)
196
-
197
-	if _, err := os.Stat(confFile); err != nil {
198
-		return &configFile, nil //missing file is not an error
199
-	}
200
-
201
-	b, err := ioutil.ReadFile(confFile)
202
-	if err != nil {
203
-		return &configFile, err
204
-	}
205
-
206
-	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
207
-		arr := strings.Split(string(b), "\n")
208
-		if len(arr) < 2 {
209
-			return &configFile, fmt.Errorf("The Auth config file is empty")
210
-		}
211
-		authConfig := AuthConfig{}
212
-		origAuth := strings.Split(arr[0], " = ")
213
-		if len(origAuth) != 2 {
214
-			return &configFile, fmt.Errorf("Invalid Auth config file")
215
-		}
216
-		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
217
-		if err != nil {
218
-			return &configFile, err
219
-		}
220
-		origEmail := strings.Split(arr[1], " = ")
221
-		if len(origEmail) != 2 {
222
-			return &configFile, fmt.Errorf("Invalid Auth config file")
223
-		}
224
-		authConfig.Email = origEmail[1]
225
-		authConfig.ServerAddress = IndexServerAddress()
226
-		// *TODO: Switch to using IndexServerName() instead?
227
-		configFile.AuthConfigs[IndexServerAddress()] = authConfig
228
-	} else {
229
-		for k, authConfig := range configFile.AuthConfigs {
230
-			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
231
-			if err != nil {
232
-				return &configFile, err
233
-			}
234
-			authConfig.Auth = ""
235
-			authConfig.ServerAddress = k
236
-			configFile.AuthConfigs[k] = authConfig
237
-		}
238
-	}
239
-	return &configFile, nil
240
-}
241
-
242
-func (configFile *ConfigFile) Save() error {
243
-	// Encode sensitive data into a new/temp struct
244
-	tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
245
-	for k, authConfig := range configFile.AuthConfigs {
246
-		authCopy := authConfig
247
-
248
-		authCopy.Auth = encodeAuth(&authCopy)
249
-		authCopy.Username = ""
250
-		authCopy.Password = ""
251
-		authCopy.ServerAddress = ""
252
-		tmpAuthConfigs[k] = authCopy
253
-	}
254
-
255
-	saveAuthConfigs := configFile.AuthConfigs
256
-	configFile.AuthConfigs = tmpAuthConfigs
257
-	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
258
-
259
-	data, err := json.MarshalIndent(configFile, "", "\t")
260
-	if err != nil {
261
-		return err
262
-	}
263
-
264
-	if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
265
-		return err
266
-	}
267
-
268
-	err = ioutil.WriteFile(configFile.filename, data, 0600)
269
-	if err != nil {
270
-		return err
271
-	}
272
-
273
-	return nil
274
-}
275
-
276 124
 // Login tries to register/login to the registry server.
277
-func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
125
+func Login(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
278 126
 	// Separates the v2 registry login logic from the v1 logic.
279 127
 	if registryEndpoint.Version == APIVersion2 {
280 128
 		return loginV2(authConfig, registryEndpoint, factory)
... ...
@@ -283,7 +101,7 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestd
283 283
 }
284 284
 
285 285
 // loginV1 tries to register/login to the v1 registry server.
286
-func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
286
+func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
287 287
 	var (
288 288
 		status        string
289 289
 		reqBody       []byte
... ...
@@ -396,7 +214,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *reques
396 396
 // now, users should create their account through other means like directly from a web page
397 397
 // served by the v2 registry service provider. Whether this will be supported in the future
398 398
 // is to be determined.
399
-func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
399
+func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
400 400
 	logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
401 401
 	var (
402 402
 		err       error
... ...
@@ -429,7 +247,7 @@ func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *reques
429 429
 	return "", fmt.Errorf("no successful auth challenge for %s - errors: %s", registryEndpoint, allErrors)
430 430
 }
431 431
 
432
-func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
432
+func tryV2BasicAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
433 433
 	req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
434 434
 	if err != nil {
435 435
 		return err
... ...
@@ -450,7 +268,7 @@ func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, regis
450 450
 	return nil
451 451
 }
452 452
 
453
-func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
453
+func tryV2TokenAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
454 454
 	token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client, factory)
455 455
 	if err != nil {
456 456
 		return err
... ...
@@ -477,7 +295,7 @@ func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, regis
477 477
 }
478 478
 
479 479
 // this method matches a auth configuration to a server address or a url
480
-func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
480
+func ResolveAuthConfig(config *cliconfig.ConfigFile, index *IndexInfo) cliconfig.AuthConfig {
481 481
 	configKey := index.GetAuthConfigKey()
482 482
 	// First try the happy case
483 483
 	if c, found := config.AuthConfigs[configKey]; found || index.Official {
... ...
@@ -499,16 +317,12 @@ func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
499 499
 
500 500
 	// Maybe they have a legacy config file, we will iterate the keys converting
501 501
 	// them to the new format and testing
502
-	for registry, config := range config.AuthConfigs {
502
+	for registry, ac := range config.AuthConfigs {
503 503
 		if configKey == convertToHostname(registry) {
504
-			return config
504
+			return ac
505 505
 		}
506 506
 	}
507 507
 
508 508
 	// When all else fails, return an empty auth config
509
-	return AuthConfig{}
510
-}
511
-
512
-func (config *ConfigFile) Filename() string {
513
-	return config.filename
509
+	return cliconfig.AuthConfig{}
514 510
 }
... ...
@@ -5,14 +5,16 @@ import (
5 5
 	"os"
6 6
 	"path/filepath"
7 7
 	"testing"
8
+
9
+	"github.com/docker/docker/cliconfig"
8 10
 )
9 11
 
10 12
 func TestEncodeAuth(t *testing.T) {
11
-	newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
12
-	authStr := encodeAuth(newAuthConfig)
13
-	decAuthConfig := &AuthConfig{}
13
+	newAuthConfig := &cliconfig.AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
14
+	authStr := cliconfig.EncodeAuth(newAuthConfig)
15
+	decAuthConfig := &cliconfig.AuthConfig{}
14 16
 	var err error
15
-	decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
17
+	decAuthConfig.Username, decAuthConfig.Password, err = cliconfig.DecodeAuth(authStr)
16 18
 	if err != nil {
17 19
 		t.Fatal(err)
18 20
 	}
... ...
@@ -27,19 +29,16 @@ func TestEncodeAuth(t *testing.T) {
27 27
 	}
28 28
 }
29 29
 
30
-func setupTempConfigFile() (*ConfigFile, error) {
30
+func setupTempConfigFile() (*cliconfig.ConfigFile, error) {
31 31
 	root, err := ioutil.TempDir("", "docker-test-auth")
32 32
 	if err != nil {
33 33
 		return nil, err
34 34
 	}
35
-	root = filepath.Join(root, CONFIGFILE)
36
-	configFile := &ConfigFile{
37
-		AuthConfigs: make(map[string]AuthConfig),
38
-		filename:    root,
39
-	}
35
+	root = filepath.Join(root, cliconfig.CONFIGFILE)
36
+	configFile := cliconfig.NewConfigFile(root)
40 37
 
41 38
 	for _, registry := range []string{"testIndex", IndexServerAddress()} {
42
-		configFile.AuthConfigs[registry] = AuthConfig{
39
+		configFile.AuthConfigs[registry] = cliconfig.AuthConfig{
43 40
 			Username: "docker-user",
44 41
 			Password: "docker-pass",
45 42
 			Email:    "docker@docker.io",
... ...
@@ -54,7 +53,7 @@ func TestSameAuthDataPostSave(t *testing.T) {
54 54
 	if err != nil {
55 55
 		t.Fatal(err)
56 56
 	}
57
-	defer os.RemoveAll(configFile.filename)
57
+	defer os.RemoveAll(configFile.Filename())
58 58
 
59 59
 	err = configFile.Save()
60 60
 	if err != nil {
... ...
@@ -81,7 +80,7 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
81 81
 	if err != nil {
82 82
 		t.Fatal(err)
83 83
 	}
84
-	defer os.RemoveAll(configFile.filename)
84
+	defer os.RemoveAll(configFile.Filename())
85 85
 
86 86
 	indexConfig := configFile.AuthConfigs[IndexServerAddress()]
87 87
 
... ...
@@ -92,10 +91,10 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
92 92
 		Official: false,
93 93
 	}
94 94
 
95
-	resolved := configFile.ResolveAuthConfig(officialIndex)
95
+	resolved := ResolveAuthConfig(configFile, officialIndex)
96 96
 	assertEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to return IndexServerAddress()")
97 97
 
98
-	resolved = configFile.ResolveAuthConfig(privateIndex)
98
+	resolved = ResolveAuthConfig(configFile, privateIndex)
99 99
 	assertNotEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to not return IndexServerAddress()")
100 100
 }
101 101
 
... ...
@@ -104,26 +103,26 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
104 104
 	if err != nil {
105 105
 		t.Fatal(err)
106 106
 	}
107
-	defer os.RemoveAll(configFile.filename)
107
+	defer os.RemoveAll(configFile.Filename())
108 108
 
109
-	registryAuth := AuthConfig{
109
+	registryAuth := cliconfig.AuthConfig{
110 110
 		Username: "foo-user",
111 111
 		Password: "foo-pass",
112 112
 		Email:    "foo@example.com",
113 113
 	}
114
-	localAuth := AuthConfig{
114
+	localAuth := cliconfig.AuthConfig{
115 115
 		Username: "bar-user",
116 116
 		Password: "bar-pass",
117 117
 		Email:    "bar@example.com",
118 118
 	}
119
-	officialAuth := AuthConfig{
119
+	officialAuth := cliconfig.AuthConfig{
120 120
 		Username: "baz-user",
121 121
 		Password: "baz-pass",
122 122
 		Email:    "baz@example.com",
123 123
 	}
124 124
 	configFile.AuthConfigs[IndexServerAddress()] = officialAuth
125 125
 
126
-	expectedAuths := map[string]AuthConfig{
126
+	expectedAuths := map[string]cliconfig.AuthConfig{
127 127
 		"registry.example.com": registryAuth,
128 128
 		"localhost:8000":       localAuth,
129 129
 		"registry.com":         localAuth,
... ...
@@ -160,12 +159,12 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
160 160
 		}
161 161
 		for _, registry := range registries {
162 162
 			configFile.AuthConfigs[registry] = configured
163
-			resolved := configFile.ResolveAuthConfig(index)
163
+			resolved := ResolveAuthConfig(configFile, index)
164 164
 			if resolved.Email != configured.Email {
165 165
 				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
166 166
 			}
167 167
 			delete(configFile.AuthConfigs, registry)
168
-			resolved = configFile.ResolveAuthConfig(index)
168
+			resolved = ResolveAuthConfig(configFile, index)
169 169
 			if resolved.Email == configured.Email {
170 170
 				t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)
171 171
 			}
172 172
deleted file mode 100644
... ...
@@ -1,157 +0,0 @@
1
-package registry
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"path/filepath"
7
-	"runtime"
8
-	"strings"
9
-	"testing"
10
-
11
-	"github.com/docker/docker/pkg/homedir"
12
-)
13
-
14
-func TestMissingFile(t *testing.T) {
15
-	tmpHome, _ := ioutil.TempDir("", "config-test")
16
-
17
-	config, err := LoadConfig(tmpHome)
18
-	if err != nil {
19
-		t.Fatalf("Failed loading on missing file: %q", err)
20
-	}
21
-
22
-	// Now save it and make sure it shows up in new form
23
-	err = config.Save()
24
-	if err != nil {
25
-		t.Fatalf("Failed to save: %q", err)
26
-	}
27
-
28
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
29
-	if !strings.Contains(string(buf), `"auths":`) {
30
-		t.Fatalf("Should have save in new form: %s", string(buf))
31
-	}
32
-}
33
-
34
-func TestSaveFileToDirs(t *testing.T) {
35
-	tmpHome, _ := ioutil.TempDir("", "config-test")
36
-
37
-	tmpHome += "/.docker"
38
-
39
-	config, err := LoadConfig(tmpHome)
40
-	if err != nil {
41
-		t.Fatalf("Failed loading on missing file: %q", err)
42
-	}
43
-
44
-	// Now save it and make sure it shows up in new form
45
-	err = config.Save()
46
-	if err != nil {
47
-		t.Fatalf("Failed to save: %q", err)
48
-	}
49
-
50
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
51
-	if !strings.Contains(string(buf), `"auths":`) {
52
-		t.Fatalf("Should have save in new form: %s", string(buf))
53
-	}
54
-}
55
-
56
-func TestEmptyFile(t *testing.T) {
57
-	tmpHome, _ := ioutil.TempDir("", "config-test")
58
-	fn := filepath.Join(tmpHome, CONFIGFILE)
59
-	ioutil.WriteFile(fn, []byte(""), 0600)
60
-
61
-	_, err := LoadConfig(tmpHome)
62
-	if err == nil {
63
-		t.Fatalf("Was supposed to fail")
64
-	}
65
-}
66
-
67
-func TestEmptyJson(t *testing.T) {
68
-	tmpHome, _ := ioutil.TempDir("", "config-test")
69
-	fn := filepath.Join(tmpHome, CONFIGFILE)
70
-	ioutil.WriteFile(fn, []byte("{}"), 0600)
71
-
72
-	config, err := LoadConfig(tmpHome)
73
-	if err != nil {
74
-		t.Fatalf("Failed loading on empty json file: %q", err)
75
-	}
76
-
77
-	// Now save it and make sure it shows up in new form
78
-	err = config.Save()
79
-	if err != nil {
80
-		t.Fatalf("Failed to save: %q", err)
81
-	}
82
-
83
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
84
-	if !strings.Contains(string(buf), `"auths":`) {
85
-		t.Fatalf("Should have save in new form: %s", string(buf))
86
-	}
87
-}
88
-
89
-func TestOldJson(t *testing.T) {
90
-	if runtime.GOOS == "windows" {
91
-		return
92
-	}
93
-
94
-	tmpHome, _ := ioutil.TempDir("", "config-test")
95
-	defer os.RemoveAll(tmpHome)
96
-
97
-	homeKey := homedir.Key()
98
-	homeVal := homedir.Get()
99
-
100
-	defer func() { os.Setenv(homeKey, homeVal) }()
101
-	os.Setenv(homeKey, tmpHome)
102
-
103
-	fn := filepath.Join(tmpHome, OLD_CONFIGFILE)
104
-	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
105
-	ioutil.WriteFile(fn, []byte(js), 0600)
106
-
107
-	config, err := LoadConfig(tmpHome)
108
-	if err != nil {
109
-		t.Fatalf("Failed loading on empty json file: %q", err)
110
-	}
111
-
112
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
113
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
114
-		t.Fatalf("Missing data from parsing:\n%q", config)
115
-	}
116
-
117
-	// Now save it and make sure it shows up in new form
118
-	err = config.Save()
119
-	if err != nil {
120
-		t.Fatalf("Failed to save: %q", err)
121
-	}
122
-
123
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
124
-	if !strings.Contains(string(buf), `"auths":`) ||
125
-		!strings.Contains(string(buf), "user@example.com") {
126
-		t.Fatalf("Should have save in new form: %s", string(buf))
127
-	}
128
-}
129
-
130
-func TestNewJson(t *testing.T) {
131
-	tmpHome, _ := ioutil.TempDir("", "config-test")
132
-	fn := filepath.Join(tmpHome, CONFIGFILE)
133
-	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
134
-	ioutil.WriteFile(fn, []byte(js), 0600)
135
-
136
-	config, err := LoadConfig(tmpHome)
137
-	if err != nil {
138
-		t.Fatalf("Failed loading on empty json file: %q", err)
139
-	}
140
-
141
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
142
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
143
-		t.Fatalf("Missing data from parsing:\n%q", config)
144
-	}
145
-
146
-	// Now save it and make sure it shows up in new form
147
-	err = config.Save()
148
-	if err != nil {
149
-		t.Fatalf("Failed to save: %q", err)
150
-	}
151
-
152
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
153
-	if !strings.Contains(string(buf), `"auths":`) ||
154
-		!strings.Contains(string(buf), "user@example.com") {
155
-		t.Fatalf("Should have save in new form: %s", string(buf))
156
-	}
157
-}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"strings"
8 8
 	"testing"
9 9
 
10
+	"github.com/docker/docker/cliconfig"
10 11
 	"github.com/docker/docker/pkg/requestdecorator"
11 12
 )
12 13
 
... ...
@@ -20,7 +21,7 @@ const (
20 20
 )
21 21
 
22 22
 func spawnTestRegistrySession(t *testing.T) *Session {
23
-	authConfig := &AuthConfig{}
23
+	authConfig := &cliconfig.AuthConfig{}
24 24
 	endpoint, err := NewEndpoint(makeIndex("/v1/"))
25 25
 	if err != nil {
26 26
 		t.Fatal(err)
... ...
@@ -33,7 +34,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
33 33
 }
34 34
 
35 35
 func TestPublicSession(t *testing.T) {
36
-	authConfig := &AuthConfig{}
36
+	authConfig := &cliconfig.AuthConfig{}
37 37
 
38 38
 	getSessionDecorators := func(index *IndexInfo) int {
39 39
 		endpoint, err := NewEndpoint(index)
... ...
@@ -1,5 +1,7 @@
1 1
 package registry
2 2
 
3
+import "github.com/docker/docker/cliconfig"
4
+
3 5
 type Service struct {
4 6
 	Config *ServiceConfig
5 7
 }
... ...
@@ -15,7 +17,7 @@ func NewService(options *Options) *Service {
15 15
 // Auth contacts the public registry with the provided credentials,
16 16
 // and returns OK if authentication was sucessful.
17 17
 // It can be used to verify the validity of a client's credentials.
18
-func (s *Service) Auth(authConfig *AuthConfig) (string, error) {
18
+func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
19 19
 	addr := authConfig.ServerAddress
20 20
 	if addr == "" {
21 21
 		// Use the official registry address if not specified.
... ...
@@ -35,7 +37,7 @@ func (s *Service) Auth(authConfig *AuthConfig) (string, error) {
35 35
 
36 36
 // Search queries the public registry for images matching the specified
37 37
 // search terms, and returns the results.
38
-func (s *Service) Search(term string, authConfig *AuthConfig, headers map[string][]string) (*SearchResults, error) {
38
+func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
39 39
 	repoInfo, err := s.ResolveRepository(term)
40 40
 	if err != nil {
41 41
 		return nil, err
... ...
@@ -18,20 +18,21 @@ import (
18 18
 	"time"
19 19
 
20 20
 	"github.com/Sirupsen/logrus"
21
+	"github.com/docker/docker/cliconfig"
21 22
 	"github.com/docker/docker/pkg/httputils"
22 23
 	"github.com/docker/docker/pkg/requestdecorator"
23 24
 	"github.com/docker/docker/pkg/tarsum"
24 25
 )
25 26
 
26 27
 type Session struct {
27
-	authConfig    *AuthConfig
28
+	authConfig    *cliconfig.AuthConfig
28 29
 	reqFactory    *requestdecorator.RequestFactory
29 30
 	indexEndpoint *Endpoint
30 31
 	jar           *cookiejar.Jar
31 32
 	timeout       TimeoutType
32 33
 }
33 34
 
34
-func NewSession(authConfig *AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
35
+func NewSession(authConfig *cliconfig.AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
35 36
 	r = &Session{
36 37
 		authConfig:    authConfig,
37 38
 		indexEndpoint: endpoint,
... ...
@@ -600,12 +601,12 @@ func (r *Session) SearchRepositories(term string) (*SearchResults, error) {
600 600
 	return result, err
601 601
 }
602 602
 
603
-func (r *Session) GetAuthConfig(withPasswd bool) *AuthConfig {
603
+func (r *Session) GetAuthConfig(withPasswd bool) *cliconfig.AuthConfig {
604 604
 	password := ""
605 605
 	if withPasswd {
606 606
 		password = r.authConfig.Password
607 607
 	}
608
-	return &AuthConfig{
608
+	return &cliconfig.AuthConfig{
609 609
 		Username: r.authConfig.Username,
610 610
 		Password: password,
611 611
 		Email:    r.authConfig.Email,