1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #12009 from duglin/AddConfig

Migrate .dockercfg to .docker/config.json and support for HTTP Headers
This commit is contained in:
Alexander Morozov 2015-04-20 13:12:17 -07:00
commit 08ef006d17
19 changed files with 360 additions and 81 deletions

View file

@ -286,10 +286,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
v.Set("dockerfile", *dockerfileName)
cli.LoadConfigFile()
headers := http.Header(make(map[string][]string))
buf, err := json.Marshal(cli.configFile)
buf, err := json.Marshal(cli.configFile.AuthConfigs)
if err != nil {
return err
}

View file

@ -9,6 +9,7 @@ import (
"net"
"net/http"
"os"
"path/filepath"
"reflect"
"strings"
"text/template"
@ -120,14 +121,6 @@ func (cli *DockerCli) Subcmd(name, signature, description string, exitOnError bo
return flags
}
func (cli *DockerCli) LoadConfigFile() (err error) {
cli.configFile, err = registry.LoadConfig(homedir.Get())
if err != nil {
fmt.Fprintf(cli.err, "WARNING: %s\n", err)
}
return err
}
func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
// In order to attach to a container tty, input stream for the client must
// be a tty itself: redirecting or piping the client standard input is
@ -184,9 +177,15 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, a
tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
}
configFile, e := registry.LoadConfig(filepath.Join(homedir.Get(), ".docker"))
if e != nil {
fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
}
return &DockerCli{
proto: proto,
addr: addr,
configFile: configFile,
in: in,
out: out,
err: err,

View file

@ -37,9 +37,6 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
return err
}
// Load the auth config file, to be able to pull the image
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
buf, err := json.Marshal(authConfig)

View file

@ -142,6 +142,13 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
if err != nil {
return err
}
// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
// then the user can't change OUR headers
for k, v := range cli.configFile.HttpHeaders {
req.Header.Set(k, v)
}
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.Header.Set("Content-Type", "text/plain")
req.Header.Set("Connection", "Upgrade")

View file

@ -68,8 +68,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
}
if info.IndexServerAddress != "" {
cli.LoadConfigFile()
u := cli.configFile.Configs[info.IndexServerAddress].Username
u := cli.configFile.AuthConfigs[info.IndexServerAddress].Username
if len(u) > 0 {
fmt.Fprintf(cli.out, "Username: %v\n", u)
fmt.Fprintf(cli.out, "Registry: %v\n", info.IndexServerAddress)

View file

@ -6,11 +6,9 @@ import (
"fmt"
"io"
"os"
"path"
"strings"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/homedir"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/term"
"github.com/docker/docker/registry"
@ -56,8 +54,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
return string(line)
}
cli.LoadConfigFile()
authconfig, ok := cli.configFile.Configs[serverAddress]
authconfig, ok := cli.configFile.AuthConfigs[serverAddress]
if !ok {
authconfig = registry.AuthConfig{}
}
@ -113,12 +110,14 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
authconfig.Password = password
authconfig.Email = email
authconfig.ServerAddress = serverAddress
cli.configFile.Configs[serverAddress] = authconfig
cli.configFile.AuthConfigs[serverAddress] = authconfig
stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], nil)
stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.AuthConfigs[serverAddress], nil)
if statusCode == 401 {
delete(cli.configFile.Configs, serverAddress)
registry.SaveConfig(cli.configFile)
delete(cli.configFile.AuthConfigs, serverAddress)
if err2 := cli.configFile.Save(); err2 != nil {
fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2)
}
return err
}
if err != nil {
@ -127,12 +126,15 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
var response types.AuthResponse
if err := json.NewDecoder(stream).Decode(&response); err != nil {
cli.configFile, _ = registry.LoadConfig(homedir.Get())
// Upon error, remove entry
delete(cli.configFile.AuthConfigs, serverAddress)
return err
}
registry.SaveConfig(cli.configFile)
fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s.\n", path.Join(homedir.Get(), registry.CONFIGFILE))
if err := cli.configFile.Save(); err != nil {
return fmt.Errorf("Error saving config file: %v", err)
}
fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s\n", cli.configFile.Filename())
if response.Status != "" {
fmt.Fprintf(cli.out, "%s\n", response.Status)

View file

@ -22,14 +22,13 @@ func (cli *DockerCli) CmdLogout(args ...string) error {
serverAddress = cmd.Arg(0)
}
cli.LoadConfigFile()
if _, ok := cli.configFile.Configs[serverAddress]; !ok {
if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
} else {
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress)
delete(cli.configFile.Configs, serverAddress)
delete(cli.configFile.AuthConfigs, serverAddress)
if err := registry.SaveConfig(cli.configFile); err != nil {
if err := cli.configFile.Save(); err != nil {
return fmt.Errorf("Failed to save docker config: %v", err)
}
}

View file

@ -42,8 +42,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
return err
}
cli.LoadConfigFile()
_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
return err
}

