diff --git a/cli/config/config.go b/cli/config/config.go index b054dc49fa..5e638a519f 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -68,48 +68,42 @@ func Load(configDir string) (*configfile.ConfigFile, error) { configDir = Dir() } - configFile := configfile.ConfigFile{ - AuthConfigs: make(map[string]types.AuthConfig), - Filename: filepath.Join(configDir, ConfigFileName), - } + filename := filepath.Join(configDir, ConfigFileName) + configFile := configfile.NewConfigFile(filename) // Try happy path first - latest config file - if _, err := os.Stat(configFile.Filename); err == nil { - file, err := os.Open(configFile.Filename) + if _, err := os.Stat(filename); err == nil { + file, err := os.Open(filename) if err != nil { - return &configFile, errors.Errorf("%s - %v", configFile.Filename, err) + return configFile, errors.Errorf("%s - %v", filename, err) } defer file.Close() err = configFile.LoadFromReader(file) if err != nil { - err = errors.Errorf("%s - %v", configFile.Filename, err) + err = errors.Errorf("%s - %v", filename, err) } - return &configFile, err + return configFile, err } else if !os.IsNotExist(err) { // if file is there but we can't stat it for any reason other // than it doesn't exist then stop - return &configFile, errors.Errorf("%s - %v", configFile.Filename, err) + return configFile, errors.Errorf("%s - %v", filename, err) } // Can't find latest config file so check for the old one confFile := filepath.Join(homedir.Get(), oldConfigfile) if _, err := os.Stat(confFile); err != nil { - return &configFile, nil //missing file is not an error + return configFile, nil //missing file is not an error } file, err := os.Open(confFile) if err != nil { - return &configFile, errors.Errorf("%s - %v", confFile, err) + return configFile, errors.Errorf("%s - %v", confFile, err) } defer file.Close() err = configFile.LegacyLoadFromReader(file) if err != nil { - return &configFile, errors.Errorf("%s - %v", confFile, err) + return configFile, errors.Errorf("%s - %v", confFile, err) } - - if configFile.HTTPHeaders == nil { - configFile.HTTPHeaders = map[string]string{} - } - return &configFile, nil + return configFile, nil } // LoadDefaultConfigFile attempts to load the default config file and returns diff --git a/cli/config/config_test.go b/cli/config/config_test.go index 4f98df9bff..1248913c5e 100644 --- a/cli/config/config_test.go +++ b/cli/config/config_test.go @@ -1,6 +1,7 @@ package config import ( + "bytes" "io/ioutil" "os" "path/filepath" @@ -8,28 +9,34 @@ import ( "testing" "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/credentials" "github.com/docker/docker/pkg/homedir" + "github.com/docker/docker/pkg/testutil" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestEmptyConfigDir(t *testing.T) { - tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpHome) +func setupConfigDir(t *testing.T) (string, func()) { + tmpdir, err := ioutil.TempDir("", "config-test") + require.NoError(t, err) + oldDir := Dir() + SetDir(tmpdir) - SetDir(tmpHome) + return tmpdir, func() { + SetDir(oldDir) + os.RemoveAll(tmpdir) + } +} + +func TestEmptyConfigDir(t *testing.T) { + tmpHome, cleanup := setupConfigDir(t) + defer cleanup() config, err := Load("") - if err != nil { - t.Fatalf("Failed loading on empty config dir: %q", err) - } + require.NoError(t, err) expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName) - if config.Filename != expectedConfigFilename { - t.Fatalf("Expected config filename %s, got %s", expectedConfigFilename, config.Filename) - } + assert.Equal(t, expectedConfigFilename, config.Filename) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -37,15 +44,11 @@ func TestEmptyConfigDir(t *testing.T) { func TestMissingFile(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on missing file: %q", err) - } + require.NoError(t, err) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -53,17 +56,13 @@ func TestMissingFile(t *testing.T) { func TestSaveFileToDirs(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) tmpHome += "/.docker" config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on missing file: %q", err) - } + require.NoError(t, err) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -71,38 +70,28 @@ func TestSaveFileToDirs(t *testing.T) { func TestEmptyFile(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) - if err := ioutil.WriteFile(fn, []byte(""), 0600); err != nil { - t.Fatal(err) - } + err = ioutil.WriteFile(fn, []byte(""), 0600) + require.NoError(t, err) _, err = Load(tmpHome) - if err == nil { - t.Fatalf("Was supposed to fail") - } + testutil.ErrorContains(t, err, "EOF") } func TestEmptyJSON(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) - if err := ioutil.WriteFile(fn, []byte("{}"), 0600); err != nil { - t.Fatal(err) - } + err = ioutil.WriteFile(fn, []byte("{}"), 0600) + require.NoError(t, err) config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -118,9 +107,7 @@ email`: "Invalid auth configuration file", } tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -131,27 +118,17 @@ email`: "Invalid auth configuration file", for content, expectedError := range invalids { fn := filepath.Join(tmpHome, oldConfigfile) - if err := ioutil.WriteFile(fn, []byte(content), 0600); err != nil { - t.Fatal(err) - } - - config, err := Load(tmpHome) - // Use Contains instead of == since the file name will change each time - if err == nil || !strings.Contains(err.Error(), expectedError) { - t.Fatalf("Should have failed\nConfig: %v\nGot: %v\nExpected: %v", config, err, expectedError) - } + err := ioutil.WriteFile(fn, []byte(content), 0600) + require.NoError(t, err) + _, err = Load(tmpHome) + testutil.ErrorContains(t, err, expectedError) } } func TestOldValidAuth(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -163,14 +140,11 @@ func TestOldValidAuth(t *testing.T) { fn := filepath.Join(tmpHome, oldConfigfile) js := `username = am9lam9lOmhlbGxv email = user@example.com` - if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil { - t.Fatal(err) - } + err = ioutil.WriteFile(fn, []byte(js), 0600) + require.NoError(t, err) config, err := Load(tmpHome) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // defaultIndexserver is https://index.docker.io/v1/ ac := config.AuthConfigs["https://index.docker.io/v1/"] @@ -189,16 +163,12 @@ func TestOldValidAuth(t *testing.T) { } }` - if configStr != expConfStr { - t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr) - } + assert.Equal(t, expConfStr, configStr) } func TestOldJSONInvalid(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -222,9 +192,7 @@ func TestOldJSONInvalid(t *testing.T) { func TestOldJSON(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -240,9 +208,7 @@ func TestOldJSON(t *testing.T) { } config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -268,9 +234,7 @@ func TestOldJSON(t *testing.T) { func TestNewJSON(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -280,9 +244,7 @@ func TestNewJSON(t *testing.T) { } config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -307,9 +269,7 @@ func TestNewJSON(t *testing.T) { func TestNewJSONNoEmail(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -319,9 +279,7 @@ func TestNewJSONNoEmail(t *testing.T) { } config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -346,9 +304,7 @@ func TestNewJSONNoEmail(t *testing.T) { func TestJSONWithPsFormat(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -361,9 +317,7 @@ func TestJSONWithPsFormat(t *testing.T) { } config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` { t.Fatalf("Unknown ps format: %s\n", config.PsFormat) @@ -379,9 +333,7 @@ func TestJSONWithPsFormat(t *testing.T) { func TestJSONWithCredentialStore(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -394,9 +346,7 @@ func TestJSONWithCredentialStore(t *testing.T) { } config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) if config.CredentialsStore != "crazy-secure-storage" { t.Fatalf("Unknown credential store: %s\n", config.CredentialsStore) @@ -412,9 +362,7 @@ func TestJSONWithCredentialStore(t *testing.T) { func TestJSONWithCredentialHelpers(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -427,9 +375,7 @@ func TestJSONWithCredentialHelpers(t *testing.T) { } config, err := Load(tmpHome) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) if config.CredentialHelpers == nil { t.Fatal("config.CredentialHelpers was nil") @@ -450,26 +396,18 @@ func TestJSONWithCredentialHelpers(t *testing.T) { } // Save it and make sure it shows up in new form -func saveConfigAndValidateNewFormat(t *testing.T, config *configfile.ConfigFile, homeFolder string) string { - if err := config.Save(); err != nil { - t.Fatalf("Failed to save: %q", err) - } +func saveConfigAndValidateNewFormat(t *testing.T, config *configfile.ConfigFile, configDir string) string { + require.NoError(t, config.Save()) - buf, err := ioutil.ReadFile(filepath.Join(homeFolder, ConfigFileName)) - if err != nil { - t.Fatal(err) - } - if !strings.Contains(string(buf), `"auths":`) { - t.Fatalf("Should have save in new form: %s", string(buf)) - } + buf, err := ioutil.ReadFile(filepath.Join(configDir, ConfigFileName)) + require.NoError(t, err) + assert.Contains(t, string(buf), `"auths":`) return string(buf) } func TestConfigDir(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) if Dir() == tmpHome { @@ -488,9 +426,7 @@ func TestJSONReaderNoFile(t *testing.T) { js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }` config, err := LoadFromReader(strings.NewReader(js)) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -503,9 +439,7 @@ func TestOldJSONReaderNoFile(t *testing.T) { js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` config, err := LegacyLoadFromReader(strings.NewReader(js)) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -519,9 +453,7 @@ func TestJSONWithPsFormatNoFile(t *testing.T) { "psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}" }` config, err := LoadFromReader(strings.NewReader(js)) - if err != nil { - t.Fatalf("Failed loading on empty json file: %q", err) - } + require.NoError(t, err) if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` { t.Fatalf("Unknown ps format: %s\n", config.PsFormat) @@ -537,28 +469,19 @@ func TestJSONSaveWithNoFile(t *testing.T) { config, err := LoadFromReader(strings.NewReader(js)) require.NoError(t, err) err = config.Save() - if err == nil { - t.Fatalf("Expected error. File should not have been able to save with no file name.") - } + testutil.ErrorContains(t, err, "with empty filename") tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatalf("Failed to create a temp dir: %q", err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) defer f.Close() - err = config.SaveToWriter(f) - if err != nil { - t.Fatalf("Failed saving to file: %q", err) - } + require.NoError(t, config.SaveToWriter(f)) buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) expConfStr := `{ "auths": { "https://index.docker.io/v1/": { @@ -573,32 +496,23 @@ func TestJSONSaveWithNoFile(t *testing.T) { } func TestLegacyJSONSaveWithNoFile(t *testing.T) { - js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` config, err := LegacyLoadFromReader(strings.NewReader(js)) require.NoError(t, err) err = config.Save() - if err == nil { - t.Fatalf("Expected error. File should not have been able to save with no file name.") - } + testutil.ErrorContains(t, err, "with empty filename") tmpHome, err := ioutil.TempDir("", "config-test") - if err != nil { - t.Fatalf("Failed to create a temp dir: %q", err) - } + require.NoError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) defer f.Close() - if err = config.SaveToWriter(f); err != nil { - t.Fatalf("Failed saving to file: %q", err) - } + require.NoError(t, config.SaveToWriter(f)) buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) expConfStr := `{ "auths": { @@ -613,3 +527,22 @@ func TestLegacyJSONSaveWithNoFile(t *testing.T) { t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr) } } + +func TestLoadDefaultConfigFile(t *testing.T) { + dir, cleanup := setupConfigDir(t) + defer cleanup() + buffer := new(bytes.Buffer) + + filename := filepath.Join(dir, ConfigFileName) + content := []byte(`{"PsFormat": "format"}`) + err := ioutil.WriteFile(filename, content, 0644) + require.NoError(t, err) + + configFile := LoadDefaultConfigFile(buffer) + credStore := credentials.DetectDefaultStore("") + expected := configfile.NewConfigFile(filename) + expected.CredentialsStore = credStore + expected.PsFormat = "format" + + assert.Equal(t, expected, configFile) +} diff --git a/cli/config/configfile/file_test.go b/cli/config/configfile/file_test.go index ac3271006e..4e24009777 100644 --- a/cli/config/configfile/file_test.go +++ b/cli/config/configfile/file_test.go @@ -6,26 +6,18 @@ import ( "github.com/docker/docker/api/types" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestEncodeAuth(t *testing.T) { newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"} authStr := encodeAuth(newAuthConfig) - decAuthConfig := &types.AuthConfig{} + + expected := &types.AuthConfig{} var err error - decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr) - if err != nil { - t.Fatal(err) - } - if newAuthConfig.Username != decAuthConfig.Username { - t.Fatal("Encode Username doesn't match decoded Username") - } - if newAuthConfig.Password != decAuthConfig.Password { - t.Fatal("Encode Password doesn't match decoded Password") - } - if authStr != "a2VuOnRlc3Q=" { - t.Fatal("AuthString encoding isn't correct.") - } + expected.Username, expected.Password, err = decodeAuth(authStr) + require.NoError(t, err) + assert.Equal(t, expected, newAuthConfig) } func TestProxyConfig(t *testing.T) { @@ -149,3 +141,19 @@ func TestConfigFile(t *testing.T) { assert.Equal(t, configFilename, configFile.Filename) } + +func TestGetAllCredentials(t *testing.T) { + configFile := NewConfigFile("filename") + exampleAuth := types.AuthConfig{ + Username: "user", + Password: "pass", + } + configFile.AuthConfigs["example.com/foo"] = exampleAuth + + authConfigs, err := configFile.GetAllCredentials() + require.NoError(t, err) + + expected := make(map[string]types.AuthConfig) + expected["example.com/foo"] = exampleAuth + assert.Equal(t, expected, authConfigs) +}