Browse code

Move package cliconfig to cli/config

I felt it made more sence 👼

Signed-off-by: Vincent Demeester <vincent@sbr.pm>

Vincent Demeester authored on 2016/12/26 04:31:52
Showing 36 changed files
... ...
@@ -12,10 +12,10 @@ import (
12 12
 	"github.com/docker/docker/api"
13 13
 	"github.com/docker/docker/api/types"
14 14
 	"github.com/docker/docker/api/types/versions"
15
+	cliconfig "github.com/docker/docker/cli/config"
16
+	"github.com/docker/docker/cli/config/configfile"
17
+	"github.com/docker/docker/cli/config/credentials"
15 18
 	cliflags "github.com/docker/docker/cli/flags"
16
-	"github.com/docker/docker/cliconfig"
17
-	"github.com/docker/docker/cliconfig/configfile"
18
-	"github.com/docker/docker/cliconfig/credentials"
19 19
 	"github.com/docker/docker/client"
20 20
 	"github.com/docker/docker/dockerversion"
21 21
 	dopts "github.com/docker/docker/opts"
... ...
@@ -150,7 +150,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
150 150
 	cli.defaultVersion = cli.client.ClientVersion()
151 151
 
152 152
 	if opts.Common.TrustKey == "" {
153
-		cli.keyFile = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
153
+		cli.keyFile = filepath.Join(cliconfig.Dir(), cliflags.DefaultTrustKeyFile)
154 154
 	} else {
155 155
 		cli.keyFile = opts.Common.TrustKey
156 156
 	}
... ...
@@ -179,7 +179,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli {
179 179
 // LoadDefaultConfigFile attempts to load the default config file and returns
180 180
 // an initialized ConfigFile struct if none is found.
181 181
 func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile {
182
-	configFile, e := cliconfig.Load(cliconfig.ConfigDir())
182
+	configFile, e := cliconfig.Load(cliconfig.Dir())
183 183
 	if e != nil {
184 184
 		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
185 185
 	}
186 186
new file mode 100644
... ...
@@ -0,0 +1,120 @@
0
+package config
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"os"
6
+	"path/filepath"
7
+
8
+	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/cli/config/configfile"
10
+	"github.com/docker/docker/pkg/homedir"
11
+)
12
+
13
+const (
14
+	// ConfigFileName is the name of config file
15
+	ConfigFileName = "config.json"
16
+	configFileDir  = ".docker"
17
+	oldConfigfile  = ".dockercfg"
18
+)
19
+
20
+var (
21
+	configDir = os.Getenv("DOCKER_CONFIG")
22
+)
23
+
24
+func init() {
25
+	if configDir == "" {
26
+		configDir = filepath.Join(homedir.Get(), configFileDir)
27
+	}
28
+}
29
+
30
+// Dir returns the directory the configuration file is stored in
31
+func Dir() string {
32
+	return configDir
33
+}
34
+
35
+// SetDir sets the directory the configuration file is stored in
36
+func SetDir(dir string) {
37
+	configDir = dir
38
+}
39
+
40
+// NewConfigFile initializes an empty configuration file for the given filename 'fn'
41
+func NewConfigFile(fn string) *configfile.ConfigFile {
42
+	return &configfile.ConfigFile{
43
+		AuthConfigs: make(map[string]types.AuthConfig),
44
+		HTTPHeaders: make(map[string]string),
45
+		Filename:    fn,
46
+	}
47
+}
48
+
49
+// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
50
+// a non-nested reader
51
+func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
52
+	configFile := configfile.ConfigFile{
53
+		AuthConfigs: make(map[string]types.AuthConfig),
54
+	}
55
+	err := configFile.LegacyLoadFromReader(configData)
56
+	return &configFile, err
57
+}
58
+
59
+// LoadFromReader is a convenience function that creates a ConfigFile object from
60
+// a reader
61
+func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
62
+	configFile := configfile.ConfigFile{
63
+		AuthConfigs: make(map[string]types.AuthConfig),
64
+	}
65
+	err := configFile.LoadFromReader(configData)
66
+	return &configFile, err
67
+}
68
+
69
+// Load reads the configuration files in the given directory, and sets up
70
+// the auth config information and returns values.
71
+// FIXME: use the internal golang config parser
72
+func Load(configDir string) (*configfile.ConfigFile, error) {
73
+	if configDir == "" {
74
+		configDir = Dir()
75
+	}
76
+
77
+	configFile := configfile.ConfigFile{
78
+		AuthConfigs: make(map[string]types.AuthConfig),
79
+		Filename:    filepath.Join(configDir, ConfigFileName),
80
+	}
81
+
82
+	// Try happy path first - latest config file
83
+	if _, err := os.Stat(configFile.Filename); err == nil {
84
+		file, err := os.Open(configFile.Filename)
85
+		if err != nil {
86
+			return &configFile, fmt.Errorf("%s - %v", configFile.Filename, err)
87
+		}
88
+		defer file.Close()
89
+		err = configFile.LoadFromReader(file)
90
+		if err != nil {
91
+			err = fmt.Errorf("%s - %v", configFile.Filename, err)
92
+		}
93
+		return &configFile, err
94
+	} else if !os.IsNotExist(err) {
95
+		// if file is there but we can't stat it for any reason other
96
+		// than it doesn't exist then stop
97
+		return &configFile, fmt.Errorf("%s - %v", configFile.Filename, err)
98
+	}
99
+
100
+	// Can't find latest config file so check for the old one
101
+	confFile := filepath.Join(homedir.Get(), oldConfigfile)
102
+	if _, err := os.Stat(confFile); err != nil {
103
+		return &configFile, nil //missing file is not an error
104
+	}
105
+	file, err := os.Open(confFile)
106
+	if err != nil {
107
+		return &configFile, fmt.Errorf("%s - %v", confFile, err)
108
+	}
109
+	defer file.Close()
110
+	err = configFile.LegacyLoadFromReader(file)
111
+	if err != nil {
112
+		return &configFile, fmt.Errorf("%s - %v", confFile, err)
113
+	}
114
+
115
+	if configFile.HTTPHeaders == nil {
116
+		configFile.HTTPHeaders = map[string]string{}
117
+	}
118
+	return &configFile, nil
119
+}
0 120
new file mode 100644
... ...
@@ -0,0 +1,621 @@
0
+package config
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"path/filepath"
6
+	"strings"
7
+	"testing"
8
+
9
+	"github.com/docker/docker/cli/config/configfile"
10
+	"github.com/docker/docker/pkg/homedir"
11
+)
12
+
13
+func TestEmptyConfigDir(t *testing.T) {
14
+	tmpHome, err := ioutil.TempDir("", "config-test")
15
+	if err != nil {
16
+		t.Fatal(err)
17
+	}
18
+	defer os.RemoveAll(tmpHome)
19
+
20
+	SetDir(tmpHome)
21
+
22
+	config, err := Load("")
23
+	if err != nil {
24
+		t.Fatalf("Failed loading on empty config dir: %q", err)
25
+	}
26
+
27
+	expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName)
28
+	if config.Filename != expectedConfigFilename {
29
+		t.Fatalf("Expected config filename %s, got %s", expectedConfigFilename, config.Filename)
30
+	}
31
+
32
+	// Now save it and make sure it shows up in new form
33
+	saveConfigAndValidateNewFormat(t, config, tmpHome)
34
+}
35
+
36
+func TestMissingFile(t *testing.T) {
37
+	tmpHome, err := ioutil.TempDir("", "config-test")
38
+	if err != nil {
39
+		t.Fatal(err)
40
+	}
41
+	defer os.RemoveAll(tmpHome)
42
+
43
+	config, err := Load(tmpHome)
44
+	if err != nil {
45
+		t.Fatalf("Failed loading on missing file: %q", err)
46
+	}
47
+
48
+	// Now save it and make sure it shows up in new form
49
+	saveConfigAndValidateNewFormat(t, config, tmpHome)
50
+}
51
+
52
+func TestSaveFileToDirs(t *testing.T) {
53
+	tmpHome, err := ioutil.TempDir("", "config-test")
54
+	if err != nil {
55
+		t.Fatal(err)
56
+	}
57
+	defer os.RemoveAll(tmpHome)
58
+
59
+	tmpHome += "/.docker"
60
+
61
+	config, err := Load(tmpHome)
62
+	if err != nil {
63
+		t.Fatalf("Failed loading on missing file: %q", err)
64
+	}
65
+
66
+	// Now save it and make sure it shows up in new form
67
+	saveConfigAndValidateNewFormat(t, config, tmpHome)
68
+}
69
+
70
+func TestEmptyFile(t *testing.T) {
71
+	tmpHome, err := ioutil.TempDir("", "config-test")
72
+	if err != nil {
73
+		t.Fatal(err)
74
+	}
75
+	defer os.RemoveAll(tmpHome)
76
+
77
+	fn := filepath.Join(tmpHome, ConfigFileName)
78
+	if err := ioutil.WriteFile(fn, []byte(""), 0600); err != nil {
79
+		t.Fatal(err)
80
+	}
81
+
82
+	_, err = Load(tmpHome)
83
+	if err == nil {
84
+		t.Fatalf("Was supposed to fail")
85
+	}
86
+}
87
+
88
+func TestEmptyJSON(t *testing.T) {
89
+	tmpHome, err := ioutil.TempDir("", "config-test")
90
+	if err != nil {
91
+		t.Fatal(err)
92
+	}
93
+	defer os.RemoveAll(tmpHome)
94
+
95
+	fn := filepath.Join(tmpHome, ConfigFileName)
96
+	if err := ioutil.WriteFile(fn, []byte("{}"), 0600); err != nil {
97
+		t.Fatal(err)
98
+	}
99
+
100
+	config, err := Load(tmpHome)
101
+	if err != nil {
102
+		t.Fatalf("Failed loading on empty json file: %q", err)
103
+	}
104
+
105
+	// Now save it and make sure it shows up in new form
106
+	saveConfigAndValidateNewFormat(t, config, tmpHome)
107
+}
108
+
109
+func TestOldInvalidsAuth(t *testing.T) {
110
+	invalids := map[string]string{
111
+		`username = test`: "The Auth config file is empty",
112
+		`username
113
+password`: "Invalid Auth config file",
114
+		`username = test
115
+email`: "Invalid auth configuration file",
116
+	}
117
+
118
+	tmpHome, err := ioutil.TempDir("", "config-test")
119
+	if err != nil {
120
+		t.Fatal(err)
121
+	}
122
+	defer os.RemoveAll(tmpHome)
123
+
124
+	homeKey := homedir.Key()
125
+	homeVal := homedir.Get()
126
+
127
+	defer func() { os.Setenv(homeKey, homeVal) }()
128
+	os.Setenv(homeKey, tmpHome)
129
+
130
+	for content, expectedError := range invalids {
131
+		fn := filepath.Join(tmpHome, oldConfigfile)
132
+		if err := ioutil.WriteFile(fn, []byte(content), 0600); err != nil {
133
+			t.Fatal(err)
134
+		}
135
+
136
+		config, err := Load(tmpHome)
137
+		// Use Contains instead of == since the file name will change each time
138
+		if err == nil || !strings.Contains(err.Error(), expectedError) {
139
+			t.Fatalf("Should have failed\nConfig: %v\nGot: %v\nExpected: %v", config, err, expectedError)
140
+		}
141
+
142
+	}
143
+}
144
+
145
+func TestOldValidAuth(t *testing.T) {
146
+	tmpHome, err := ioutil.TempDir("", "config-test")
147
+	if err != nil {
148
+		t.Fatal(err)
149
+	}
150
+	if err != nil {
151
+		t.Fatal(err)
152
+	}
153
+	defer os.RemoveAll(tmpHome)
154
+
155
+	homeKey := homedir.Key()
156
+	homeVal := homedir.Get()
157
+
158
+	defer func() { os.Setenv(homeKey, homeVal) }()
159
+	os.Setenv(homeKey, tmpHome)
160
+
161
+	fn := filepath.Join(tmpHome, oldConfigfile)
162
+	js := `username = am9lam9lOmhlbGxv
163
+	email = user@example.com`
164
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
165
+		t.Fatal(err)
166
+	}
167
+
168
+	config, err := Load(tmpHome)
169
+	if err != nil {
170
+		t.Fatal(err)
171
+	}
172
+
173
+	// defaultIndexserver is https://index.docker.io/v1/
174
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
175
+	if ac.Username != "joejoe" || ac.Password != "hello" {
176
+		t.Fatalf("Missing data from parsing:\n%q", config)
177
+	}
178
+
179
+	// Now save it and make sure it shows up in new form
180
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
181
+
182
+	expConfStr := `{
183
+	"auths": {
184
+		"https://index.docker.io/v1/": {
185
+			"auth": "am9lam9lOmhlbGxv"
186
+		}
187
+	}
188
+}`
189
+
190
+	if configStr != expConfStr {
191
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
192
+	}
193
+}
194
+
195
+func TestOldJSONInvalid(t *testing.T) {
196
+	tmpHome, err := ioutil.TempDir("", "config-test")
197
+	if err != nil {
198
+		t.Fatal(err)
199
+	}
200
+	defer os.RemoveAll(tmpHome)
201
+
202
+	homeKey := homedir.Key()
203
+	homeVal := homedir.Get()
204
+
205
+	defer func() { os.Setenv(homeKey, homeVal) }()
206
+	os.Setenv(homeKey, tmpHome)
207
+
208
+	fn := filepath.Join(tmpHome, oldConfigfile)
209
+	js := `{"https://index.docker.io/v1/":{"auth":"test","email":"user@example.com"}}`
210
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
211
+		t.Fatal(err)
212
+	}
213
+
214
+	config, err := Load(tmpHome)
215
+	// Use Contains instead of == since the file name will change each time
216
+	if err == nil || !strings.Contains(err.Error(), "Invalid auth configuration file") {
217
+		t.Fatalf("Expected an error got : %v, %v", config, err)
218
+	}
219
+}
220
+
221
+func TestOldJSON(t *testing.T) {
222
+	tmpHome, err := ioutil.TempDir("", "config-test")
223
+	if err != nil {
224
+		t.Fatal(err)
225
+	}
226
+	defer os.RemoveAll(tmpHome)
227
+
228
+	homeKey := homedir.Key()
229
+	homeVal := homedir.Get()
230
+
231
+	defer func() { os.Setenv(homeKey, homeVal) }()
232
+	os.Setenv(homeKey, tmpHome)
233
+
234
+	fn := filepath.Join(tmpHome, oldConfigfile)
235
+	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
236
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
237
+		t.Fatal(err)
238
+	}
239
+
240
+	config, err := Load(tmpHome)
241
+	if err != nil {
242
+		t.Fatalf("Failed loading on empty json file: %q", err)
243
+	}
244
+
245
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
246
+	if ac.Username != "joejoe" || ac.Password != "hello" {
247
+		t.Fatalf("Missing data from parsing:\n%q", config)
248
+	}
249
+
250
+	// Now save it and make sure it shows up in new form
251
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
252
+
253
+	expConfStr := `{
254
+	"auths": {
255
+		"https://index.docker.io/v1/": {
256
+			"auth": "am9lam9lOmhlbGxv",
257
+			"email": "user@example.com"
258
+		}
259
+	}
260
+}`
261
+
262
+	if configStr != expConfStr {
263
+		t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr)
264
+	}
265
+}
266
+
267
+func TestNewJSON(t *testing.T) {
268
+	tmpHome, err := ioutil.TempDir("", "config-test")
269
+	if err != nil {
270
+		t.Fatal(err)
271
+	}
272
+	defer os.RemoveAll(tmpHome)
273
+
274
+	fn := filepath.Join(tmpHome, ConfigFileName)
275
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
276
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
277
+		t.Fatal(err)
278
+	}
279
+
280
+	config, err := Load(tmpHome)
281
+	if err != nil {
282
+		t.Fatalf("Failed loading on empty json file: %q", err)
283
+	}
284
+
285
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
286
+	if ac.Username != "joejoe" || ac.Password != "hello" {
287
+		t.Fatalf("Missing data from parsing:\n%q", config)
288
+	}
289
+
290
+	// Now save it and make sure it shows up in new form
291
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
292
+
293
+	expConfStr := `{
294
+	"auths": {
295
+		"https://index.docker.io/v1/": {
296
+			"auth": "am9lam9lOmhlbGxv"
297
+		}
298
+	}
299
+}`
300
+
301
+	if configStr != expConfStr {
302
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
303
+	}
304
+}
305
+
306
+func TestNewJSONNoEmail(t *testing.T) {
307
+	tmpHome, err := ioutil.TempDir("", "config-test")
308
+	if err != nil {
309
+		t.Fatal(err)
310
+	}
311
+	defer os.RemoveAll(tmpHome)
312
+
313
+	fn := filepath.Join(tmpHome, ConfigFileName)
314
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
315
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
316
+		t.Fatal(err)
317
+	}
318
+
319
+	config, err := Load(tmpHome)
320
+	if err != nil {
321
+		t.Fatalf("Failed loading on empty json file: %q", err)
322
+	}
323
+
324
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
325
+	if ac.Username != "joejoe" || ac.Password != "hello" {
326
+		t.Fatalf("Missing data from parsing:\n%q", config)
327
+	}
328
+
329
+	// Now save it and make sure it shows up in new form
330
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
331
+
332
+	expConfStr := `{
333
+	"auths": {
334
+		"https://index.docker.io/v1/": {
335
+			"auth": "am9lam9lOmhlbGxv"
336
+		}
337
+	}
338
+}`
339
+
340
+	if configStr != expConfStr {
341
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
342
+	}
343
+}
344
+
345
+func TestJSONWithPsFormat(t *testing.T) {
346
+	tmpHome, err := ioutil.TempDir("", "config-test")
347
+	if err != nil {
348
+		t.Fatal(err)
349
+	}
350
+	defer os.RemoveAll(tmpHome)
351
+
352
+	fn := filepath.Join(tmpHome, ConfigFileName)
353
+	js := `{
354
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
355
+		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
356
+}`
357
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
358
+		t.Fatal(err)
359
+	}
360
+
361
+	config, err := Load(tmpHome)
362
+	if err != nil {
363
+		t.Fatalf("Failed loading on empty json file: %q", err)
364
+	}
365
+
366
+	if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
367
+		t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
368
+	}
369
+
370
+	// Now save it and make sure it shows up in new form
371
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
372
+	if !strings.Contains(configStr, `"psFormat":`) ||
373
+		!strings.Contains(configStr, "{{.ID}}") {
374
+		t.Fatalf("Should have save in new form: %s", configStr)
375
+	}
376
+}
377
+
378
+func TestJSONWithCredentialStore(t *testing.T) {
379
+	tmpHome, err := ioutil.TempDir("", "config-test")
380
+	if err != nil {
381
+		t.Fatal(err)
382
+	}
383
+	defer os.RemoveAll(tmpHome)
384
+
385
+	fn := filepath.Join(tmpHome, ConfigFileName)
386
+	js := `{
387
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
388
+		"credsStore": "crazy-secure-storage"
389
+}`
390
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
391
+		t.Fatal(err)
392
+	}
393
+
394
+	config, err := Load(tmpHome)
395
+	if err != nil {
396
+		t.Fatalf("Failed loading on empty json file: %q", err)
397
+	}
398
+
399
+	if config.CredentialsStore != "crazy-secure-storage" {
400
+		t.Fatalf("Unknown credential store: %s\n", config.CredentialsStore)
401
+	}
402
+
403
+	// Now save it and make sure it shows up in new form
404
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
405
+	if !strings.Contains(configStr, `"credsStore":`) ||
406
+		!strings.Contains(configStr, "crazy-secure-storage") {
407
+		t.Fatalf("Should have save in new form: %s", configStr)
408
+	}
409
+}
410
+
411
+func TestJSONWithCredentialHelpers(t *testing.T) {
412
+	tmpHome, err := ioutil.TempDir("", "config-test")
413
+	if err != nil {
414
+		t.Fatal(err)
415
+	}
416
+	defer os.RemoveAll(tmpHome)
417
+
418
+	fn := filepath.Join(tmpHome, ConfigFileName)
419
+	js := `{
420
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
421
+		"credHelpers": { "images.io": "images-io", "containers.com": "crazy-secure-storage" }
422
+}`
423
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
424
+		t.Fatal(err)
425
+	}
426
+
427
+	config, err := Load(tmpHome)
428
+	if err != nil {
429
+		t.Fatalf("Failed loading on empty json file: %q", err)
430
+	}
431
+
432
+	if config.CredentialHelpers == nil {
433
+		t.Fatal("config.CredentialHelpers was nil")
434
+	} else if config.CredentialHelpers["images.io"] != "images-io" ||
435
+		config.CredentialHelpers["containers.com"] != "crazy-secure-storage" {
436
+		t.Fatalf("Credential helpers not deserialized properly: %v\n", config.CredentialHelpers)
437
+	}
438
+
439
+	// Now save it and make sure it shows up in new form
440
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
441
+	if !strings.Contains(configStr, `"credHelpers":`) ||
442
+		!strings.Contains(configStr, "images.io") ||
443
+		!strings.Contains(configStr, "images-io") ||
444
+		!strings.Contains(configStr, "containers.com") ||
445
+		!strings.Contains(configStr, "crazy-secure-storage") {
446
+		t.Fatalf("Should have save in new form: %s", configStr)
447
+	}
448
+}
449
+
450
+// Save it and make sure it shows up in new form
451
+func saveConfigAndValidateNewFormat(t *testing.T, config *configfile.ConfigFile, homeFolder string) string {
452
+	if err := config.Save(); err != nil {
453
+		t.Fatalf("Failed to save: %q", err)
454
+	}
455
+
456
+	buf, err := ioutil.ReadFile(filepath.Join(homeFolder, ConfigFileName))
457
+	if err != nil {
458
+		t.Fatal(err)
459
+	}
460
+	if !strings.Contains(string(buf), `"auths":`) {
461
+		t.Fatalf("Should have save in new form: %s", string(buf))
462
+	}
463
+	return string(buf)
464
+}
465
+
466
+func TestConfigDir(t *testing.T) {
467
+	tmpHome, err := ioutil.TempDir("", "config-test")
468
+	if err != nil {
469
+		t.Fatal(err)
470
+	}
471
+	defer os.RemoveAll(tmpHome)
472
+
473
+	if Dir() == tmpHome {
474
+		t.Fatalf("Expected ConfigDir to be different than %s by default, but was the same", tmpHome)
475
+	}
476
+
477
+	// Update configDir
478
+	SetDir(tmpHome)
479
+
480
+	if Dir() != tmpHome {
481
+		t.Fatalf("Expected ConfigDir to %s, but was %s", tmpHome, Dir())
482
+	}
483
+}
484
+
485
+func TestConfigFile(t *testing.T) {
486
+	configFilename := "configFilename"
487
+	configFile := NewConfigFile(configFilename)
488
+
489
+	if configFile.Filename != configFilename {
490
+		t.Fatalf("Expected %s, got %s", configFilename, configFile.Filename)
491
+	}
492
+}
493
+
494
+func TestJSONReaderNoFile(t *testing.T) {
495
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
496
+
497
+	config, err := LoadFromReader(strings.NewReader(js))
498
+	if err != nil {
499
+		t.Fatalf("Failed loading on empty json file: %q", err)
500
+	}
501
+
502
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
503
+	if ac.Username != "joejoe" || ac.Password != "hello" {
504
+		t.Fatalf("Missing data from parsing:\n%q", config)
505
+	}
506
+
507
+}
508
+
509
+func TestOldJSONReaderNoFile(t *testing.T) {
510
+	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
511
+
512
+	config, err := LegacyLoadFromReader(strings.NewReader(js))
513
+	if err != nil {
514
+		t.Fatalf("Failed loading on empty json file: %q", err)
515
+	}
516
+
517
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
518
+	if ac.Username != "joejoe" || ac.Password != "hello" {
519
+		t.Fatalf("Missing data from parsing:\n%q", config)
520
+	}
521
+}
522
+
523
+func TestJSONWithPsFormatNoFile(t *testing.T) {
524
+	js := `{
525
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
526
+		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
527
+}`
528
+	config, err := LoadFromReader(strings.NewReader(js))
529
+	if err != nil {
530
+		t.Fatalf("Failed loading on empty json file: %q", err)
531
+	}
532
+
533
+	if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
534
+		t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
535
+	}
536
+
537
+}
538
+
539
+func TestJSONSaveWithNoFile(t *testing.T) {
540
+	js := `{
541
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } },
542
+		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
543
+}`
544
+	config, err := LoadFromReader(strings.NewReader(js))
545
+	err = config.Save()
546
+	if err == nil {
547
+		t.Fatalf("Expected error. File should not have been able to save with no file name.")
548
+	}
549
+
550
+	tmpHome, err := ioutil.TempDir("", "config-test")
551
+	if err != nil {
552
+		t.Fatalf("Failed to create a temp dir: %q", err)
553
+	}
554
+	defer os.RemoveAll(tmpHome)
555
+
556
+	fn := filepath.Join(tmpHome, ConfigFileName)
557
+	f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
558
+	defer f.Close()
559
+
560
+	err = config.SaveToWriter(f)
561
+	if err != nil {
562
+		t.Fatalf("Failed saving to file: %q", err)
563
+	}
564
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
565
+	if err != nil {
566
+		t.Fatal(err)
567
+	}
568
+	expConfStr := `{
569
+	"auths": {
570
+		"https://index.docker.io/v1/": {
571
+			"auth": "am9lam9lOmhlbGxv"
572
+		}
573
+	},
574
+	"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
575
+}`
576
+	if string(buf) != expConfStr {
577
+		t.Fatalf("Should have save in new form: \n%s\nnot \n%s", string(buf), expConfStr)
578
+	}
579
+}
580
+
581
+func TestLegacyJSONSaveWithNoFile(t *testing.T) {
582
+
583
+	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
584
+	config, err := LegacyLoadFromReader(strings.NewReader(js))
585
+	err = config.Save()
586
+	if err == nil {
587
+		t.Fatalf("Expected error. File should not have been able to save with no file name.")
588
+	}
589
+
590
+	tmpHome, err := ioutil.TempDir("", "config-test")
591
+	if err != nil {
592
+		t.Fatalf("Failed to create a temp dir: %q", err)
593
+	}
594
+	defer os.RemoveAll(tmpHome)
595
+
596
+	fn := filepath.Join(tmpHome, ConfigFileName)
597
+	f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
598
+	defer f.Close()
599
+
600
+	if err = config.SaveToWriter(f); err != nil {
601
+		t.Fatalf("Failed saving to file: %q", err)
602
+	}
603
+	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
604
+	if err != nil {
605
+		t.Fatal(err)
606
+	}
607
+
608
+	expConfStr := `{
609
+	"auths": {
610
+		"https://index.docker.io/v1/": {
611
+			"auth": "am9lam9lOmhlbGxv",
612
+			"email": "user@example.com"
613
+		}
614
+	}
615
+}`
616
+
617
+	if string(buf) != expConfStr {
618
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr)
619
+	}
620
+}
0 621
new file mode 100644
... ...
@@ -0,0 +1,183 @@
0
+package configfile
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"fmt"
6
+	"io"
7
+	"io/ioutil"
8
+	"os"
9
+	"path/filepath"
10
+	"strings"
11
+
12
+	"github.com/docker/docker/api/types"
13
+)
14
+
15
+const (
16
+	// This constant is only used for really old config files when the
17
+	// URL wasn't saved as part of the config file and it was just
18
+	// assumed to be this value.
19
+	defaultIndexserver = "https://index.docker.io/v1/"
20
+)
21
+
22
+// ConfigFile ~/.docker/config.json file info
23
+type ConfigFile struct {
24
+	AuthConfigs          map[string]types.AuthConfig `json:"auths"`
25
+	HTTPHeaders          map[string]string           `json:"HttpHeaders,omitempty"`
26
+	PsFormat             string                      `json:"psFormat,omitempty"`
27
+	ImagesFormat         string                      `json:"imagesFormat,omitempty"`
28
+	NetworksFormat       string                      `json:"networksFormat,omitempty"`
29
+	VolumesFormat        string                      `json:"volumesFormat,omitempty"`
30
+	StatsFormat          string                      `json:"statsFormat,omitempty"`
31
+	DetachKeys           string                      `json:"detachKeys,omitempty"`
32
+	CredentialsStore     string                      `json:"credsStore,omitempty"`
33
+	CredentialHelpers    map[string]string           `json:"credHelpers,omitempty"`
34
+	Filename             string                      `json:"-"` // Note: for internal use only
35
+	ServiceInspectFormat string                      `json:"serviceInspectFormat,omitempty"`
36
+}
37
+
38
+// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
39
+// auth config information with given directory and populates the receiver object
40
+func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
41
+	b, err := ioutil.ReadAll(configData)
42
+	if err != nil {
43
+		return err
44
+	}
45
+
46
+	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
47
+		arr := strings.Split(string(b), "\n")
48
+		if len(arr) < 2 {
49
+			return fmt.Errorf("The Auth config file is empty")
50
+		}
51
+		authConfig := types.AuthConfig{}
52
+		origAuth := strings.Split(arr[0], " = ")
53
+		if len(origAuth) != 2 {
54
+			return fmt.Errorf("Invalid Auth config file")
55
+		}
56
+		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
57
+		if err != nil {
58
+			return err
59
+		}
60
+		authConfig.ServerAddress = defaultIndexserver
61
+		configFile.AuthConfigs[defaultIndexserver] = authConfig
62
+	} else {
63
+		for k, authConfig := range configFile.AuthConfigs {
64
+			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
65
+			if err != nil {
66
+				return err
67
+			}
68
+			authConfig.Auth = ""
69
+			authConfig.ServerAddress = k
70
+			configFile.AuthConfigs[k] = authConfig
71
+		}
72
+	}
73
+	return nil
74
+}
75
+
76
+// LoadFromReader reads the configuration data given and sets up the auth config
77
+// information with given directory and populates the receiver object
78
+func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
79
+	if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
80
+		return err
81
+	}
82
+	var err error
83
+	for addr, ac := range configFile.AuthConfigs {
84
+		ac.Username, ac.Password, err = decodeAuth(ac.Auth)
85
+		if err != nil {
86
+			return err
87
+		}
88
+		ac.Auth = ""
89
+		ac.ServerAddress = addr
90
+		configFile.AuthConfigs[addr] = ac
91
+	}
92
+	return nil
93
+}
94
+
95
+// ContainsAuth returns whether there is authentication configured
96
+// in this file or not.
97
+func (configFile *ConfigFile) ContainsAuth() bool {
98
+	return configFile.CredentialsStore != "" ||
99
+		len(configFile.CredentialHelpers) > 0 ||
100
+		len(configFile.AuthConfigs) > 0
101
+}
102
+
103
+// SaveToWriter encodes and writes out all the authorization information to
104
+// the given writer
105
+func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
106
+	// Encode sensitive data into a new/temp struct
107
+	tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
108
+	for k, authConfig := range configFile.AuthConfigs {
109
+		authCopy := authConfig
110
+		// encode and save the authstring, while blanking out the original fields
111
+		authCopy.Auth = encodeAuth(&authCopy)
112
+		authCopy.Username = ""
113
+		authCopy.Password = ""
114
+		authCopy.ServerAddress = ""
115
+		tmpAuthConfigs[k] = authCopy
116
+	}
117
+
118
+	saveAuthConfigs := configFile.AuthConfigs
119
+	configFile.AuthConfigs = tmpAuthConfigs
120
+	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
121
+
122
+	data, err := json.MarshalIndent(configFile, "", "\t")
123
+	if err != nil {
124
+		return err
125
+	}
126
+	_, err = writer.Write(data)
127
+	return err
128
+}
129
+
130
+// Save encodes and writes out all the authorization information
131
+func (configFile *ConfigFile) Save() error {
132
+	if configFile.Filename == "" {
133
+		return fmt.Errorf("Can't save config with empty filename")
134
+	}
135
+
136
+	if err := os.MkdirAll(filepath.Dir(configFile.Filename), 0700); err != nil {
137
+		return err
138
+	}
139
+	f, err := os.OpenFile(configFile.Filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
140
+	if err != nil {
141
+		return err
142
+	}
143
+	defer f.Close()
144
+	return configFile.SaveToWriter(f)
145
+}
146
+
147
+// encodeAuth creates a base64 encoded string to containing authorization information
148
+func encodeAuth(authConfig *types.AuthConfig) string {
149
+	if authConfig.Username == "" && authConfig.Password == "" {
150
+		return ""
151
+	}
152
+
153
+	authStr := authConfig.Username + ":" + authConfig.Password
154
+	msg := []byte(authStr)
155
+	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
156
+	base64.StdEncoding.Encode(encoded, msg)
157
+	return string(encoded)
158
+}
159
+
160
+// decodeAuth decodes a base64 encoded string and returns username and password
161
+func decodeAuth(authStr string) (string, string, error) {
162
+	if authStr == "" {
163
+		return "", "", nil
164
+	}
165
+
166
+	decLen := base64.StdEncoding.DecodedLen(len(authStr))
167
+	decoded := make([]byte, decLen)
168
+	authByte := []byte(authStr)
169
+	n, err := base64.StdEncoding.Decode(decoded, authByte)
170
+	if err != nil {
171
+		return "", "", err
172
+	}
173
+	if n > decLen {
174
+		return "", "", fmt.Errorf("Something went wrong decoding auth config")
175
+	}
176
+	arr := strings.SplitN(string(decoded), ":", 2)
177
+	if len(arr) != 2 {
178
+		return "", "", fmt.Errorf("Invalid auth configuration file")
179
+	}
180
+	password := strings.Trim(arr[1], "\x00")
181
+	return arr[0], password, nil
182
+}
0 183
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package configfile
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/docker/docker/api/types"
6
+)
7
+
8
+func TestEncodeAuth(t *testing.T) {
9
+	newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"}
10
+	authStr := encodeAuth(newAuthConfig)
11
+	decAuthConfig := &types.AuthConfig{}
12
+	var err error
13
+	decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
14
+	if err != nil {
15
+		t.Fatal(err)
16
+	}
17
+	if newAuthConfig.Username != decAuthConfig.Username {
18
+		t.Fatal("Encode Username doesn't match decoded Username")
19
+	}
20
+	if newAuthConfig.Password != decAuthConfig.Password {
21
+		t.Fatal("Encode Password doesn't match decoded Password")
22
+	}
23
+	if authStr != "a2VuOnRlc3Q=" {
24
+		t.Fatal("AuthString encoding isn't correct.")
25
+	}
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+package credentials
1
+
2
+import (
3
+	"github.com/docker/docker/api/types"
4
+)
5
+
6
+// Store is the interface that any credentials store must implement.
7
+type Store interface {
8
+	// Erase removes credentials from the store for a given server.
9
+	Erase(serverAddress string) error
10
+	// Get retrieves credentials from the store for a given server.
11
+	Get(serverAddress string) (types.AuthConfig, error)
12
+	// GetAll retrieves all the credentials from the store.
13
+	GetAll() (map[string]types.AuthConfig, error)
14
+	// Store saves credentials in the store.
15
+	Store(authConfig types.AuthConfig) error
16
+}
0 17
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+package credentials
1
+
2
+import (
3
+	"os/exec"
4
+
5
+	"github.com/docker/docker/cli/config/configfile"
6
+)
7
+
8
+// DetectDefaultStore sets the default credentials store
9
+// if the host includes the default store helper program.
10
+func DetectDefaultStore(c *configfile.ConfigFile) {
11
+	if c.CredentialsStore != "" {
12
+		// user defined
13
+		return
14
+	}
15
+
16
+	if defaultCredentialsStore != "" {
17
+		if _, err := exec.LookPath(remoteCredentialsPrefix + defaultCredentialsStore); err == nil {
18
+			c.CredentialsStore = defaultCredentialsStore
19
+		}
20
+	}
21
+}
0 22
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+package credentials
1
+
2
+const defaultCredentialsStore = "osxkeychain"
0 3
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+package credentials
1
+
2
+const defaultCredentialsStore = "secretservice"
0 3
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+// +build !windows,!darwin,!linux
1
+
2
+package credentials
3
+
4
+const defaultCredentialsStore = ""
0 5
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+package credentials
1
+
2
+const defaultCredentialsStore = "wincred"
0 3
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package credentials
1
+
2
+import (
3
+	"github.com/docker/docker/api/types"
4
+	"github.com/docker/docker/cli/config/configfile"
5
+	"github.com/docker/docker/registry"
6
+)
7
+
8
+// fileStore implements a credentials store using
9
+// the docker configuration file to keep the credentials in plain text.
10
+type fileStore struct {
11
+	file *configfile.ConfigFile
12
+}
13
+
14
+// NewFileStore creates a new file credentials store.
15
+func NewFileStore(file *configfile.ConfigFile) Store {
16
+	return &fileStore{
17
+		file: file,
18
+	}
19
+}
20
+
21
+// Erase removes the given credentials from the file store.
22
+func (c *fileStore) Erase(serverAddress string) error {
23
+	delete(c.file.AuthConfigs, serverAddress)
24
+	return c.file.Save()
25
+}
26
+
27
+// Get retrieves credentials for a specific server from the file store.
28
+func (c *fileStore) Get(serverAddress string) (types.AuthConfig, error) {
29
+	authConfig, ok := c.file.AuthConfigs[serverAddress]
30
+	if !ok {
31
+		// Maybe they have a legacy config file, we will iterate the keys converting
32
+		// them to the new format and testing
33
+		for r, ac := range c.file.AuthConfigs {
34
+			if serverAddress == registry.ConvertToHostname(r) {
35
+				return ac, nil
36
+			}
37
+		}
38
+
39
+		authConfig = types.AuthConfig{}
40
+	}
41
+	return authConfig, nil
42
+}
43
+
44
+func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) {
45
+	return c.file.AuthConfigs, nil
46
+}
47
+
48
+// Store saves the given credentials in the file store.
49
+func (c *fileStore) Store(authConfig types.AuthConfig) error {
50
+	c.file.AuthConfigs[authConfig.ServerAddress] = authConfig
51
+	return c.file.Save()
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,139 @@
0
+package credentials
1
+
2
+import (
3
+	"io/ioutil"
4
+	"testing"
5
+
6
+	"github.com/docker/docker/api/types"
7
+	cliconfig "github.com/docker/docker/cli/config"
8
+	"github.com/docker/docker/cli/config/configfile"
9
+)
10
+
11
+func newConfigFile(auths map[string]types.AuthConfig) *configfile.ConfigFile {
12
+	tmp, _ := ioutil.TempFile("", "docker-test")
13
+	name := tmp.Name()
14
+	tmp.Close()
15
+
16
+	c := cliconfig.NewConfigFile(name)
17
+	c.AuthConfigs = auths
18
+	return c
19
+}
20
+
21
+func TestFileStoreAddCredentials(t *testing.T) {
22
+	f := newConfigFile(make(map[string]types.AuthConfig))
23
+
24
+	s := NewFileStore(f)
25
+	err := s.Store(types.AuthConfig{
26
+		Auth:          "super_secret_token",
27
+		Email:         "foo@example.com",
28
+		ServerAddress: "https://example.com",
29
+	})
30
+
31
+	if err != nil {
32
+		t.Fatal(err)
33
+	}
34
+
35
+	if len(f.AuthConfigs) != 1 {
36
+		t.Fatalf("expected 1 auth config, got %d", len(f.AuthConfigs))
37
+	}
38
+
39
+	a, ok := f.AuthConfigs["https://example.com"]
40
+	if !ok {
41
+		t.Fatalf("expected auth for https://example.com, got %v", f.AuthConfigs)
42
+	}
43
+	if a.Auth != "super_secret_token" {
44
+		t.Fatalf("expected auth `super_secret_token`, got %s", a.Auth)
45
+	}
46
+	if a.Email != "foo@example.com" {
47
+		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
48
+	}
49
+}
50
+
51
+func TestFileStoreGet(t *testing.T) {
52
+	f := newConfigFile(map[string]types.AuthConfig{
53
+		"https://example.com": {
54
+			Auth:          "super_secret_token",
55
+			Email:         "foo@example.com",
56
+			ServerAddress: "https://example.com",
57
+		},
58
+	})
59
+
60
+	s := NewFileStore(f)
61
+	a, err := s.Get("https://example.com")
62
+	if err != nil {
63
+		t.Fatal(err)
64
+	}
65
+	if a.Auth != "super_secret_token" {
66
+		t.Fatalf("expected auth `super_secret_token`, got %s", a.Auth)
67
+	}
68
+	if a.Email != "foo@example.com" {
69
+		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
70
+	}
71
+}
72
+
73
+func TestFileStoreGetAll(t *testing.T) {
74
+	s1 := "https://example.com"
75
+	s2 := "https://example2.com"
76
+	f := newConfigFile(map[string]types.AuthConfig{
77
+		s1: {
78
+			Auth:          "super_secret_token",
79
+			Email:         "foo@example.com",
80
+			ServerAddress: "https://example.com",
81
+		},
82
+		s2: {
83
+			Auth:          "super_secret_token2",
84
+			Email:         "foo@example2.com",
85
+			ServerAddress: "https://example2.com",
86
+		},
87
+	})
88
+
89
+	s := NewFileStore(f)
90
+	as, err := s.GetAll()
91
+	if err != nil {
92
+		t.Fatal(err)
93
+	}
94
+	if len(as) != 2 {
95
+		t.Fatalf("wanted 2, got %d", len(as))
96
+	}
97
+	if as[s1].Auth != "super_secret_token" {
98
+		t.Fatalf("expected auth `super_secret_token`, got %s", as[s1].Auth)
99
+	}
100
+	if as[s1].Email != "foo@example.com" {
101
+		t.Fatalf("expected email `foo@example.com`, got %s", as[s1].Email)
102
+	}
103
+	if as[s2].Auth != "super_secret_token2" {
104
+		t.Fatalf("expected auth `super_secret_token2`, got %s", as[s2].Auth)
105
+	}
106
+	if as[s2].Email != "foo@example2.com" {
107
+		t.Fatalf("expected email `foo@example2.com`, got %s", as[s2].Email)
108
+	}
109
+}
110
+
111
+func TestFileStoreErase(t *testing.T) {
112
+	f := newConfigFile(map[string]types.AuthConfig{
113
+		"https://example.com": {
114
+			Auth:          "super_secret_token",
115
+			Email:         "foo@example.com",
116
+			ServerAddress: "https://example.com",
117
+		},
118
+	})
119
+
120
+	s := NewFileStore(f)
121
+	err := s.Erase("https://example.com")
122
+	if err != nil {
123
+		t.Fatal(err)
124
+	}
125
+
126
+	// file store never returns errors, check that the auth config is empty
127
+	a, err := s.Get("https://example.com")
128
+	if err != nil {
129
+		t.Fatal(err)
130
+	}
131
+
132
+	if a.Auth != "" {
133
+		t.Fatalf("expected empty auth token, got %s", a.Auth)
134
+	}
135
+	if a.Email != "" {
136
+		t.Fatalf("expected empty email, got %s", a.Email)
137
+	}
138
+}
0 139
new file mode 100644
... ...
@@ -0,0 +1,144 @@
0
+package credentials
1
+
2
+import (
3
+	"github.com/docker/docker-credential-helpers/client"
4
+	"github.com/docker/docker-credential-helpers/credentials"
5
+	"github.com/docker/docker/api/types"
6
+	"github.com/docker/docker/cli/config/configfile"
7
+)
8
+
9
+const (
10
+	remoteCredentialsPrefix = "docker-credential-"
11
+	tokenUsername           = "<token>"
12
+)
13
+
14
+// nativeStore implements a credentials store
15
+// using native keychain to keep credentials secure.
16
+// It piggybacks into a file store to keep users' emails.
17
+type nativeStore struct {
18
+	programFunc client.ProgramFunc
19
+	fileStore   Store
20
+}
21
+
22
+// NewNativeStore creates a new native store that
23
+// uses a remote helper program to manage credentials.
24
+func NewNativeStore(file *configfile.ConfigFile, helperSuffix string) Store {
25
+	name := remoteCredentialsPrefix + helperSuffix
26
+	return &nativeStore{
27
+		programFunc: client.NewShellProgramFunc(name),
28
+		fileStore:   NewFileStore(file),
29
+	}
30
+}
31
+
32
+// Erase removes the given credentials from the native store.
33
+func (c *nativeStore) Erase(serverAddress string) error {
34
+	if err := client.Erase(c.programFunc, serverAddress); err != nil {
35
+		return err
36
+	}
37
+
38
+	// Fallback to plain text store to remove email
39
+	return c.fileStore.Erase(serverAddress)
40
+}
41
+
42
+// Get retrieves credentials for a specific server from the native store.
43
+func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) {
44
+	// load user email if it exist or an empty auth config.
45
+	auth, _ := c.fileStore.Get(serverAddress)
46
+
47
+	creds, err := c.getCredentialsFromStore(serverAddress)
48
+	if err != nil {
49
+		return auth, err
50
+	}
51
+	auth.Username = creds.Username
52
+	auth.IdentityToken = creds.IdentityToken
53
+	auth.Password = creds.Password
54
+
55
+	return auth, nil
56
+}
57
+
58
+// GetAll retrieves all the credentials from the native store.
59
+func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) {
60
+	auths, err := c.listCredentialsInStore()
61
+	if err != nil {
62
+		return nil, err
63
+	}
64
+
65
+	// Emails are only stored in the file store.
66
+	// This call can be safely eliminated when emails are removed.
67
+	fileConfigs, _ := c.fileStore.GetAll()
68
+
69
+	authConfigs := make(map[string]types.AuthConfig)
70
+	for registry := range auths {
71
+		creds, err := c.getCredentialsFromStore(registry)
72
+		if err != nil {
73
+			return nil, err
74
+		}
75
+		ac, _ := fileConfigs[registry] // might contain Email
76
+		ac.Username = creds.Username
77
+		ac.Password = creds.Password
78
+		ac.IdentityToken = creds.IdentityToken
79
+		authConfigs[registry] = ac
80
+	}
81
+
82
+	return authConfigs, nil
83
+}
84
+
85
+// Store saves the given credentials in the file store.
86
+func (c *nativeStore) Store(authConfig types.AuthConfig) error {
87
+	if err := c.storeCredentialsInStore(authConfig); err != nil {
88
+		return err
89
+	}
90
+	authConfig.Username = ""
91
+	authConfig.Password = ""
92
+	authConfig.IdentityToken = ""
93
+
94
+	// Fallback to old credential in plain text to save only the email
95
+	return c.fileStore.Store(authConfig)
96
+}
97
+
98
+// storeCredentialsInStore executes the command to store the credentials in the native store.
99
+func (c *nativeStore) storeCredentialsInStore(config types.AuthConfig) error {
100
+	creds := &credentials.Credentials{
101
+		ServerURL: config.ServerAddress,
102
+		Username:  config.Username,
103
+		Secret:    config.Password,
104
+	}
105
+
106
+	if config.IdentityToken != "" {
107
+		creds.Username = tokenUsername
108
+		creds.Secret = config.IdentityToken
109
+	}
110
+
111
+	return client.Store(c.programFunc, creds)
112
+}
113
+
114
+// getCredentialsFromStore executes the command to get the credentials from the native store.
115
+func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthConfig, error) {
116
+	var ret types.AuthConfig
117
+
118
+	creds, err := client.Get(c.programFunc, serverAddress)
119
+	if err != nil {
120
+		if credentials.IsErrCredentialsNotFound(err) {
121
+			// do not return an error if the credentials are not
122
+			// in the keyckain. Let docker ask for new credentials.
123
+			return ret, nil
124
+		}
125
+		return ret, err
126
+	}
127
+
128
+	if creds.Username == tokenUsername {
129
+		ret.IdentityToken = creds.Secret
130
+	} else {
131
+		ret.Password = creds.Secret
132
+		ret.Username = creds.Username
133
+	}
134
+
135
+	ret.ServerAddress = serverAddress
136
+	return ret, nil
137
+}
138
+
139
+// listCredentialsInStore returns a listing of stored credentials as a map of
140
+// URL -> username.
141
+func (c *nativeStore) listCredentialsInStore() (map[string]string, error) {
142
+	return client.List(c.programFunc)
143
+}
0 144
new file mode 100644
... ...
@@ -0,0 +1,355 @@
0
+package credentials
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"io"
6
+	"io/ioutil"
7
+	"strings"
8
+	"testing"
9
+
10
+	"github.com/docker/docker-credential-helpers/client"
11
+	"github.com/docker/docker-credential-helpers/credentials"
12
+	"github.com/docker/docker/api/types"
13
+)
14
+
15
+const (
16
+	validServerAddress   = "https://index.docker.io/v1"
17
+	validServerAddress2  = "https://example.com:5002"
18
+	invalidServerAddress = "https://foobar.example.com"
19
+	missingCredsAddress  = "https://missing.docker.io/v1"
20
+)
21
+
22
+var errCommandExited = fmt.Errorf("exited 1")
23
+
24
+// mockCommand simulates interactions between the docker client and a remote
25
+// credentials helper.
26
+// Unit tests inject this mocked command into the remote to control execution.
27
+type mockCommand struct {
28
+	arg   string
29
+	input io.Reader
30
+}
31
+
32
+// Output returns responses from the remote credentials helper.
33
+// It mocks those responses based in the input in the mock.
34
+func (m *mockCommand) Output() ([]byte, error) {
35
+	in, err := ioutil.ReadAll(m.input)
36
+	if err != nil {
37
+		return nil, err
38
+	}
39
+	inS := string(in)
40
+
41
+	switch m.arg {
42
+	case "erase":
43
+		switch inS {
44
+		case validServerAddress:
45
+			return nil, nil
46
+		default:
47
+			return []byte("program failed"), errCommandExited
48
+		}
49
+	case "get":
50
+		switch inS {
51
+		case validServerAddress:
52
+			return []byte(`{"Username": "foo", "Secret": "bar"}`), nil
53
+		case validServerAddress2:
54
+			return []byte(`{"Username": "<token>", "Secret": "abcd1234"}`), nil
55
+		case missingCredsAddress:
56
+			return []byte(credentials.NewErrCredentialsNotFound().Error()), errCommandExited
57
+		case invalidServerAddress:
58
+			return []byte("program failed"), errCommandExited
59
+		}
60
+	case "store":
61
+		var c credentials.Credentials
62
+		err := json.NewDecoder(strings.NewReader(inS)).Decode(&c)
63
+		if err != nil {
64
+			return []byte("program failed"), errCommandExited
65
+		}
66
+		switch c.ServerURL {
67
+		case validServerAddress:
68
+			return nil, nil
69
+		default:
70
+			return []byte("program failed"), errCommandExited
71
+		}
72
+	case "list":
73
+		return []byte(fmt.Sprintf(`{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "<token>")), nil
74
+	}
75
+
76
+	return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
77
+}
78
+
79
+// Input sets the input to send to a remote credentials helper.
80
+func (m *mockCommand) Input(in io.Reader) {
81
+	m.input = in
82
+}
83
+
84
+func mockCommandFn(args ...string) client.Program {
85
+	return &mockCommand{
86
+		arg: args[0],
87
+	}
88
+}
89
+
90
+func TestNativeStoreAddCredentials(t *testing.T) {
91
+	f := newConfigFile(make(map[string]types.AuthConfig))
92
+	f.CredentialsStore = "mock"
93
+
94
+	s := &nativeStore{
95
+		programFunc: mockCommandFn,
96
+		fileStore:   NewFileStore(f),
97
+	}
98
+	err := s.Store(types.AuthConfig{
99
+		Username:      "foo",
100
+		Password:      "bar",
101
+		Email:         "foo@example.com",
102
+		ServerAddress: validServerAddress,
103
+	})
104
+
105
+	if err != nil {
106
+		t.Fatal(err)
107
+	}
108
+
109
+	if len(f.AuthConfigs) != 1 {
110
+		t.Fatalf("expected 1 auth config, got %d", len(f.AuthConfigs))
111
+	}
112
+
113
+	a, ok := f.AuthConfigs[validServerAddress]
114
+	if !ok {
115
+		t.Fatalf("expected auth for %s, got %v", validServerAddress, f.AuthConfigs)
116
+	}
117
+	if a.Auth != "" {
118
+		t.Fatalf("expected auth to be empty, got %s", a.Auth)
119
+	}
120
+	if a.Username != "" {
121
+		t.Fatalf("expected username to be empty, got %s", a.Username)
122
+	}
123
+	if a.Password != "" {
124
+		t.Fatalf("expected password to be empty, got %s", a.Password)
125
+	}
126
+	if a.IdentityToken != "" {
127
+		t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
128
+	}
129
+	if a.Email != "foo@example.com" {
130
+		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
131
+	}
132
+}
133
+
134
+func TestNativeStoreAddInvalidCredentials(t *testing.T) {
135
+	f := newConfigFile(make(map[string]types.AuthConfig))
136
+	f.CredentialsStore = "mock"
137
+
138
+	s := &nativeStore{
139
+		programFunc: mockCommandFn,
140
+		fileStore:   NewFileStore(f),
141
+	}
142
+	err := s.Store(types.AuthConfig{
143
+		Username:      "foo",
144
+		Password:      "bar",
145
+		Email:         "foo@example.com",
146
+		ServerAddress: invalidServerAddress,
147
+	})
148
+
149
+	if err == nil {
150
+		t.Fatal("expected error, got nil")
151
+	}
152
+
153
+	if !strings.Contains(err.Error(), "program failed") {
154
+		t.Fatalf("expected `program failed`, got %v", err)
155
+	}
156
+
157
+	if len(f.AuthConfigs) != 0 {
158
+		t.Fatalf("expected 0 auth config, got %d", len(f.AuthConfigs))
159
+	}
160
+}
161
+
162
+func TestNativeStoreGet(t *testing.T) {
163
+	f := newConfigFile(map[string]types.AuthConfig{
164
+		validServerAddress: {
165
+			Email: "foo@example.com",
166
+		},
167
+	})
168
+	f.CredentialsStore = "mock"
169
+
170
+	s := &nativeStore{
171
+		programFunc: mockCommandFn,
172
+		fileStore:   NewFileStore(f),
173
+	}
174
+	a, err := s.Get(validServerAddress)
175
+	if err != nil {
176
+		t.Fatal(err)
177
+	}
178
+
179
+	if a.Username != "foo" {
180
+		t.Fatalf("expected username `foo`, got %s", a.Username)
181
+	}
182
+	if a.Password != "bar" {
183
+		t.Fatalf("expected password `bar`, got %s", a.Password)
184
+	}
185
+	if a.IdentityToken != "" {
186
+		t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
187
+	}
188
+	if a.Email != "foo@example.com" {
189
+		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
190
+	}
191
+}
192
+
193
+func TestNativeStoreGetIdentityToken(t *testing.T) {
194
+	f := newConfigFile(map[string]types.AuthConfig{
195
+		validServerAddress2: {
196
+			Email: "foo@example2.com",
197
+		},
198
+	})
199
+	f.CredentialsStore = "mock"
200
+
201
+	s := &nativeStore{
202
+		programFunc: mockCommandFn,
203
+		fileStore:   NewFileStore(f),
204
+	}
205
+	a, err := s.Get(validServerAddress2)
206
+	if err != nil {
207
+		t.Fatal(err)
208
+	}
209
+
210
+	if a.Username != "" {
211
+		t.Fatalf("expected username to be empty, got %s", a.Username)
212
+	}
213
+	if a.Password != "" {
214
+		t.Fatalf("expected password to be empty, got %s", a.Password)
215
+	}
216
+	if a.IdentityToken != "abcd1234" {
217
+		t.Fatalf("expected identity token `abcd1234`, got %s", a.IdentityToken)
218
+	}
219
+	if a.Email != "foo@example2.com" {
220
+		t.Fatalf("expected email `foo@example2.com`, got %s", a.Email)
221
+	}
222
+}
223
+
224
+func TestNativeStoreGetAll(t *testing.T) {
225
+	f := newConfigFile(map[string]types.AuthConfig{
226
+		validServerAddress: {
227
+			Email: "foo@example.com",
228
+		},
229
+	})
230
+	f.CredentialsStore = "mock"
231
+
232
+	s := &nativeStore{
233
+		programFunc: mockCommandFn,
234
+		fileStore:   NewFileStore(f),
235
+	}
236
+	as, err := s.GetAll()
237
+	if err != nil {
238
+		t.Fatal(err)
239
+	}
240
+
241
+	if len(as) != 2 {
242
+		t.Fatalf("wanted 2, got %d", len(as))
243
+	}
244
+
245
+	if as[validServerAddress].Username != "foo" {
246
+		t.Fatalf("expected username `foo` for %s, got %s", validServerAddress, as[validServerAddress].Username)
247
+	}
248
+	if as[validServerAddress].Password != "bar" {
249
+		t.Fatalf("expected password `bar` for %s, got %s", validServerAddress, as[validServerAddress].Password)
250
+	}
251
+	if as[validServerAddress].IdentityToken != "" {
252
+		t.Fatalf("expected identity to be empty for %s, got %s", validServerAddress, as[validServerAddress].IdentityToken)
253
+	}
254
+	if as[validServerAddress].Email != "foo@example.com" {
255
+		t.Fatalf("expected email `foo@example.com` for %s, got %s", validServerAddress, as[validServerAddress].Email)
256
+	}
257
+	if as[validServerAddress2].Username != "" {
258
+		t.Fatalf("expected username to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Username)
259
+	}
260
+	if as[validServerAddress2].Password != "" {
261
+		t.Fatalf("expected password to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Password)
262
+	}
263
+	if as[validServerAddress2].IdentityToken != "abcd1234" {
264
+		t.Fatalf("expected identity token `abcd1324` for %s, got %s", validServerAddress2, as[validServerAddress2].IdentityToken)
265
+	}
266
+	if as[validServerAddress2].Email != "" {
267
+		t.Fatalf("expected no email for %s, got %s", validServerAddress2, as[validServerAddress2].Email)
268
+	}
269
+}
270
+
271
+func TestNativeStoreGetMissingCredentials(t *testing.T) {
272
+	f := newConfigFile(map[string]types.AuthConfig{
273
+		validServerAddress: {
274
+			Email: "foo@example.com",
275
+		},
276
+	})
277
+	f.CredentialsStore = "mock"
278
+
279
+	s := &nativeStore{
280
+		programFunc: mockCommandFn,
281
+		fileStore:   NewFileStore(f),
282
+	}
283
+	_, err := s.Get(missingCredsAddress)
284
+	if err != nil {
285
+		// missing credentials do not produce an error
286
+		t.Fatal(err)
287
+	}
288
+}
289
+
290
+func TestNativeStoreGetInvalidAddress(t *testing.T) {
291
+	f := newConfigFile(map[string]types.AuthConfig{
292
+		validServerAddress: {
293
+			Email: "foo@example.com",
294
+		},
295
+	})
296
+	f.CredentialsStore = "mock"
297
+
298
+	s := &nativeStore{
299
+		programFunc: mockCommandFn,
300
+		fileStore:   NewFileStore(f),
301
+	}
302
+	_, err := s.Get(invalidServerAddress)
303
+	if err == nil {
304
+		t.Fatal("expected error, got nil")
305
+	}
306
+
307
+	if !strings.Contains(err.Error(), "program failed") {
308
+		t.Fatalf("expected `program failed`, got %v", err)
309
+	}
310
+}
311
+
312
+func TestNativeStoreErase(t *testing.T) {
313
+	f := newConfigFile(map[string]types.AuthConfig{
314
+		validServerAddress: {
315
+			Email: "foo@example.com",
316
+		},
317
+	})
318
+	f.CredentialsStore = "mock"
319
+
320
+	s := &nativeStore{
321
+		programFunc: mockCommandFn,
322
+		fileStore:   NewFileStore(f),
323
+	}
324
+	err := s.Erase(validServerAddress)
325
+	if err != nil {
326
+		t.Fatal(err)
327
+	}
328
+
329
+	if len(f.AuthConfigs) != 0 {
330
+		t.Fatalf("expected 0 auth configs, got %d", len(f.AuthConfigs))
331
+	}
332
+}
333
+
334
+func TestNativeStoreEraseInvalidAddress(t *testing.T) {
335
+	f := newConfigFile(map[string]types.AuthConfig{
336
+		validServerAddress: {
337
+			Email: "foo@example.com",
338
+		},
339
+	})
340
+	f.CredentialsStore = "mock"
341
+
342
+	s := &nativeStore{
343
+		programFunc: mockCommandFn,
344
+		fileStore:   NewFileStore(f),
345
+	}
346
+	err := s.Erase(invalidServerAddress)
347
+	if err == nil {
348
+		t.Fatal("expected error, got nil")
349
+	}
350
+
351
+	if !strings.Contains(err.Error(), "program failed") {
352
+		t.Fatalf("expected `program failed`, got %v", err)
353
+	}
354
+}
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"path/filepath"
7 7
 
8 8
 	"github.com/Sirupsen/logrus"
9
-	"github.com/docker/docker/cliconfig"
9
+	cliconfig "github.com/docker/docker/cli/config"
10 10
 	"github.com/docker/docker/opts"
11 11
 	"github.com/docker/go-connections/tlsconfig"
12 12
 	"github.com/spf13/pflag"
... ...
@@ -49,7 +49,7 @@ func NewCommonOptions() *CommonOptions {
49 49
 // InstallFlags adds flags for the common options on the FlagSet
50 50
 func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
51 51
 	if dockerCertPath == "" {
52
-		dockerCertPath = cliconfig.ConfigDir()
52
+		dockerCertPath = cliconfig.Dir()
53 53
 	}
54 54
 
55 55
 	flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode")
... ...
@@ -18,7 +18,7 @@ import (
18 18
 	"github.com/docker/docker/api/types"
19 19
 	registrytypes "github.com/docker/docker/api/types/registry"
20 20
 	"github.com/docker/docker/cli/command"
21
-	"github.com/docker/docker/cliconfig"
21
+	cliconfig "github.com/docker/docker/cli/config"
22 22
 	"github.com/docker/docker/registry"
23 23
 	"github.com/docker/go-connections/tlsconfig"
24 24
 	"github.com/docker/notary"
... ...
@@ -37,7 +37,7 @@ var (
37 37
 )
38 38
 
39 39
 func trustDirectory() string {
40
-	return filepath.Join(cliconfig.ConfigDir(), "trust")
40
+	return filepath.Join(cliconfig.Dir(), "trust")
41 41
 }
42 42
 
43 43
 // certificateDirectory returns the directory containing
... ...
@@ -49,7 +49,7 @@ func certificateDirectory(server string) (string, error) {
49 49
 		return "", err
50 50
 	}
51 51
 
52
-	return filepath.Join(cliconfig.ConfigDir(), "tls", u.Host), nil
52
+	return filepath.Join(cliconfig.Dir(), "tls", u.Host), nil
53 53
 }
54 54
 
55 55
 // Server returns the base URL for the trust server.
56 56
deleted file mode 100644
... ...
@@ -1,120 +0,0 @@
1
-package cliconfig
2
-
3
-import (
4
-	"fmt"
5
-	"io"
6
-	"os"
7
-	"path/filepath"
8
-
9
-	"github.com/docker/docker/api/types"
10
-	"github.com/docker/docker/cliconfig/configfile"
11
-	"github.com/docker/docker/pkg/homedir"
12
-)
13
-
14
-const (
15
-	// ConfigFileName is the name of config file
16
-	ConfigFileName = "config.json"
17
-	configFileDir  = ".docker"
18
-	oldConfigfile  = ".dockercfg"
19
-)
20
-
21
-var (
22
-	configDir = os.Getenv("DOCKER_CONFIG")
23
-)
24
-
25
-func init() {
26
-	if configDir == "" {
27
-		configDir = filepath.Join(homedir.Get(), configFileDir)
28
-	}
29
-}
30
-
31
-// ConfigDir returns the directory the configuration file is stored in
32
-func ConfigDir() string {
33
-	return configDir
34
-}
35
-
36
-// SetConfigDir sets the directory the configuration file is stored in
37
-func SetConfigDir(dir string) {
38
-	configDir = dir
39
-}
40
-
41
-// NewConfigFile initializes an empty configuration file for the given filename 'fn'
42
-func NewConfigFile(fn string) *configfile.ConfigFile {
43
-	return &configfile.ConfigFile{
44
-		AuthConfigs: make(map[string]types.AuthConfig),
45
-		HTTPHeaders: make(map[string]string),
46
-		Filename:    fn,
47
-	}
48
-}
49
-
50
-// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
51
-// a non-nested reader
52
-func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
53
-	configFile := configfile.ConfigFile{
54
-		AuthConfigs: make(map[string]types.AuthConfig),
55
-	}
56
-	err := configFile.LegacyLoadFromReader(configData)
57
-	return &configFile, err
58
-}
59
-
60
-// LoadFromReader is a convenience function that creates a ConfigFile object from
61
-// a reader
62
-func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
63
-	configFile := configfile.ConfigFile{
64
-		AuthConfigs: make(map[string]types.AuthConfig),
65
-	}
66
-	err := configFile.LoadFromReader(configData)
67
-	return &configFile, err
68
-}
69
-
70
-// Load reads the configuration files in the given directory, and sets up
71
-// the auth config information and returns values.
72
-// FIXME: use the internal golang config parser
73
-func Load(configDir string) (*configfile.ConfigFile, error) {
74
-	if configDir == "" {
75
-		configDir = ConfigDir()
76
-	}
77
-
78
-	configFile := configfile.ConfigFile{
79
-		AuthConfigs: make(map[string]types.AuthConfig),
80
-		Filename:    filepath.Join(configDir, ConfigFileName),
81
-	}
82
-
83
-	// Try happy path first - latest config file
84
-	if _, err := os.Stat(configFile.Filename); err == nil {
85
-		file, err := os.Open(configFile.Filename)
86
-		if err != nil {
87
-			return &configFile, fmt.Errorf("%s - %v", configFile.Filename, err)
88
-		}
89
-		defer file.Close()
90
-		err = configFile.LoadFromReader(file)
91
-		if err != nil {
92
-			err = fmt.Errorf("%s - %v", configFile.Filename, err)
93
-		}
94
-		return &configFile, err
95
-	} else if !os.IsNotExist(err) {
96
-		// if file is there but we can't stat it for any reason other
97
-		// than it doesn't exist then stop
98
-		return &configFile, fmt.Errorf("%s - %v", configFile.Filename, err)
99
-	}
100
-
101
-	// Can't find latest config file so check for the old one
102
-	confFile := filepath.Join(homedir.Get(), oldConfigfile)
103
-	if _, err := os.Stat(confFile); err != nil {
104
-		return &configFile, nil //missing file is not an error
105
-	}
106
-	file, err := os.Open(confFile)
107
-	if err != nil {
108
-		return &configFile, fmt.Errorf("%s - %v", confFile, err)
109
-	}
110
-	defer file.Close()
111
-	err = configFile.LegacyLoadFromReader(file)
112
-	if err != nil {
113
-		return &configFile, fmt.Errorf("%s - %v", confFile, err)
114
-	}
115
-
116
-	if configFile.HTTPHeaders == nil {
117
-		configFile.HTTPHeaders = map[string]string{}
118
-	}
119
-	return &configFile, nil
120
-}
121 1
deleted file mode 100644
... ...
@@ -1,621 +0,0 @@
1
-package cliconfig
2
-
3
-import (
4
-	"io/ioutil"
5
-	"os"
6
-	"path/filepath"
7
-	"strings"
8
-	"testing"
9
-
10
-	"github.com/docker/docker/cliconfig/configfile"
11
-	"github.com/docker/docker/pkg/homedir"
12
-)
13
-
14
-func TestEmptyConfigDir(t *testing.T) {
15
-	tmpHome, err := ioutil.TempDir("", "config-test")
16
-	if err != nil {
17
-		t.Fatal(err)
18
-	}
19
-	defer os.RemoveAll(tmpHome)
20
-
21
-	SetConfigDir(tmpHome)
22
-
23
-	config, err := Load("")
24
-	if err != nil {
25
-		t.Fatalf("Failed loading on empty config dir: %q", err)
26
-	}
27
-
28
-	expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName)
29
-	if config.Filename != expectedConfigFilename {
30
-		t.Fatalf("Expected config filename %s, got %s", expectedConfigFilename, config.Filename)
31
-	}
32
-
33
-	// Now save it and make sure it shows up in new form
34
-	saveConfigAndValidateNewFormat(t, config, tmpHome)
35
-}
36
-
37
-func TestMissingFile(t *testing.T) {
38
-	tmpHome, err := ioutil.TempDir("", "config-test")
39
-	if err != nil {
40
-		t.Fatal(err)
41
-	}
42
-	defer os.RemoveAll(tmpHome)
43
-
44
-	config, err := Load(tmpHome)
45
-	if err != nil {
46
-		t.Fatalf("Failed loading on missing file: %q", err)
47
-	}
48
-
49
-	// Now save it and make sure it shows up in new form
50
-	saveConfigAndValidateNewFormat(t, config, tmpHome)
51
-}
52
-
53
-func TestSaveFileToDirs(t *testing.T) {
54
-	tmpHome, err := ioutil.TempDir("", "config-test")
55
-	if err != nil {
56
-		t.Fatal(err)
57
-	}
58
-	defer os.RemoveAll(tmpHome)
59
-
60
-	tmpHome += "/.docker"
61
-
62
-	config, err := Load(tmpHome)
63
-	if err != nil {
64
-		t.Fatalf("Failed loading on missing file: %q", err)
65
-	}
66
-
67
-	// Now save it and make sure it shows up in new form
68
-	saveConfigAndValidateNewFormat(t, config, tmpHome)
69
-}
70
-
71
-func TestEmptyFile(t *testing.T) {
72
-	tmpHome, err := ioutil.TempDir("", "config-test")
73
-	if err != nil {
74
-		t.Fatal(err)
75
-	}
76
-	defer os.RemoveAll(tmpHome)
77
-
78
-	fn := filepath.Join(tmpHome, ConfigFileName)
79
-	if err := ioutil.WriteFile(fn, []byte(""), 0600); err != nil {
80
-		t.Fatal(err)
81
-	}
82
-
83
-	_, err = Load(tmpHome)
84
-	if err == nil {
85
-		t.Fatalf("Was supposed to fail")
86
-	}
87
-}
88
-
89
-func TestEmptyJSON(t *testing.T) {
90
-	tmpHome, err := ioutil.TempDir("", "config-test")
91
-	if err != nil {
92
-		t.Fatal(err)
93
-	}
94
-	defer os.RemoveAll(tmpHome)
95
-
96
-	fn := filepath.Join(tmpHome, ConfigFileName)
97
-	if err := ioutil.WriteFile(fn, []byte("{}"), 0600); err != nil {
98
-		t.Fatal(err)
99
-	}
100
-
101
-	config, err := Load(tmpHome)
102
-	if err != nil {
103
-		t.Fatalf("Failed loading on empty json file: %q", err)
104
-	}
105
-
106
-	// Now save it and make sure it shows up in new form
107
-	saveConfigAndValidateNewFormat(t, config, tmpHome)
108
-}
109
-
110
-func TestOldInvalidsAuth(t *testing.T) {
111
-	invalids := map[string]string{
112
-		`username = test`: "The Auth config file is empty",
113
-		`username
114
-password`: "Invalid Auth config file",
115
-		`username = test
116
-email`: "Invalid auth configuration file",
117
-	}
118
-
119
-	tmpHome, err := ioutil.TempDir("", "config-test")
120
-	if err != nil {
121
-		t.Fatal(err)
122
-	}
123
-	defer os.RemoveAll(tmpHome)
124
-
125
-	homeKey := homedir.Key()
126
-	homeVal := homedir.Get()
127
-
128
-	defer func() { os.Setenv(homeKey, homeVal) }()
129
-	os.Setenv(homeKey, tmpHome)
130
-
131
-	for content, expectedError := range invalids {
132
-		fn := filepath.Join(tmpHome, oldConfigfile)
133
-		if err := ioutil.WriteFile(fn, []byte(content), 0600); err != nil {
134
-			t.Fatal(err)
135
-		}
136
-
137
-		config, err := Load(tmpHome)
138
-		// Use Contains instead of == since the file name will change each time
139
-		if err == nil || !strings.Contains(err.Error(), expectedError) {
140
-			t.Fatalf("Should have failed\nConfig: %v\nGot: %v\nExpected: %v", config, err, expectedError)
141
-		}
142
-
143
-	}
144
-}
145
-
146
-func TestOldValidAuth(t *testing.T) {
147
-	tmpHome, err := ioutil.TempDir("", "config-test")
148
-	if err != nil {
149
-		t.Fatal(err)
150
-	}
151
-	if err != nil {
152
-		t.Fatal(err)
153
-	}
154
-	defer os.RemoveAll(tmpHome)
155
-
156
-	homeKey := homedir.Key()
157
-	homeVal := homedir.Get()
158
-
159
-	defer func() { os.Setenv(homeKey, homeVal) }()
160
-	os.Setenv(homeKey, tmpHome)
161
-
162
-	fn := filepath.Join(tmpHome, oldConfigfile)
163
-	js := `username = am9lam9lOmhlbGxv
164
-	email = user@example.com`
165
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
166
-		t.Fatal(err)
167
-	}
168
-
169
-	config, err := Load(tmpHome)
170
-	if err != nil {
171
-		t.Fatal(err)
172
-	}
173
-
174
-	// defaultIndexserver is https://index.docker.io/v1/
175
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
176
-	if ac.Username != "joejoe" || ac.Password != "hello" {
177
-		t.Fatalf("Missing data from parsing:\n%q", config)
178
-	}
179
-
180
-	// Now save it and make sure it shows up in new form
181
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
182
-
183
-	expConfStr := `{
184
-	"auths": {
185
-		"https://index.docker.io/v1/": {
186
-			"auth": "am9lam9lOmhlbGxv"
187
-		}
188
-	}
189
-}`
190
-
191
-	if configStr != expConfStr {
192
-		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
193
-	}
194
-}
195
-
196
-func TestOldJSONInvalid(t *testing.T) {
197
-	tmpHome, err := ioutil.TempDir("", "config-test")
198
-	if err != nil {
199
-		t.Fatal(err)
200
-	}
201
-	defer os.RemoveAll(tmpHome)
202
-
203
-	homeKey := homedir.Key()
204
-	homeVal := homedir.Get()
205
-
206
-	defer func() { os.Setenv(homeKey, homeVal) }()
207
-	os.Setenv(homeKey, tmpHome)
208
-
209
-	fn := filepath.Join(tmpHome, oldConfigfile)
210
-	js := `{"https://index.docker.io/v1/":{"auth":"test","email":"user@example.com"}}`
211
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
212
-		t.Fatal(err)
213
-	}
214
-
215
-	config, err := Load(tmpHome)
216
-	// Use Contains instead of == since the file name will change each time
217
-	if err == nil || !strings.Contains(err.Error(), "Invalid auth configuration file") {
218
-		t.Fatalf("Expected an error got : %v, %v", config, err)
219
-	}
220
-}
221
-
222
-func TestOldJSON(t *testing.T) {
223
-	tmpHome, err := ioutil.TempDir("", "config-test")
224
-	if err != nil {
225
-		t.Fatal(err)
226
-	}
227
-	defer os.RemoveAll(tmpHome)
228
-
229
-	homeKey := homedir.Key()
230
-	homeVal := homedir.Get()
231
-
232
-	defer func() { os.Setenv(homeKey, homeVal) }()
233
-	os.Setenv(homeKey, tmpHome)
234
-
235
-	fn := filepath.Join(tmpHome, oldConfigfile)
236
-	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
237
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
238
-		t.Fatal(err)
239
-	}
240
-
241
-	config, err := Load(tmpHome)
242
-	if err != nil {
243
-		t.Fatalf("Failed loading on empty json file: %q", err)
244
-	}
245
-
246
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
247
-	if ac.Username != "joejoe" || ac.Password != "hello" {
248
-		t.Fatalf("Missing data from parsing:\n%q", config)
249
-	}
250
-
251
-	// Now save it and make sure it shows up in new form
252
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
253
-
254
-	expConfStr := `{
255
-	"auths": {
256
-		"https://index.docker.io/v1/": {
257
-			"auth": "am9lam9lOmhlbGxv",
258
-			"email": "user@example.com"
259
-		}
260
-	}
261
-}`
262
-
263
-	if configStr != expConfStr {
264
-		t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr)
265
-	}
266
-}
267
-
268
-func TestNewJSON(t *testing.T) {
269
-	tmpHome, err := ioutil.TempDir("", "config-test")
270
-	if err != nil {
271
-		t.Fatal(err)
272
-	}
273
-	defer os.RemoveAll(tmpHome)
274
-
275
-	fn := filepath.Join(tmpHome, ConfigFileName)
276
-	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
277
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
278
-		t.Fatal(err)
279
-	}
280
-
281
-	config, err := Load(tmpHome)
282
-	if err != nil {
283
-		t.Fatalf("Failed loading on empty json file: %q", err)
284
-	}
285
-
286
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
287
-	if ac.Username != "joejoe" || ac.Password != "hello" {
288
-		t.Fatalf("Missing data from parsing:\n%q", config)
289
-	}
290
-
291
-	// Now save it and make sure it shows up in new form
292
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
293
-
294
-	expConfStr := `{
295
-	"auths": {
296
-		"https://index.docker.io/v1/": {
297
-			"auth": "am9lam9lOmhlbGxv"
298
-		}
299
-	}
300
-}`
301
-
302
-	if configStr != expConfStr {
303
-		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
304
-	}
305
-}
306
-
307
-func TestNewJSONNoEmail(t *testing.T) {
308
-	tmpHome, err := ioutil.TempDir("", "config-test")
309
-	if err != nil {
310
-		t.Fatal(err)
311
-	}
312
-	defer os.RemoveAll(tmpHome)
313
-
314
-	fn := filepath.Join(tmpHome, ConfigFileName)
315
-	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
316
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
317
-		t.Fatal(err)
318
-	}
319
-
320
-	config, err := Load(tmpHome)
321
-	if err != nil {
322
-		t.Fatalf("Failed loading on empty json file: %q", err)
323
-	}
324
-
325
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
326
-	if ac.Username != "joejoe" || ac.Password != "hello" {
327
-		t.Fatalf("Missing data from parsing:\n%q", config)
328
-	}
329
-
330
-	// Now save it and make sure it shows up in new form
331
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
332
-
333
-	expConfStr := `{
334
-	"auths": {
335
-		"https://index.docker.io/v1/": {
336
-			"auth": "am9lam9lOmhlbGxv"
337
-		}
338
-	}
339
-}`
340
-
341
-	if configStr != expConfStr {
342
-		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
343
-	}
344
-}
345
-
346
-func TestJSONWithPsFormat(t *testing.T) {
347
-	tmpHome, err := ioutil.TempDir("", "config-test")
348
-	if err != nil {
349
-		t.Fatal(err)
350
-	}
351
-	defer os.RemoveAll(tmpHome)
352
-
353
-	fn := filepath.Join(tmpHome, ConfigFileName)
354
-	js := `{
355
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
356
-		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
357
-}`
358
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
359
-		t.Fatal(err)
360
-	}
361
-
362
-	config, err := Load(tmpHome)
363
-	if err != nil {
364
-		t.Fatalf("Failed loading on empty json file: %q", err)
365
-	}
366
-
367
-	if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
368
-		t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
369
-	}
370
-
371
-	// Now save it and make sure it shows up in new form
372
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
373
-	if !strings.Contains(configStr, `"psFormat":`) ||
374
-		!strings.Contains(configStr, "{{.ID}}") {
375
-		t.Fatalf("Should have save in new form: %s", configStr)
376
-	}
377
-}
378
-
379
-func TestJSONWithCredentialStore(t *testing.T) {
380
-	tmpHome, err := ioutil.TempDir("", "config-test")
381
-	if err != nil {
382
-		t.Fatal(err)
383
-	}
384
-	defer os.RemoveAll(tmpHome)
385
-
386
-	fn := filepath.Join(tmpHome, ConfigFileName)
387
-	js := `{
388
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
389
-		"credsStore": "crazy-secure-storage"
390
-}`
391
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
392
-		t.Fatal(err)
393
-	}
394
-
395
-	config, err := Load(tmpHome)
396
-	if err != nil {
397
-		t.Fatalf("Failed loading on empty json file: %q", err)
398
-	}
399
-
400
-	if config.CredentialsStore != "crazy-secure-storage" {
401
-		t.Fatalf("Unknown credential store: %s\n", config.CredentialsStore)
402
-	}
403
-
404
-	// Now save it and make sure it shows up in new form
405
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
406
-	if !strings.Contains(configStr, `"credsStore":`) ||
407
-		!strings.Contains(configStr, "crazy-secure-storage") {
408
-		t.Fatalf("Should have save in new form: %s", configStr)
409
-	}
410
-}
411
-
412
-func TestJSONWithCredentialHelpers(t *testing.T) {
413
-	tmpHome, err := ioutil.TempDir("", "config-test")
414
-	if err != nil {
415
-		t.Fatal(err)
416
-	}
417
-	defer os.RemoveAll(tmpHome)
418
-
419
-	fn := filepath.Join(tmpHome, ConfigFileName)
420
-	js := `{
421
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
422
-		"credHelpers": { "images.io": "images-io", "containers.com": "crazy-secure-storage" }
423
-}`
424
-	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
425
-		t.Fatal(err)
426
-	}
427
-
428
-	config, err := Load(tmpHome)
429
-	if err != nil {
430
-		t.Fatalf("Failed loading on empty json file: %q", err)
431
-	}
432
-
433
-	if config.CredentialHelpers == nil {
434
-		t.Fatal("config.CredentialHelpers was nil")
435
-	} else if config.CredentialHelpers["images.io"] != "images-io" ||
436
-		config.CredentialHelpers["containers.com"] != "crazy-secure-storage" {
437
-		t.Fatalf("Credential helpers not deserialized properly: %v\n", config.CredentialHelpers)
438
-	}
439
-
440
-	// Now save it and make sure it shows up in new form
441
-	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
442
-	if !strings.Contains(configStr, `"credHelpers":`) ||
443
-		!strings.Contains(configStr, "images.io") ||
444
-		!strings.Contains(configStr, "images-io") ||
445
-		!strings.Contains(configStr, "containers.com") ||
446
-		!strings.Contains(configStr, "crazy-secure-storage") {
447
-		t.Fatalf("Should have save in new form: %s", configStr)
448
-	}
449
-}
450
-
451
-// Save it and make sure it shows up in new form
452
-func saveConfigAndValidateNewFormat(t *testing.T, config *configfile.ConfigFile, homeFolder string) string {
453
-	if err := config.Save(); err != nil {
454
-		t.Fatalf("Failed to save: %q", err)
455
-	}
456
-
457
-	buf, err := ioutil.ReadFile(filepath.Join(homeFolder, ConfigFileName))
458
-	if err != nil {
459
-		t.Fatal(err)
460
-	}
461
-	if !strings.Contains(string(buf), `"auths":`) {
462
-		t.Fatalf("Should have save in new form: %s", string(buf))
463
-	}
464
-	return string(buf)
465
-}
466
-
467
-func TestConfigDir(t *testing.T) {
468
-	tmpHome, err := ioutil.TempDir("", "config-test")
469
-	if err != nil {
470
-		t.Fatal(err)
471
-	}
472
-	defer os.RemoveAll(tmpHome)
473
-
474
-	if ConfigDir() == tmpHome {
475
-		t.Fatalf("Expected ConfigDir to be different than %s by default, but was the same", tmpHome)
476
-	}
477
-
478
-	// Update configDir
479
-	SetConfigDir(tmpHome)
480
-
481
-	if ConfigDir() != tmpHome {
482
-		t.Fatalf("Expected ConfigDir to %s, but was %s", tmpHome, ConfigDir())
483
-	}
484
-}
485
-
486
-func TestConfigFile(t *testing.T) {
487
-	configFilename := "configFilename"
488
-	configFile := NewConfigFile(configFilename)
489
-
490
-	if configFile.Filename != configFilename {
491
-		t.Fatalf("Expected %s, got %s", configFilename, configFile.Filename)
492
-	}
493
-}
494
-
495
-func TestJSONReaderNoFile(t *testing.T) {
496
-	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
497
-
498
-	config, err := LoadFromReader(strings.NewReader(js))
499
-	if err != nil {
500
-		t.Fatalf("Failed loading on empty json file: %q", err)
501
-	}
502
-
503
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
504
-	if ac.Username != "joejoe" || ac.Password != "hello" {
505
-		t.Fatalf("Missing data from parsing:\n%q", config)
506
-	}
507
-
508
-}
509
-
510
-func TestOldJSONReaderNoFile(t *testing.T) {
511
-	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
512
-
513
-	config, err := LegacyLoadFromReader(strings.NewReader(js))
514
-	if err != nil {
515
-		t.Fatalf("Failed loading on empty json file: %q", err)
516
-	}
517
-
518
-	ac := config.AuthConfigs["https://index.docker.io/v1/"]
519
-	if ac.Username != "joejoe" || ac.Password != "hello" {
520
-		t.Fatalf("Missing data from parsing:\n%q", config)
521
-	}
522
-}
523
-
524
-func TestJSONWithPsFormatNoFile(t *testing.T) {
525
-	js := `{
526
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
527
-		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
528
-}`
529
-	config, err := LoadFromReader(strings.NewReader(js))
530
-	if err != nil {
531
-		t.Fatalf("Failed loading on empty json file: %q", err)
532
-	}
533
-
534
-	if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
535
-		t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
536
-	}
537
-
538
-}
539
-
540
-func TestJSONSaveWithNoFile(t *testing.T) {
541
-	js := `{
542
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } },
543
-		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
544
-}`
545
-	config, err := LoadFromReader(strings.NewReader(js))
546
-	err = config.Save()
547
-	if err == nil {
548
-		t.Fatalf("Expected error. File should not have been able to save with no file name.")
549
-	}
550
-
551
-	tmpHome, err := ioutil.TempDir("", "config-test")
552
-	if err != nil {
553
-		t.Fatalf("Failed to create a temp dir: %q", err)
554
-	}
555
-	defer os.RemoveAll(tmpHome)
556
-
557
-	fn := filepath.Join(tmpHome, ConfigFileName)
558
-	f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
559
-	defer f.Close()
560
-
561
-	err = config.SaveToWriter(f)
562
-	if err != nil {
563
-		t.Fatalf("Failed saving to file: %q", err)
564
-	}
565
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
566
-	if err != nil {
567
-		t.Fatal(err)
568
-	}
569
-	expConfStr := `{
570
-	"auths": {
571
-		"https://index.docker.io/v1/": {
572
-			"auth": "am9lam9lOmhlbGxv"
573
-		}
574
-	},
575
-	"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
576
-}`
577
-	if string(buf) != expConfStr {
578
-		t.Fatalf("Should have save in new form: \n%s\nnot \n%s", string(buf), expConfStr)
579
-	}
580
-}
581
-
582
-func TestLegacyJSONSaveWithNoFile(t *testing.T) {
583
-
584
-	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
585
-	config, err := LegacyLoadFromReader(strings.NewReader(js))
586
-	err = config.Save()
587
-	if err == nil {
588
-		t.Fatalf("Expected error. File should not have been able to save with no file name.")
589
-	}
590
-
591
-	tmpHome, err := ioutil.TempDir("", "config-test")
592
-	if err != nil {
593
-		t.Fatalf("Failed to create a temp dir: %q", err)
594
-	}
595
-	defer os.RemoveAll(tmpHome)
596
-
597
-	fn := filepath.Join(tmpHome, ConfigFileName)
598
-	f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
599
-	defer f.Close()
600
-
601
-	if err = config.SaveToWriter(f); err != nil {
602
-		t.Fatalf("Failed saving to file: %q", err)
603
-	}
604
-	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
605
-	if err != nil {
606
-		t.Fatal(err)
607
-	}
608
-
609
-	expConfStr := `{
610
-	"auths": {
611
-		"https://index.docker.io/v1/": {
612
-			"auth": "am9lam9lOmhlbGxv",
613
-			"email": "user@example.com"
614
-		}
615
-	}
616
-}`
617
-
618
-	if string(buf) != expConfStr {
619
-		t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr)
620
-	}
621
-}
622 1
deleted file mode 100644
... ...
@@ -1,183 +0,0 @@
1
-package configfile
2
-
3
-import (
4
-	"encoding/base64"
5
-	"encoding/json"
6
-	"fmt"
7
-	"io"
8
-	"io/ioutil"
9
-	"os"
10
-	"path/filepath"
11
-	"strings"
12
-
13
-	"github.com/docker/docker/api/types"
14
-)
15
-
16
-const (
17
-	// This constant is only used for really old config files when the
18
-	// URL wasn't saved as part of the config file and it was just
19
-	// assumed to be this value.
20
-	defaultIndexserver = "https://index.docker.io/v1/"
21
-)
22
-
23
-// ConfigFile ~/.docker/config.json file info
24
-type ConfigFile struct {
25
-	AuthConfigs          map[string]types.AuthConfig `json:"auths"`
26
-	HTTPHeaders          map[string]string           `json:"HttpHeaders,omitempty"`
27
-	PsFormat             string                      `json:"psFormat,omitempty"`
28
-	ImagesFormat         string                      `json:"imagesFormat,omitempty"`
29
-	NetworksFormat       string                      `json:"networksFormat,omitempty"`
30
-	VolumesFormat        string                      `json:"volumesFormat,omitempty"`
31
-	StatsFormat          string                      `json:"statsFormat,omitempty"`
32
-	DetachKeys           string                      `json:"detachKeys,omitempty"`
33
-	CredentialsStore     string                      `json:"credsStore,omitempty"`
34
-	CredentialHelpers    map[string]string           `json:"credHelpers,omitempty"`
35
-	Filename             string                      `json:"-"` // Note: for internal use only
36
-	ServiceInspectFormat string                      `json:"serviceInspectFormat,omitempty"`
37
-}
38
-
39
-// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
40
-// auth config information with given directory and populates the receiver object
41
-func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
42
-	b, err := ioutil.ReadAll(configData)
43
-	if err != nil {
44
-		return err
45
-	}
46
-
47
-	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
48
-		arr := strings.Split(string(b), "\n")
49
-		if len(arr) < 2 {
50
-			return fmt.Errorf("The Auth config file is empty")
51
-		}
52
-		authConfig := types.AuthConfig{}
53
-		origAuth := strings.Split(arr[0], " = ")
54
-		if len(origAuth) != 2 {
55
-			return fmt.Errorf("Invalid Auth config file")
56
-		}
57
-		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
58
-		if err != nil {
59
-			return err
60
-		}
61
-		authConfig.ServerAddress = defaultIndexserver
62
-		configFile.AuthConfigs[defaultIndexserver] = authConfig
63
-	} else {
64
-		for k, authConfig := range configFile.AuthConfigs {
65
-			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
66
-			if err != nil {
67
-				return err
68
-			}
69
-			authConfig.Auth = ""
70
-			authConfig.ServerAddress = k
71
-			configFile.AuthConfigs[k] = authConfig
72
-		}
73
-	}
74
-	return nil
75
-}
76
-
77
-// LoadFromReader reads the configuration data given and sets up the auth config
78
-// information with given directory and populates the receiver object
79
-func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
80
-	if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
81
-		return err
82
-	}
83
-	var err error
84
-	for addr, ac := range configFile.AuthConfigs {
85
-		ac.Username, ac.Password, err = decodeAuth(ac.Auth)
86
-		if err != nil {
87
-			return err
88
-		}
89
-		ac.Auth = ""
90
-		ac.ServerAddress = addr
91
-		configFile.AuthConfigs[addr] = ac
92
-	}
93
-	return nil
94
-}
95
-
96
-// ContainsAuth returns whether there is authentication configured
97
-// in this file or not.
98
-func (configFile *ConfigFile) ContainsAuth() bool {
99
-	return configFile.CredentialsStore != "" ||
100
-		len(configFile.CredentialHelpers) > 0 ||
101
-		len(configFile.AuthConfigs) > 0
102
-}
103
-
104
-// SaveToWriter encodes and writes out all the authorization information to
105
-// the given writer
106
-func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
107
-	// Encode sensitive data into a new/temp struct
108
-	tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
109
-	for k, authConfig := range configFile.AuthConfigs {
110
-		authCopy := authConfig
111
-		// encode and save the authstring, while blanking out the original fields
112
-		authCopy.Auth = encodeAuth(&authCopy)
113
-		authCopy.Username = ""
114
-		authCopy.Password = ""
115
-		authCopy.ServerAddress = ""
116
-		tmpAuthConfigs[k] = authCopy
117
-	}
118
-
119
-	saveAuthConfigs := configFile.AuthConfigs
120
-	configFile.AuthConfigs = tmpAuthConfigs
121
-	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
122
-
123
-	data, err := json.MarshalIndent(configFile, "", "\t")
124
-	if err != nil {
125
-		return err
126
-	}
127
-	_, err = writer.Write(data)
128
-	return err
129
-}
130
-
131
-// Save encodes and writes out all the authorization information
132
-func (configFile *ConfigFile) Save() error {
133
-	if configFile.Filename == "" {
134
-		return fmt.Errorf("Can't save config with empty filename")
135
-	}
136
-
137
-	if err := os.MkdirAll(filepath.Dir(configFile.Filename), 0700); err != nil {
138
-		return err
139
-	}
140
-	f, err := os.OpenFile(configFile.Filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
141
-	if err != nil {
142
-		return err
143
-	}
144
-	defer f.Close()
145
-	return configFile.SaveToWriter(f)
146
-}
147
-
148
-// encodeAuth creates a base64 encoded string to containing authorization information
149
-func encodeAuth(authConfig *types.AuthConfig) string {
150
-	if authConfig.Username == "" && authConfig.Password == "" {
151
-		return ""
152
-	}
153
-
154
-	authStr := authConfig.Username + ":" + authConfig.Password
155
-	msg := []byte(authStr)
156
-	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
157
-	base64.StdEncoding.Encode(encoded, msg)
158
-	return string(encoded)
159
-}
160
-
161
-// decodeAuth decodes a base64 encoded string and returns username and password
162
-func decodeAuth(authStr string) (string, string, error) {
163
-	if authStr == "" {
164
-		return "", "", nil
165
-	}
166
-
167
-	decLen := base64.StdEncoding.DecodedLen(len(authStr))
168
-	decoded := make([]byte, decLen)
169
-	authByte := []byte(authStr)
170
-	n, err := base64.StdEncoding.Decode(decoded, authByte)
171
-	if err != nil {
172
-		return "", "", err
173
-	}
174
-	if n > decLen {
175
-		return "", "", fmt.Errorf("Something went wrong decoding auth config")
176
-	}
177
-	arr := strings.SplitN(string(decoded), ":", 2)
178
-	if len(arr) != 2 {
179
-		return "", "", fmt.Errorf("Invalid auth configuration file")
180
-	}
181
-	password := strings.Trim(arr[1], "\x00")
182
-	return arr[0], password, nil
183
-}
184 1
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-package configfile
2
-
3
-import (
4
-	"testing"
5
-
6
-	"github.com/docker/docker/api/types"
7
-)
8
-
9
-func TestEncodeAuth(t *testing.T) {
10
-	newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"}
11
-	authStr := encodeAuth(newAuthConfig)
12
-	decAuthConfig := &types.AuthConfig{}
13
-	var err error
14
-	decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
15
-	if err != nil {
16
-		t.Fatal(err)
17
-	}
18
-	if newAuthConfig.Username != decAuthConfig.Username {
19
-		t.Fatal("Encode Username doesn't match decoded Username")
20
-	}
21
-	if newAuthConfig.Password != decAuthConfig.Password {
22
-		t.Fatal("Encode Password doesn't match decoded Password")
23
-	}
24
-	if authStr != "a2VuOnRlc3Q=" {
25
-		t.Fatal("AuthString encoding isn't correct.")
26
-	}
27
-}
28 1
deleted file mode 100644
... ...
@@ -1,17 +0,0 @@
1
-package credentials
2
-
3
-import (
4
-	"github.com/docker/docker/api/types"
5
-)
6
-
7
-// Store is the interface that any credentials store must implement.
8
-type Store interface {
9
-	// Erase removes credentials from the store for a given server.
10
-	Erase(serverAddress string) error
11
-	// Get retrieves credentials from the store for a given server.
12
-	Get(serverAddress string) (types.AuthConfig, error)
13
-	// GetAll retrieves all the credentials from the store.
14
-	GetAll() (map[string]types.AuthConfig, error)
15
-	// Store saves credentials in the store.
16
-	Store(authConfig types.AuthConfig) error
17
-}
18 1
deleted file mode 100644
... ...
@@ -1,22 +0,0 @@
1
-package credentials
2
-
3
-import (
4
-	"os/exec"
5
-
6
-	"github.com/docker/docker/cliconfig/configfile"
7
-)
8
-
9
-// DetectDefaultStore sets the default credentials store
10
-// if the host includes the default store helper program.
11
-func DetectDefaultStore(c *configfile.ConfigFile) {
12
-	if c.CredentialsStore != "" {
13
-		// user defined
14
-		return
15
-	}
16
-
17
-	if defaultCredentialsStore != "" {
18
-		if _, err := exec.LookPath(remoteCredentialsPrefix + defaultCredentialsStore); err == nil {
19
-			c.CredentialsStore = defaultCredentialsStore
20
-		}
21
-	}
22
-}
23 1
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-package credentials
2
-
3
-const defaultCredentialsStore = "osxkeychain"
4 1
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-package credentials
2
-
3
-const defaultCredentialsStore = "secretservice"
4 1
deleted file mode 100644
... ...
@@ -1,5 +0,0 @@
1
-// +build !windows,!darwin,!linux
2
-
3
-package credentials
4
-
5
-const defaultCredentialsStore = ""
6 1
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-package credentials
2
-
3
-const defaultCredentialsStore = "wincred"
4 1
deleted file mode 100644
... ...
@@ -1,53 +0,0 @@
1
-package credentials
2
-
3
-import (
4
-	"github.com/docker/docker/api/types"
5
-	"github.com/docker/docker/cliconfig/configfile"
6
-	"github.com/docker/docker/registry"
7
-)
8
-
9
-// fileStore implements a credentials store using
10
-// the docker configuration file to keep the credentials in plain text.
11
-type fileStore struct {
12
-	file *configfile.ConfigFile
13
-}
14
-
15
-// NewFileStore creates a new file credentials store.
16
-func NewFileStore(file *configfile.ConfigFile) Store {
17
-	return &fileStore{
18
-		file: file,
19
-	}
20
-}
21
-
22
-// Erase removes the given credentials from the file store.
23
-func (c *fileStore) Erase(serverAddress string) error {
24
-	delete(c.file.AuthConfigs, serverAddress)
25
-	return c.file.Save()
26
-}
27
-
28
-// Get retrieves credentials for a specific server from the file store.
29
-func (c *fileStore) Get(serverAddress string) (types.AuthConfig, error) {
30
-	authConfig, ok := c.file.AuthConfigs[serverAddress]
31
-	if !ok {
32
-		// Maybe they have a legacy config file, we will iterate the keys converting
33
-		// them to the new format and testing
34
-		for r, ac := range c.file.AuthConfigs {
35
-			if serverAddress == registry.ConvertToHostname(r) {
36
-				return ac, nil
37
-			}
38
-		}
39
-
40
-		authConfig = types.AuthConfig{}
41
-	}
42
-	return authConfig, nil
43
-}
44
-
45
-func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) {
46
-	return c.file.AuthConfigs, nil
47
-}
48
-
49
-// Store saves the given credentials in the file store.
50
-func (c *fileStore) Store(authConfig types.AuthConfig) error {
51
-	c.file.AuthConfigs[authConfig.ServerAddress] = authConfig
52
-	return c.file.Save()
53
-}
54 1
deleted file mode 100644
... ...
@@ -1,139 +0,0 @@
1
-package credentials
2
-
3
-import (
4
-	"io/ioutil"
5
-	"testing"
6
-
7
-	"github.com/docker/docker/api/types"
8
-	"github.com/docker/docker/cliconfig"
9
-	"github.com/docker/docker/cliconfig/configfile"
10
-)
11
-
12
-func newConfigFile(auths map[string]types.AuthConfig) *configfile.ConfigFile {
13
-	tmp, _ := ioutil.TempFile("", "docker-test")
14
-	name := tmp.Name()
15
-	tmp.Close()
16
-
17
-	c := cliconfig.NewConfigFile(name)
18
-	c.AuthConfigs = auths
19
-	return c
20
-}
21
-
22
-func TestFileStoreAddCredentials(t *testing.T) {
23
-	f := newConfigFile(make(map[string]types.AuthConfig))
24
-
25
-	s := NewFileStore(f)
26
-	err := s.Store(types.AuthConfig{
27
-		Auth:          "super_secret_token",
28
-		Email:         "foo@example.com",
29
-		ServerAddress: "https://example.com",
30
-	})
31
-
32
-	if err != nil {
33
-		t.Fatal(err)
34
-	}
35
-
36
-	if len(f.AuthConfigs) != 1 {
37
-		t.Fatalf("expected 1 auth config, got %d", len(f.AuthConfigs))
38
-	}
39
-
40
-	a, ok := f.AuthConfigs["https://example.com"]
41
-	if !ok {
42
-		t.Fatalf("expected auth for https://example.com, got %v", f.AuthConfigs)
43
-	}
44
-	if a.Auth != "super_secret_token" {
45
-		t.Fatalf("expected auth `super_secret_token`, got %s", a.Auth)
46
-	}
47
-	if a.Email != "foo@example.com" {
48
-		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
49
-	}
50
-}
51
-
52
-func TestFileStoreGet(t *testing.T) {
53
-	f := newConfigFile(map[string]types.AuthConfig{
54
-		"https://example.com": {
55
-			Auth:          "super_secret_token",
56
-			Email:         "foo@example.com",
57
-			ServerAddress: "https://example.com",
58
-		},
59
-	})
60
-
61
-	s := NewFileStore(f)
62
-	a, err := s.Get("https://example.com")
63
-	if err != nil {
64
-		t.Fatal(err)
65
-	}
66
-	if a.Auth != "super_secret_token" {
67
-		t.Fatalf("expected auth `super_secret_token`, got %s", a.Auth)
68
-	}
69
-	if a.Email != "foo@example.com" {
70
-		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
71
-	}
72
-}
73
-
74
-func TestFileStoreGetAll(t *testing.T) {
75
-	s1 := "https://example.com"
76
-	s2 := "https://example2.com"
77
-	f := newConfigFile(map[string]types.AuthConfig{
78
-		s1: {
79
-			Auth:          "super_secret_token",
80
-			Email:         "foo@example.com",
81
-			ServerAddress: "https://example.com",
82
-		},
83
-		s2: {
84
-			Auth:          "super_secret_token2",
85
-			Email:         "foo@example2.com",
86
-			ServerAddress: "https://example2.com",
87
-		},
88
-	})
89
-
90
-	s := NewFileStore(f)
91
-	as, err := s.GetAll()
92
-	if err != nil {
93
-		t.Fatal(err)
94
-	}
95
-	if len(as) != 2 {
96
-		t.Fatalf("wanted 2, got %d", len(as))
97
-	}
98
-	if as[s1].Auth != "super_secret_token" {
99
-		t.Fatalf("expected auth `super_secret_token`, got %s", as[s1].Auth)
100
-	}
101
-	if as[s1].Email != "foo@example.com" {
102
-		t.Fatalf("expected email `foo@example.com`, got %s", as[s1].Email)
103
-	}
104
-	if as[s2].Auth != "super_secret_token2" {
105
-		t.Fatalf("expected auth `super_secret_token2`, got %s", as[s2].Auth)
106
-	}
107
-	if as[s2].Email != "foo@example2.com" {
108
-		t.Fatalf("expected email `foo@example2.com`, got %s", as[s2].Email)
109
-	}
110
-}
111
-
112
-func TestFileStoreErase(t *testing.T) {
113
-	f := newConfigFile(map[string]types.AuthConfig{
114
-		"https://example.com": {
115
-			Auth:          "super_secret_token",
116
-			Email:         "foo@example.com",
117
-			ServerAddress: "https://example.com",
118
-		},
119
-	})
120
-
121
-	s := NewFileStore(f)
122
-	err := s.Erase("https://example.com")
123
-	if err != nil {
124
-		t.Fatal(err)
125
-	}
126
-
127
-	// file store never returns errors, check that the auth config is empty
128
-	a, err := s.Get("https://example.com")
129
-	if err != nil {
130
-		t.Fatal(err)
131
-	}
132
-
133
-	if a.Auth != "" {
134
-		t.Fatalf("expected empty auth token, got %s", a.Auth)
135
-	}
136
-	if a.Email != "" {
137
-		t.Fatalf("expected empty email, got %s", a.Email)
138
-	}
139
-}
140 1
deleted file mode 100644
... ...
@@ -1,144 +0,0 @@
1
-package credentials
2
-
3
-import (
4
-	"github.com/docker/docker-credential-helpers/client"
5
-	"github.com/docker/docker-credential-helpers/credentials"
6
-	"github.com/docker/docker/api/types"
7
-	"github.com/docker/docker/cliconfig/configfile"
8
-)
9
-
10
-const (
11
-	remoteCredentialsPrefix = "docker-credential-"
12
-	tokenUsername           = "<token>"
13
-)
14
-
15
-// nativeStore implements a credentials store
16
-// using native keychain to keep credentials secure.
17
-// It piggybacks into a file store to keep users' emails.
18
-type nativeStore struct {
19
-	programFunc client.ProgramFunc
20
-	fileStore   Store
21
-}
22
-
23
-// NewNativeStore creates a new native store that
24
-// uses a remote helper program to manage credentials.
25
-func NewNativeStore(file *configfile.ConfigFile, helperSuffix string) Store {
26
-	name := remoteCredentialsPrefix + helperSuffix
27
-	return &nativeStore{
28
-		programFunc: client.NewShellProgramFunc(name),
29
-		fileStore:   NewFileStore(file),
30
-	}
31
-}
32
-
33
-// Erase removes the given credentials from the native store.
34
-func (c *nativeStore) Erase(serverAddress string) error {
35
-	if err := client.Erase(c.programFunc, serverAddress); err != nil {
36
-		return err
37
-	}
38
-
39
-	// Fallback to plain text store to remove email
40
-	return c.fileStore.Erase(serverAddress)
41
-}
42
-
43
-// Get retrieves credentials for a specific server from the native store.
44
-func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) {
45
-	// load user email if it exist or an empty auth config.
46
-	auth, _ := c.fileStore.Get(serverAddress)
47
-
48
-	creds, err := c.getCredentialsFromStore(serverAddress)
49
-	if err != nil {
50
-		return auth, err
51
-	}
52
-	auth.Username = creds.Username
53
-	auth.IdentityToken = creds.IdentityToken
54
-	auth.Password = creds.Password
55
-
56
-	return auth, nil
57
-}
58
-
59
-// GetAll retrieves all the credentials from the native store.
60
-func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) {
61
-	auths, err := c.listCredentialsInStore()
62
-	if err != nil {
63
-		return nil, err
64
-	}
65
-
66
-	// Emails are only stored in the file store.
67
-	// This call can be safely eliminated when emails are removed.
68
-	fileConfigs, _ := c.fileStore.GetAll()
69
-
70
-	authConfigs := make(map[string]types.AuthConfig)
71
-	for registry := range auths {
72
-		creds, err := c.getCredentialsFromStore(registry)
73
-		if err != nil {
74
-			return nil, err
75
-		}
76
-		ac, _ := fileConfigs[registry] // might contain Email
77
-		ac.Username = creds.Username
78
-		ac.Password = creds.Password
79
-		ac.IdentityToken = creds.IdentityToken
80
-		authConfigs[registry] = ac
81
-	}
82
-
83
-	return authConfigs, nil
84
-}
85
-
86
-// Store saves the given credentials in the file store.
87
-func (c *nativeStore) Store(authConfig types.AuthConfig) error {
88
-	if err := c.storeCredentialsInStore(authConfig); err != nil {
89
-		return err
90
-	}
91
-	authConfig.Username = ""
92
-	authConfig.Password = ""
93
-	authConfig.IdentityToken = ""
94
-
95
-	// Fallback to old credential in plain text to save only the email
96
-	return c.fileStore.Store(authConfig)
97
-}
98
-
99
-// storeCredentialsInStore executes the command to store the credentials in the native store.
100
-func (c *nativeStore) storeCredentialsInStore(config types.AuthConfig) error {
101
-	creds := &credentials.Credentials{
102
-		ServerURL: config.ServerAddress,
103
-		Username:  config.Username,
104
-		Secret:    config.Password,
105
-	}
106
-
107
-	if config.IdentityToken != "" {
108
-		creds.Username = tokenUsername
109
-		creds.Secret = config.IdentityToken
110
-	}
111
-
112
-	return client.Store(c.programFunc, creds)
113
-}
114
-
115
-// getCredentialsFromStore executes the command to get the credentials from the native store.
116
-func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthConfig, error) {
117
-	var ret types.AuthConfig
118
-
119
-	creds, err := client.Get(c.programFunc, serverAddress)
120
-	if err != nil {
121
-		if credentials.IsErrCredentialsNotFound(err) {
122
-			// do not return an error if the credentials are not
123
-			// in the keyckain. Let docker ask for new credentials.
124
-			return ret, nil
125
-		}
126
-		return ret, err
127
-	}
128
-
129
-	if creds.Username == tokenUsername {
130
-		ret.IdentityToken = creds.Secret
131
-	} else {
132
-		ret.Password = creds.Secret
133
-		ret.Username = creds.Username
134
-	}
135
-
136
-	ret.ServerAddress = serverAddress
137
-	return ret, nil
138
-}
139
-
140
-// listCredentialsInStore returns a listing of stored credentials as a map of
141
-// URL -> username.
142
-func (c *nativeStore) listCredentialsInStore() (map[string]string, error) {
143
-	return client.List(c.programFunc)
144
-}
145 1
deleted file mode 100644
... ...
@@ -1,355 +0,0 @@
1
-package credentials
2
-
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"io"
7
-	"io/ioutil"
8
-	"strings"
9
-	"testing"
10
-
11
-	"github.com/docker/docker-credential-helpers/client"
12
-	"github.com/docker/docker-credential-helpers/credentials"
13
-	"github.com/docker/docker/api/types"
14
-)
15
-
16
-const (
17
-	validServerAddress   = "https://index.docker.io/v1"
18
-	validServerAddress2  = "https://example.com:5002"
19
-	invalidServerAddress = "https://foobar.example.com"
20
-	missingCredsAddress  = "https://missing.docker.io/v1"
21
-)
22
-
23
-var errCommandExited = fmt.Errorf("exited 1")
24
-
25
-// mockCommand simulates interactions between the docker client and a remote
26
-// credentials helper.
27
-// Unit tests inject this mocked command into the remote to control execution.
28
-type mockCommand struct {
29
-	arg   string
30
-	input io.Reader
31
-}
32
-
33
-// Output returns responses from the remote credentials helper.
34
-// It mocks those responses based in the input in the mock.
35
-func (m *mockCommand) Output() ([]byte, error) {
36
-	in, err := ioutil.ReadAll(m.input)
37
-	if err != nil {
38
-		return nil, err
39
-	}
40
-	inS := string(in)
41
-
42
-	switch m.arg {
43
-	case "erase":
44
-		switch inS {
45
-		case validServerAddress:
46
-			return nil, nil
47
-		default:
48
-			return []byte("program failed"), errCommandExited
49
-		}
50
-	case "get":
51
-		switch inS {
52
-		case validServerAddress:
53
-			return []byte(`{"Username": "foo", "Secret": "bar"}`), nil
54
-		case validServerAddress2:
55
-			return []byte(`{"Username": "<token>", "Secret": "abcd1234"}`), nil
56
-		case missingCredsAddress:
57
-			return []byte(credentials.NewErrCredentialsNotFound().Error()), errCommandExited
58
-		case invalidServerAddress:
59
-			return []byte("program failed"), errCommandExited
60
-		}
61
-	case "store":
62
-		var c credentials.Credentials
63
-		err := json.NewDecoder(strings.NewReader(inS)).Decode(&c)
64
-		if err != nil {
65
-			return []byte("program failed"), errCommandExited
66
-		}
67
-		switch c.ServerURL {
68
-		case validServerAddress:
69
-			return nil, nil
70
-		default:
71
-			return []byte("program failed"), errCommandExited
72
-		}
73
-	case "list":
74
-		return []byte(fmt.Sprintf(`{"%s": "%s", "%s": "%s"}`, validServerAddress, "foo", validServerAddress2, "<token>")), nil
75
-	}
76
-
77
-	return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
78
-}
79
-
80
-// Input sets the input to send to a remote credentials helper.
81
-func (m *mockCommand) Input(in io.Reader) {
82
-	m.input = in
83
-}
84
-
85
-func mockCommandFn(args ...string) client.Program {
86
-	return &mockCommand{
87
-		arg: args[0],
88
-	}
89
-}
90
-
91
-func TestNativeStoreAddCredentials(t *testing.T) {
92
-	f := newConfigFile(make(map[string]types.AuthConfig))
93
-	f.CredentialsStore = "mock"
94
-
95
-	s := &nativeStore{
96
-		programFunc: mockCommandFn,
97
-		fileStore:   NewFileStore(f),
98
-	}
99
-	err := s.Store(types.AuthConfig{
100
-		Username:      "foo",
101
-		Password:      "bar",
102
-		Email:         "foo@example.com",
103
-		ServerAddress: validServerAddress,
104
-	})
105
-
106
-	if err != nil {
107
-		t.Fatal(err)
108
-	}
109
-
110
-	if len(f.AuthConfigs) != 1 {
111
-		t.Fatalf("expected 1 auth config, got %d", len(f.AuthConfigs))
112
-	}
113
-
114
-	a, ok := f.AuthConfigs[validServerAddress]
115
-	if !ok {
116
-		t.Fatalf("expected auth for %s, got %v", validServerAddress, f.AuthConfigs)
117
-	}
118
-	if a.Auth != "" {
119
-		t.Fatalf("expected auth to be empty, got %s", a.Auth)
120
-	}
121
-	if a.Username != "" {
122
-		t.Fatalf("expected username to be empty, got %s", a.Username)
123
-	}
124
-	if a.Password != "" {
125
-		t.Fatalf("expected password to be empty, got %s", a.Password)
126
-	}
127
-	if a.IdentityToken != "" {
128
-		t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
129
-	}
130
-	if a.Email != "foo@example.com" {
131
-		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
132
-	}
133
-}
134
-
135
-func TestNativeStoreAddInvalidCredentials(t *testing.T) {
136
-	f := newConfigFile(make(map[string]types.AuthConfig))
137
-	f.CredentialsStore = "mock"
138
-
139
-	s := &nativeStore{
140
-		programFunc: mockCommandFn,
141
-		fileStore:   NewFileStore(f),
142
-	}
143
-	err := s.Store(types.AuthConfig{
144
-		Username:      "foo",
145
-		Password:      "bar",
146
-		Email:         "foo@example.com",
147
-		ServerAddress: invalidServerAddress,
148
-	})
149
-
150
-	if err == nil {
151
-		t.Fatal("expected error, got nil")
152
-	}
153
-
154
-	if !strings.Contains(err.Error(), "program failed") {
155
-		t.Fatalf("expected `program failed`, got %v", err)
156
-	}
157
-
158
-	if len(f.AuthConfigs) != 0 {
159
-		t.Fatalf("expected 0 auth config, got %d", len(f.AuthConfigs))
160
-	}
161
-}
162
-
163
-func TestNativeStoreGet(t *testing.T) {
164
-	f := newConfigFile(map[string]types.AuthConfig{
165
-		validServerAddress: {
166
-			Email: "foo@example.com",
167
-		},
168
-	})
169
-	f.CredentialsStore = "mock"
170
-
171
-	s := &nativeStore{
172
-		programFunc: mockCommandFn,
173
-		fileStore:   NewFileStore(f),
174
-	}
175
-	a, err := s.Get(validServerAddress)
176
-	if err != nil {
177
-		t.Fatal(err)
178
-	}
179
-
180
-	if a.Username != "foo" {
181
-		t.Fatalf("expected username `foo`, got %s", a.Username)
182
-	}
183
-	if a.Password != "bar" {
184
-		t.Fatalf("expected password `bar`, got %s", a.Password)
185
-	}
186
-	if a.IdentityToken != "" {
187
-		t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
188
-	}
189
-	if a.Email != "foo@example.com" {
190
-		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
191
-	}
192
-}
193
-
194
-func TestNativeStoreGetIdentityToken(t *testing.T) {
195
-	f := newConfigFile(map[string]types.AuthConfig{
196
-		validServerAddress2: {
197
-			Email: "foo@example2.com",
198
-		},
199
-	})
200
-	f.CredentialsStore = "mock"
201
-
202
-	s := &nativeStore{
203
-		programFunc: mockCommandFn,
204
-		fileStore:   NewFileStore(f),
205
-	}
206
-	a, err := s.Get(validServerAddress2)
207
-	if err != nil {
208
-		t.Fatal(err)
209
-	}
210
-
211
-	if a.Username != "" {
212
-		t.Fatalf("expected username to be empty, got %s", a.Username)
213
-	}
214
-	if a.Password != "" {
215
-		t.Fatalf("expected password to be empty, got %s", a.Password)
216
-	}
217
-	if a.IdentityToken != "abcd1234" {
218
-		t.Fatalf("expected identity token `abcd1234`, got %s", a.IdentityToken)
219
-	}
220
-	if a.Email != "foo@example2.com" {
221
-		t.Fatalf("expected email `foo@example2.com`, got %s", a.Email)
222
-	}
223
-}
224
-
225
-func TestNativeStoreGetAll(t *testing.T) {
226
-	f := newConfigFile(map[string]types.AuthConfig{
227
-		validServerAddress: {
228
-			Email: "foo@example.com",
229
-		},
230
-	})
231
-	f.CredentialsStore = "mock"
232
-
233
-	s := &nativeStore{
234
-		programFunc: mockCommandFn,
235
-		fileStore:   NewFileStore(f),
236
-	}
237
-	as, err := s.GetAll()
238
-	if err != nil {
239
-		t.Fatal(err)
240
-	}
241
-
242
-	if len(as) != 2 {
243
-		t.Fatalf("wanted 2, got %d", len(as))
244
-	}
245
-
246
-	if as[validServerAddress].Username != "foo" {
247
-		t.Fatalf("expected username `foo` for %s, got %s", validServerAddress, as[validServerAddress].Username)
248
-	}
249
-	if as[validServerAddress].Password != "bar" {
250
-		t.Fatalf("expected password `bar` for %s, got %s", validServerAddress, as[validServerAddress].Password)
251
-	}
252
-	if as[validServerAddress].IdentityToken != "" {
253
-		t.Fatalf("expected identity to be empty for %s, got %s", validServerAddress, as[validServerAddress].IdentityToken)
254
-	}
255
-	if as[validServerAddress].Email != "foo@example.com" {
256
-		t.Fatalf("expected email `foo@example.com` for %s, got %s", validServerAddress, as[validServerAddress].Email)
257
-	}
258
-	if as[validServerAddress2].Username != "" {
259
-		t.Fatalf("expected username to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Username)
260
-	}
261
-	if as[validServerAddress2].Password != "" {
262
-		t.Fatalf("expected password to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Password)
263
-	}
264
-	if as[validServerAddress2].IdentityToken != "abcd1234" {
265
-		t.Fatalf("expected identity token `abcd1324` for %s, got %s", validServerAddress2, as[validServerAddress2].IdentityToken)
266
-	}
267
-	if as[validServerAddress2].Email != "" {
268
-		t.Fatalf("expected no email for %s, got %s", validServerAddress2, as[validServerAddress2].Email)
269
-	}
270
-}
271
-
272
-func TestNativeStoreGetMissingCredentials(t *testing.T) {
273
-	f := newConfigFile(map[string]types.AuthConfig{
274
-		validServerAddress: {
275
-			Email: "foo@example.com",
276
-		},
277
-	})
278
-	f.CredentialsStore = "mock"
279
-
280
-	s := &nativeStore{
281
-		programFunc: mockCommandFn,
282
-		fileStore:   NewFileStore(f),
283
-	}
284
-	_, err := s.Get(missingCredsAddress)
285
-	if err != nil {
286
-		// missing credentials do not produce an error
287
-		t.Fatal(err)
288
-	}
289
-}
290
-
291
-func TestNativeStoreGetInvalidAddress(t *testing.T) {
292
-	f := newConfigFile(map[string]types.AuthConfig{
293
-		validServerAddress: {
294
-			Email: "foo@example.com",
295
-		},
296
-	})
297
-	f.CredentialsStore = "mock"
298
-
299
-	s := &nativeStore{
300
-		programFunc: mockCommandFn,
301
-		fileStore:   NewFileStore(f),
302
-	}
303
-	_, err := s.Get(invalidServerAddress)
304
-	if err == nil {
305
-		t.Fatal("expected error, got nil")
306
-	}
307
-
308
-	if !strings.Contains(err.Error(), "program failed") {
309
-		t.Fatalf("expected `program failed`, got %v", err)
310
-	}
311
-}
312
-
313
-func TestNativeStoreErase(t *testing.T) {
314
-	f := newConfigFile(map[string]types.AuthConfig{
315
-		validServerAddress: {
316
-			Email: "foo@example.com",
317
-		},
318
-	})
319
-	f.CredentialsStore = "mock"
320
-
321
-	s := &nativeStore{
322
-		programFunc: mockCommandFn,
323
-		fileStore:   NewFileStore(f),
324
-	}
325
-	err := s.Erase(validServerAddress)
326
-	if err != nil {
327
-		t.Fatal(err)
328
-	}
329
-
330
-	if len(f.AuthConfigs) != 0 {
331
-		t.Fatalf("expected 0 auth configs, got %d", len(f.AuthConfigs))
332
-	}
333
-}
334
-
335
-func TestNativeStoreEraseInvalidAddress(t *testing.T) {
336
-	f := newConfigFile(map[string]types.AuthConfig{
337
-		validServerAddress: {
338
-			Email: "foo@example.com",
339
-		},
340
-	})
341
-	f.CredentialsStore = "mock"
342
-
343
-	s := &nativeStore{
344
-		programFunc: mockCommandFn,
345
-		fileStore:   NewFileStore(f),
346
-	}
347
-	err := s.Erase(invalidServerAddress)
348
-	if err == nil {
349
-		t.Fatal("expected error, got nil")
350
-	}
351
-
352
-	if !strings.Contains(err.Error(), "program failed") {
353
-		t.Fatalf("expected `program failed`, got %v", err)
354
-	}
355
-}
... ...
@@ -10,9 +10,9 @@ import (
10 10
 	"github.com/docker/docker/cli"
11 11
 	"github.com/docker/docker/cli/command"
12 12
 	"github.com/docker/docker/cli/command/commands"
13
+	cliconfig "github.com/docker/docker/cli/config"
13 14
 	"github.com/docker/docker/cli/debug"
14 15
 	cliflags "github.com/docker/docker/cli/flags"
15
-	"github.com/docker/docker/cliconfig"
16 16
 	"github.com/docker/docker/dockerversion"
17 17
 	"github.com/docker/docker/pkg/term"
18 18
 	"github.com/spf13/cobra"
... ...
@@ -75,7 +75,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
75 75
 
76 76
 	flags = cmd.Flags()
77 77
 	flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")
78
-	flags.StringVar(&opts.ConfigDir, "config", cliconfig.ConfigDir(), "Location of client config files")
78
+	flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files")
79 79
 	opts.Common.InstallFlags(flags)
80 80
 
81 81
 	cmd.SetOutput(dockerCli.Out())
... ...
@@ -126,7 +126,7 @@ func dockerPreRun(opts *cliflags.ClientOptions) {
126 126
 	cliflags.SetLogLevel(opts.Common.LogLevel)
127 127
 
128 128
 	if opts.ConfigDir != "" {
129
-		cliconfig.SetConfigDir(opts.ConfigDir)
129
+		cliconfig.SetDir(opts.ConfigDir)
130 130
 	}
131 131
 
132 132
 	if opts.Common.Debug {
... ...
@@ -26,9 +26,9 @@ import (
26 26
 	systemrouter "github.com/docker/docker/api/server/router/system"
27 27
 	"github.com/docker/docker/api/server/router/volume"
28 28
 	"github.com/docker/docker/builder/dockerfile"
29
+	cliconfig "github.com/docker/docker/cli/config"
29 30
 	"github.com/docker/docker/cli/debug"
30 31
 	cliflags "github.com/docker/docker/cli/flags"
31
-	"github.com/docker/docker/cliconfig"
32 32
 	"github.com/docker/docker/daemon"
33 33
 	"github.com/docker/docker/daemon/cluster"
34 34
 	"github.com/docker/docker/daemon/logger"
... ...
@@ -75,7 +75,7 @@ func migrateKey(config *daemon.Config) (err error) {
75 75
 	}
76 76
 
77 77
 	// Migrate trust key if exists at ~/.docker/key.json and owned by current user
78
-	oldPath := filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
78
+	oldPath := filepath.Join(cliconfig.Dir(), cliflags.DefaultTrustKeyFile)
79 79
 	newPath := filepath.Join(getDaemonConfDir(config.Root), cliflags.DefaultTrustKeyFile)
80 80
 	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
81 81
 		defer func() {
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"testing"
11 11
 
12 12
 	"github.com/docker/docker/api/types/swarm"
13
-	"github.com/docker/docker/cliconfig"
13
+	cliconfig "github.com/docker/docker/cli/config"
14 14
 	"github.com/docker/docker/integration-cli/daemon"
15 15
 	"github.com/docker/docker/pkg/reexec"
16 16
 	"github.com/go-check/check"
... ...
@@ -359,7 +359,7 @@ func (s *DockerTrustSuite) TearDownTest(c *check.C) {
359 359
 	}
360 360
 
361 361
 	// Remove trusted keys and metadata after test
362
-	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
362
+	os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust"))
363 363
 	s.ds.TearDownTest(c)
364 364
 }
365 365
 
... ...
@@ -14,7 +14,7 @@ import (
14 14
 	"time"
15 15
 
16 16
 	"github.com/docker/distribution/reference"
17
-	"github.com/docker/docker/cliconfig"
17
+	cliconfig "github.com/docker/docker/cli/config"
18 18
 	"github.com/docker/docker/pkg/integration/checker"
19 19
 	"github.com/go-check/check"
20 20
 )
... ...
@@ -300,7 +300,7 @@ func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
300 300
 	c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out))
301 301
 
302 302
 	// Assert that we rotated the snapshot key to the server by checking our local keystore
303
-	contents, err := ioutil.ReadDir(filepath.Join(cliconfig.ConfigDir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest"))
303
+	contents, err := ioutil.ReadDir(filepath.Join(cliconfig.Dir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest"))
304 304
 	c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files"))
305 305
 	// Check that we only have 1 key (targets key)
306 306
 	c.Assert(contents, checker.HasLen, 1)
... ...
@@ -496,7 +496,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C)
496 496
 	s.assertTargetNotInRoles(c, repoName, "latest", "targets")
497 497
 
498 498
 	// Try pull after push
499
-	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
499
+	os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust"))
500 500
 
501 501
 	pullCmd := exec.Command(dockerBinary, "pull", targetName)
502 502
 	s.trustedCmd(pullCmd)
... ...
@@ -539,7 +539,7 @@ func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c
539 539
 	s.assertTargetNotInRoles(c, repoName, "latest", "targets")
540 540
 
541 541
 	// Try pull after push
542
-	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
542
+	os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust"))
543 543
 
544 544
 	// pull should fail because none of these are the releases role
545 545
 	pullCmd := exec.Command(dockerBinary, "pull", targetName)
... ...
@@ -580,7 +580,7 @@ func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *
580 580
 	s.assertTargetNotInRoles(c, repoName, "latest", "targets")
581 581
 
582 582
 	// Try pull after push
583
-	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
583
+	os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust"))
584 584
 
585 585
 	// pull should fail because none of these are the releases role
586 586
 	pullCmd := exec.Command(dockerBinary, "pull", targetName)
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	"strings"
12 12
 	"time"
13 13
 
14
-	"github.com/docker/docker/cliconfig"
14
+	cliconfig "github.com/docker/docker/cli/config"
15 15
 	"github.com/docker/docker/pkg/integration/checker"
16 16
 	"github.com/docker/go-connections/tlsconfig"
17 17
 	"github.com/go-check/check"
... ...
@@ -90,7 +90,7 @@ func newTestNotary(c *check.C) (*testNotary, error) {
90 90
 		"skipTLSVerify": true
91 91
 	}
92 92
 }`
93
-	if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.ConfigDir(), "trust"), notaryURL); err != nil {
93
+	if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.Dir(), "trust"), notaryURL); err != nil {
94 94
 		os.RemoveAll(tmp)
95 95
 		return nil, err
96 96
 	}