View file

@ -20,8 +20,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
name := cmd.Arg(0)
cli.LoadConfigFile()
remote, tag := parsers.ParseRepositoryTag(name)
// Resolve the Repository name from fqn to RepositoryInfo

View file

@ -44,8 +44,6 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
return err
}
cli.LoadConfigFile()
rdr, _, err := cli.clientRequestAttemptLogin("GET", "/images/search?"+v.Encode(), nil, nil, repoInfo.Index, "search")
if err != nil {
return err

View file

@ -65,6 +65,13 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
if err != nil {
return nil, "", -1, err
}
// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
// then the user can't change OUR headers
for k, v := range cli.configFile.HttpHeaders {
req.Header.Set(k, v)
}
req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme
@ -299,7 +306,7 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
sigchan := make(chan os.Signal, 1)
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for _ = range sigchan {
for range sigchan {
cli.resizeTty(id, isExec)
}
}()

View file

@ -102,7 +102,7 @@ type Builder struct {
disableCommit bool
AuthConfig *registry.AuthConfig
AuthConfigFile *registry.ConfigFile
ConfigFile *registry.ConfigFile
// Deprecated, original writer used for ImagePull. To be removed.
OutOld io.Writer

View file

@ -437,13 +437,13 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
}
pullRegistryAuth := b.AuthConfig
if len(b.AuthConfigFile.Configs) > 0 {
if len(b.ConfigFile.AuthConfigs) > 0 {
// The request came with a full auth config file, we prefer to use that
repoInfo, err := b.Daemon.RegistryService.ResolveRepository(remote)
if err != nil {
return nil, err
}
resolvedAuth := b.AuthConfigFile.ResolveAuthConfig(repoInfo.Index)
resolvedAuth := b.ConfigFile.ResolveAuthConfig(repoInfo.Index)
pullRegistryAuth = &resolvedAuth
}

View file

@ -150,7 +150,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
OutOld: job.Stdout,
StreamFormatter: sf,
AuthConfig: authConfig,
AuthConfigFile: configFile,
ConfigFile: configFile,
dockerfileName: dockerfileName,
cpuShares: cpuShares,
cpuSetCpus: cpuSetCpus,

View file

@ -48,6 +48,35 @@ These Go environment variables are case-insensitive. See the
[Go specification](http://golang.org/pkg/net/http/) for details on these
variables.
## Configuration Files
The Docker command line stores its configuration files in a directory called
`.docker` within your `HOME` directory. Docker manages most of the files in
`.docker` and you should not modify them. However, you *can modify* the
`.docker/config.json` file to control certain aspects of how the `docker`
command behaves.
Currently, you can modify the `docker` command behavior using environment
variables or command-line options. You can also use options within
`config.json` to modify some of the same behavior. When using these
mechanisms, you must keep in mind the order of precedence among them. Command
line options override environment variables and environment variables override
properties you specify in a `config.json` file.
The `config.json` file stores a JSON encoding of a single `HttpHeaders`
property. The property specifies a set of headers to include in all
messages sent from the Docker client to the daemon. Docker does not try to
interpret or understand these header; it simply puts them into the messages.
Docker does not allow these headers to change any headers it sets for itself.
Following is a sample `config.json` file:
{
"HttpHeaders: {
"MyHeader": "MyValue"
}
}
## Help
To list the help on any command just execute the command, followed by the `--help` option.

View file

@ -0,0 +1,58 @@
package main
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/homedir"
)
func TestConfigHttpHeader(t *testing.T) {
testRequires(t, UnixCli) // Can't set/unset HOME on windows right now
// We either need a level of Go that supports Unsetenv (for cases
// when HOME/USERPROFILE isn't set), or we need to be able to use
// os/user but user.Current() only works if we aren't statically compiling
var headers map[string][]string
server := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
headers = r.Header
}))
defer server.Close()
homeKey := homedir.Key()
homeVal := homedir.Get()
tmpDir, _ := ioutil.TempDir("", "fake-home")
defer os.RemoveAll(tmpDir)
dotDocker := filepath.Join(tmpDir, ".docker")
os.Mkdir(dotDocker, 0600)
tmpCfg := filepath.Join(dotDocker, "config.json")
defer func() { os.Setenv(homeKey, homeVal) }()
os.Setenv(homeKey, tmpDir)
data := `{
"HttpHeaders": { "MyHeader": "MyValue" }
}`
err := ioutil.WriteFile(tmpCfg, []byte(data), 0600)
if err != nil {
t.Fatalf("Err creating file(%s): %v", tmpCfg, err)
}
cmd := exec.Command(dockerBinary, "-H="+server.URL[7:], "ps")
out, _, _ := runCommandWithOutput(cmd)
if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
t.Fatalf("Missing/bad header: %q\nout:%v", headers, out)
}
logDone("config - add new http headers")
}

