Files
docker-cli/cli/config/config_test.go
Sebastiaan van Stijn 19bcebd122 test: make sure environment vars are reset after tests
The trust tests were not resetting the environment after they
ran, which could result in tests following those tests to fail.

While at it, I also updated some other tests to use gotest.tools

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-04-09 13:40:45 +02:00

584 lines
16 KiB
Go

package config
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/credentials"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/env"
)
var homeKey = "HOME"
func init() {
if runtime.GOOS == "windows" {
homeKey = "USERPROFILE"
}
}
func setupConfigDir(t *testing.T) (string, func()) {
tmpdir, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
oldDir := Dir()
SetDir(tmpdir)
return tmpdir, func() {
SetDir(oldDir)
os.RemoveAll(tmpdir)
}
}
func TestEmptyConfigDir(t *testing.T) {
tmpHome, cleanup := setupConfigDir(t)
defer cleanup()
config, err := Load("")
assert.NilError(t, err)
expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName)
assert.Check(t, is.Equal(expectedConfigFilename, config.Filename))
// Now save it and make sure it shows up in new form
saveConfigAndValidateNewFormat(t, config, tmpHome)
}
func TestMissingFile(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
config, err := Load(tmpHome)
assert.NilError(t, err)
// Now save it and make sure it shows up in new form
saveConfigAndValidateNewFormat(t, config, tmpHome)
}
func TestSaveFileToDirs(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
tmpHome += "/.docker"
config, err := Load(tmpHome)
assert.NilError(t, err)
// Now save it and make sure it shows up in new form
saveConfigAndValidateNewFormat(t, config, tmpHome)
}
func TestEmptyFile(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
err = ioutil.WriteFile(fn, []byte(""), 0600)
assert.NilError(t, err)
_, err = Load(tmpHome)
assert.Equal(t, errors.Cause(err), io.EOF)
assert.ErrorContains(t, err, ConfigFileName)
}
func TestEmptyJSON(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
err = ioutil.WriteFile(fn, []byte("{}"), 0600)
assert.NilError(t, err)
config, err := Load(tmpHome)
assert.NilError(t, err)
// Now save it and make sure it shows up in new form
saveConfigAndValidateNewFormat(t, config, tmpHome)
}
func TestOldInvalidsAuth(t *testing.T) {
invalids := map[string]string{
`username = test`: "The Auth config file is empty",
`username
password`: "Invalid Auth config file",
`username = test
email`: "Invalid auth configuration file",
}
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
defer env.Patch(t, homeKey, tmpHome)()
for content, expectedError := range invalids {
fn := filepath.Join(tmpHome, oldConfigfile)
err := ioutil.WriteFile(fn, []byte(content), 0600)
assert.NilError(t, err)
_, err = Load(tmpHome)
assert.ErrorContains(t, err, expectedError)
}
}
func TestOldValidAuth(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
defer env.Patch(t, homeKey, tmpHome)()
fn := filepath.Join(tmpHome, oldConfigfile)
js := `username = am9lam9lOmhlbGxv
email = user@example.com`
err = ioutil.WriteFile(fn, []byte(js), 0600)
assert.NilError(t, err)
config, err := Load(tmpHome)
assert.NilError(t, err)
// defaultIndexserver is https://index.docker.io/v1/
ac := config.AuthConfigs["https://index.docker.io/v1/"]
assert.Equal(t, ac.Username, "joejoe")
assert.Equal(t, ac.Password, "hello")
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
expConfStr := `{
"auths": {
"https://index.docker.io/v1/": {
"auth": "am9lam9lOmhlbGxv"
}
}
}`
assert.Check(t, is.Equal(expConfStr, configStr))
}
func TestOldJSONInvalid(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
defer env.Patch(t, homeKey, tmpHome)()
fn := filepath.Join(tmpHome, oldConfigfile)
js := `{"https://index.docker.io/v1/":{"auth":"test","email":"user@example.com"}}`
if err := ioutil.WriteFile(fn, []byte(js), 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(), "Invalid auth configuration file") {
t.Fatalf("Expected an error got : %v, %v", config, err)
}
}
func TestOldJSON(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
defer env.Patch(t, homeKey, tmpHome)()
fn := filepath.Join(tmpHome, oldConfigfile)
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
t.Fatal(err)
}
config, err := Load(tmpHome)
assert.NilError(t, err)
ac := config.AuthConfigs["https://index.docker.io/v1/"]
assert.Equal(t, ac.Username, "joejoe")
assert.Equal(t, ac.Password, "hello")
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
expConfStr := `{
"auths": {
"https://index.docker.io/v1/": {
"auth": "am9lam9lOmhlbGxv",
"email": "user@example.com"
}
}
}`
if configStr != expConfStr {
t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr)
}
}
func TestNewJSON(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
t.Fatal(err)
}
config, err := Load(tmpHome)
assert.NilError(t, err)
ac := config.AuthConfigs["https://index.docker.io/v1/"]
assert.Equal(t, ac.Username, "joejoe")
assert.Equal(t, ac.Password, "hello")
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
expConfStr := `{
"auths": {
"https://index.docker.io/v1/": {
"auth": "am9lam9lOmhlbGxv"
}
}
}`
if configStr != expConfStr {
t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
}
}
func TestNewJSONNoEmail(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
t.Fatal(err)
}
config, err := Load(tmpHome)
assert.NilError(t, err)
ac := config.AuthConfigs["https://index.docker.io/v1/"]
assert.Equal(t, ac.Username, "joejoe")
assert.Equal(t, ac.Password, "hello")
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
expConfStr := `{
"auths": {
"https://index.docker.io/v1/": {
"auth": "am9lam9lOmhlbGxv"
}
}
}`
if configStr != expConfStr {
t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
}
}
func TestJSONWithPsFormat(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
js := `{
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
}`
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
t.Fatal(err)
}
config, err := Load(tmpHome)
assert.NilError(t, err)
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
}
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
if !strings.Contains(configStr, `"psFormat":`) ||
!strings.Contains(configStr, "{{.ID}}") {
t.Fatalf("Should have save in new form: %s", configStr)
}
}
func TestJSONWithCredentialStore(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
js := `{
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
"credsStore": "crazy-secure-storage"
}`
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
t.Fatal(err)
}
config, err := Load(tmpHome)
assert.NilError(t, err)
if config.CredentialsStore != "crazy-secure-storage" {
t.Fatalf("Unknown credential store: %s\n", config.CredentialsStore)
}
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
if !strings.Contains(configStr, `"credsStore":`) ||
!strings.Contains(configStr, "crazy-secure-storage") {
t.Fatalf("Should have save in new form: %s", configStr)
}
}
func TestJSONWithCredentialHelpers(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
fn := filepath.Join(tmpHome, ConfigFileName)
js := `{
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
"credHelpers": { "images.io": "images-io", "containers.com": "crazy-secure-storage" }
}`
if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
t.Fatal(err)
}
config, err := Load(tmpHome)
assert.NilError(t, err)
if config.CredentialHelpers == nil {
t.Fatal("config.CredentialHelpers was nil")
} else if config.CredentialHelpers["images.io"] != "images-io" ||
config.CredentialHelpers["containers.com"] != "crazy-secure-storage" {
t.Fatalf("Credential helpers not deserialized properly: %v\n", config.CredentialHelpers)
}
// Now save it and make sure it shows up in new form
configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
if !strings.Contains(configStr, `"credHelpers":`) ||
!strings.Contains(configStr, "images.io") ||
!strings.Contains(configStr, "images-io") ||
!strings.Contains(configStr, "containers.com") ||
!strings.Contains(configStr, "crazy-secure-storage") {
t.Fatalf("Should have save in new form: %s", configStr)
}
}
// Save it and make sure it shows up in new form
func saveConfigAndValidateNewFormat(t *testing.T, config *configfile.ConfigFile, configDir string) string {
assert.NilError(t, config.Save())
buf, err := ioutil.ReadFile(filepath.Join(configDir, ConfigFileName))
assert.NilError(t, err)
assert.Check(t, is.Contains(string(buf), `"auths":`))
return string(buf)
}
func TestConfigDir(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err)
defer os.RemoveAll(tmpHome)
if Dir() == tmpHome {
t.Fatalf("Expected ConfigDir to be different than %s by default, but was the same", tmpHome)
}
// Update configDir
SetDir(tmpHome)
if Dir() != tmpHome {
t.Fatalf("Expected ConfigDir to %s, but was %s", tmpHome, Dir())
}
}
func TestJSONReaderNoFile(t *testing.T) {
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
config, err := LoadFromReader(strings.NewReader(js))
assert.NilError(t, err)
ac := config.AuthConfigs["https://index.docker.io/v1/"]
assert.Equal(t, ac.Username, "joejoe")
assert.Equal(t, ac.Password, "hello")
}
func TestOldJSONReaderNoFile(t *testing.T) {
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
config, err := LegacyLoadFromReader(strings.NewReader(js))
assert.NilError(t, err)
ac := config.AuthConfigs["https://index.docker.io/v1/"]
assert.Equal(t, ac.Username, "joejoe")
assert.Equal(t, ac.Password, "hello")
}
func TestJSONWithPsFormatNoFile(t *testing.T) {
js := `{
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
}`
config, err := LoadFromReader(strings.NewReader(js))
assert.NilError(t, err)
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
}
}
func TestJSONSaveWithNoFile(t *testing.T) {
js := `{
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } },
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
}`
config, err := LoadFromReader(strings.NewReader(js))
assert.NilError(t, err)
err = config.Save()
assert.ErrorContains(t, err, "with empty filename")
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(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()
assert.NilError(t, config.SaveToWriter(f))
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
assert.NilError(t, err)
expConfStr := `{
"auths": {
"https://index.docker.io/v1/": {
"auth": "am9lam9lOmhlbGxv"
}
},
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
}`
if string(buf) != expConfStr {
t.Fatalf("Should have save in new form: \n%s\nnot \n%s", string(buf), expConfStr)
}
}
func TestLegacyJSONSaveWithNoFile(t *testing.T) {
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
config, err := LegacyLoadFromReader(strings.NewReader(js))
assert.NilError(t, err)
err = config.Save()
assert.ErrorContains(t, err, "with empty filename")
tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(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()
assert.NilError(t, config.SaveToWriter(f))
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
assert.NilError(t, err)
expConfStr := `{
"auths": {
"https://index.docker.io/v1/": {
"auth": "am9lam9lOmhlbGxv",
"email": "user@example.com"
}
}
}`
if string(buf) != expConfStr {
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)
assert.NilError(t, err)
configFile := LoadDefaultConfigFile(buffer)
credStore := credentials.DetectDefaultStore("")
expected := configfile.New(filename)
expected.CredentialsStore = credStore
expected.PsFormat = "format"
assert.Check(t, is.DeepEqual(expected, configFile))
}
func TestConfigPath(t *testing.T) {
oldDir := Dir()
for _, tc := range []struct {
name string
dir string
path []string
expected string
expectedErr string
}{
{
name: "valid_path",
dir: "dummy",
path: []string{"a", "b"},
expected: filepath.Join("dummy", "a", "b"),
},
{
name: "valid_path_absolute_dir",
dir: "/dummy",
path: []string{"a", "b"},
expected: filepath.Join("/dummy", "a", "b"),
},
{
name: "invalid_relative_path",
dir: "dummy",
path: []string{"e", "..", "..", "f"},
expectedErr: fmt.Sprintf("is outside of root config directory %q", "dummy"),
},
{
name: "invalid_absolute_path",
dir: "dummy",
path: []string{"/a", "..", ".."},
expectedErr: fmt.Sprintf("is outside of root config directory %q", "dummy"),
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
SetDir(tc.dir)
f, err := Path(tc.path...)
assert.Equal(t, f, tc.expected)
if tc.expectedErr == "" {
assert.NilError(t, err)
} else {
assert.ErrorContains(t, err, tc.expectedErr)
}
})
}
SetDir(oldDir)
}