View file

@ -8,24 +8,27 @@ import (
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/homedir"
"github.com/docker/docker/pkg/requestdecorator"
)
const (
// Where we store the config file
CONFIGFILE = ".dockercfg"
CONFIGFILE = "config.json"
OLD_CONFIGFILE = ".dockercfg"
)
var (
ErrConfigFileMissing = errors.New("The Auth config file is missing")
)
// Registry Auth Info
type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
@ -34,9 +37,11 @@ type AuthConfig struct {
ServerAddress string `json:"serveraddress,omitempty"`
}
// ~/.docker/config.json file info
type ConfigFile struct {
Configs map[string]AuthConfig `json:"configs,omitempty"`
rootPath string
AuthConfigs map[string]AuthConfig `json:"auths"`
HttpHeaders map[string]string `json:"HttpHeaders,omitempty"`
filename string // Note: not serialized - for internal use only
}
type RequestAuthorization struct {
@ -147,18 +152,58 @@ func decodeAuth(authStr string) (string, string, error) {
// load up the auth config information and return values
// FIXME: use the internal golang config parser
func LoadConfig(rootPath string) (*ConfigFile, error) {
configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath}
confFile := path.Join(rootPath, CONFIGFILE)
func LoadConfig(configDir string) (*ConfigFile, error) {
if configDir == "" {
configDir = filepath.Join(homedir.Get(), ".docker")
}
configFile := ConfigFile{
AuthConfigs: make(map[string]AuthConfig),
filename: filepath.Join(configDir, CONFIGFILE),
}
// Try happy path first - latest config file
if _, err := os.Stat(configFile.filename); err == nil {
file, err := os.Open(configFile.filename)
if err != nil {
return &configFile, err
}
defer file.Close()
if err := json.NewDecoder(file).Decode(&configFile); err != nil {
return &configFile, err
}
for addr, ac := range configFile.AuthConfigs {
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
if err != nil {
return &configFile, err
}
ac.Auth = ""
ac.ServerAddress = addr
configFile.AuthConfigs[addr] = ac
}
return &configFile, nil
} else if !os.IsNotExist(err) {
// if file is there but we can't stat it for any reason other
// than it doesn't exist then stop
return &configFile, err
}
// Can't find latest config file so check for the old one
confFile := filepath.Join(homedir.Get(), OLD_CONFIGFILE)
if _, err := os.Stat(confFile); err != nil {
return &configFile, nil //missing file is not an error
}
b, err := ioutil.ReadFile(confFile)
if err != nil {
return &configFile, err
}
if err := json.Unmarshal(b, &configFile.Configs); err != nil {
if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
arr := strings.Split(string(b), "\n")
if len(arr) < 2 {
return &configFile, fmt.Errorf("The Auth config file is empty")
@ -179,48 +224,52 @@ func LoadConfig(rootPath string) (*ConfigFile, error) {
authConfig.Email = origEmail[1]
authConfig.ServerAddress = IndexServerAddress()
// *TODO: Switch to using IndexServerName() instead?
configFile.Configs[IndexServerAddress()] = authConfig
configFile.AuthConfigs[IndexServerAddress()] = authConfig
} else {
for k, authConfig := range configFile.Configs {
for k, authConfig := range configFile.AuthConfigs {
authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
if err != nil {
return &configFile, err
}
authConfig.Auth = ""
authConfig.ServerAddress = k
configFile.Configs[k] = authConfig
configFile.AuthConfigs[k] = authConfig
}
}
return &configFile, nil
}
// save the auth config
func SaveConfig(configFile *ConfigFile) error {
confFile := path.Join(configFile.rootPath, CONFIGFILE)
if len(configFile.Configs) == 0 {
os.Remove(confFile)
return nil
}
configs := make(map[string]AuthConfig, len(configFile.Configs))
for k, authConfig := range configFile.Configs {
func (configFile *ConfigFile) Save() error {
// Encode sensitive data into a new/temp struct
tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
for k, authConfig := range configFile.AuthConfigs {
authCopy := authConfig
authCopy.Auth = encodeAuth(&authCopy)
authCopy.Username = ""
authCopy.Password = ""
authCopy.ServerAddress = ""
configs[k] = authCopy
tmpAuthConfigs[k] = authCopy
}
b, err := json.MarshalIndent(configs, "", "\t")
saveAuthConfigs := configFile.AuthConfigs
configFile.AuthConfigs = tmpAuthConfigs
defer func() { configFile.AuthConfigs = saveAuthConfigs }()
data, err := json.MarshalIndent(configFile, "", "\t")
if err != nil {
return err
}
err = ioutil.WriteFile(confFile, b, 0600)
if err := os.MkdirAll(filepath.Dir(configFile.filename), 0600); err != nil {
return err
}
err = ioutil.WriteFile(configFile.filename, data, 0600)
if err != nil {
return err
}
return nil
}
@ -431,7 +480,7 @@ func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, regis
func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
configKey := index.GetAuthConfigKey()
// First try the happy case
if c, found := config.Configs[configKey]; found || index.Official {
if c, found := config.AuthConfigs[configKey]; found || index.Official {
return c
}
@ -450,7 +499,7 @@ func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
// Maybe they have a legacy config file, we will iterate the keys converting
// them to the new format and testing
for registry, config := range config.Configs {
for registry, config := range config.AuthConfigs {
if configKey == convertToHostname(registry) {
return config
}
@ -459,3 +508,7 @@ func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
// When all else fails, return an empty auth config
return AuthConfig{}
}
func (config *ConfigFile) Filename() string {
return config.filename
}

View file

@ -3,6 +3,7 @@ package registry
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
@ -31,13 +32,14 @@ func setupTempConfigFile() (*ConfigFile, error) {
if err != nil {
return nil, err
}
root = filepath.Join(root, CONFIGFILE)
configFile := &ConfigFile{
rootPath: root,
Configs: make(map[string]AuthConfig),
AuthConfigs: make(map[string]AuthConfig),
filename: root,
}
for _, registry := range []string{"testIndex", IndexServerAddress()} {
configFile.Configs[registry] = AuthConfig{
configFile.AuthConfigs[registry] = AuthConfig{
Username: "docker-user",
Password: "docker-pass",
Email: "docker@docker.io",
@ -52,14 +54,14 @@ func TestSameAuthDataPostSave(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(configFile.rootPath)
defer os.RemoveAll(configFile.filename)
err = SaveConfig(configFile)
err = configFile.Save()
if err != nil {
t.Fatal(err)
}
authConfig := configFile.Configs["testIndex"]
authConfig := configFile.AuthConfigs["testIndex"]
if authConfig.Username != "docker-user" {
t.Fail()
}
@ -79,9 +81,9 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(configFile.rootPath)
defer os.RemoveAll(configFile.filename)
indexConfig := configFile.Configs[IndexServerAddress()]
indexConfig := configFile.AuthConfigs[IndexServerAddress()]
officialIndex := &IndexInfo{
Official: true,
@ -102,7 +104,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(configFile.rootPath)
defer os.RemoveAll(configFile.filename)
registryAuth := AuthConfig{
Username: "foo-user",
@ -119,7 +121,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
Password: "baz-pass",
Email: "baz@example.com",
}
configFile.Configs[IndexServerAddress()] = officialAuth
configFile.AuthConfigs[IndexServerAddress()] = officialAuth
expectedAuths := map[string]AuthConfig{
"registry.example.com": registryAuth,
@ -157,12 +159,12 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
Name: configKey,
}
for _, registry := range registries {
configFile.Configs[registry] = configured
configFile.AuthConfigs[registry] = configured
resolved := configFile.ResolveAuthConfig(index)
if resolved.Email != configured.Email {
t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
}
delete(configFile.Configs, registry)
delete(configFile.AuthConfigs, registry)
resolved = configFile.ResolveAuthConfig(index)
if resolved.Email == configured.Email {
t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)

View file

@ -0,0 +1,135 @@
package registry
import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/docker/docker/pkg/homedir"
)
func TestMissingFile(t *testing.T) {
tmpHome, _ := ioutil.TempDir("", "config-test")
config, err := LoadConfig(tmpHome)
if err != nil {
t.Fatalf("Failed loading on missing file: %q", err)
}
// Now save it and make sure it shows up in new form
err = config.Save()
if err != nil {
t.Fatalf("Failed to save: %q", err)
}
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
if !strings.Contains(string(buf), `"auths":`) {
t.Fatalf("Should have save in new form: %s", string(buf))
}
}
func TestEmptyFile(t *testing.T) {
tmpHome, _ := ioutil.TempDir("", "config-test")
fn := filepath.Join(tmpHome, CONFIGFILE)
ioutil.WriteFile(fn, []byte(""), 0600)
_, err := LoadConfig(tmpHome)
if err == nil {
t.Fatalf("Was supposed to fail")
}
}
func TestEmptyJson(t *testing.T) {
tmpHome, _ := ioutil.TempDir("", "config-test")
fn := filepath.Join(tmpHome, CONFIGFILE)
ioutil.WriteFile(fn, []byte("{}"), 0600)
config, err := LoadConfig(tmpHome)
if err != nil {
t.Fatalf("Failed loading on empty json file: %q", err)
}
// Now save it and make sure it shows up in new form
err = config.Save()
if err != nil {
t.Fatalf("Failed to save: %q", err)
}
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
if !strings.Contains(string(buf), `"auths":`) {
t.Fatalf("Should have save in new form: %s", string(buf))
}
}
func TestOldJson(t *testing.T) {
if runtime.GOOS == "windows" {
return
}
tmpHome, _ := ioutil.TempDir("", "config-test")
defer os.RemoveAll(tmpHome)
homeKey := homedir.Key()
homeVal := homedir.Get()
defer func() { os.Setenv(homeKey, homeVal) }()
os.Setenv(homeKey, tmpHome)
fn := filepath.Join(tmpHome, OLD_CONFIGFILE)
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
ioutil.WriteFile(fn, []byte(js), 0600)
config, err := LoadConfig(tmpHome)
if err != nil {
t.Fatalf("Failed loading on empty json file: %q", err)
}
ac := config.AuthConfigs["https://index.docker.io/v1/"]
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
t.Fatalf("Missing data from parsing:\n%q", config)
}
// Now save it and make sure it shows up in new form
err = config.Save()
if err != nil {
t.Fatalf("Failed to save: %q", err)
}
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
if !strings.Contains(string(buf), `"auths":`) ||
!strings.Contains(string(buf), "user@example.com") {
t.Fatalf("Should have save in new form: %s", string(buf))
}
}
func TestNewJson(t *testing.T) {
tmpHome, _ := ioutil.TempDir("", "config-test")
fn := filepath.Join(tmpHome, CONFIGFILE)
js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
ioutil.WriteFile(fn, []byte(js), 0600)
config, err := LoadConfig(tmpHome)
if err != nil {
t.Fatalf("Failed loading on empty json file: %q", err)
}
ac := config.AuthConfigs["https://index.docker.io/v1/"]
if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
t.Fatalf("Missing data from parsing:\n%q", config)
}
// Now save it and make sure it shows up in new form
err = config.Save()
if err != nil {
t.Fatalf("Failed to save: %q", err)
}
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE))
if !strings.Contains(string(buf), `"auths":`) ||
!strings.Contains(string(buf), "user@example.com") {
t.Fatalf("Should have save in new form: %s", string(buf))
}
}