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) v.Set("dockerfile", *dockerfileName)
cli.LoadConfigFile()
headers := http.Header(make(map[string][]string)) headers := http.Header(make(map[string][]string))
buf, err := json.Marshal(cli.configFile) buf, err := json.Marshal(cli.configFile.AuthConfigs)
if err != nil { if err != nil {
return err return err
} }

View file

@ -9,6 +9,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"path/filepath"
"reflect" "reflect"
"strings" "strings"
"text/template" "text/template"
@ -120,14 +121,6 @@ func (cli *DockerCli) Subcmd(name, signature, description string, exitOnError bo
return flags 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 { func (cli *DockerCli) CheckTtyInput(attachStdin, ttyMode bool) error {
// In order to attach to a container tty, input stream for the client must // 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 // 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 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{ return &DockerCli{
proto: proto, proto: proto,
addr: addr, addr: addr,
configFile: configFile,
in: in, in: in,
out: out, out: out,
err: err, err: err,

View file

@ -37,9 +37,6 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
return err return err
} }
// Load the auth config file, to be able to pull the image
cli.LoadConfigFile()
// Resolve the Auth config relevant for this server // Resolve the Auth config relevant for this server
authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index) authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
buf, err := json.Marshal(authConfig) 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 { if err != nil {
return err 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("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.Header.Set("Content-Type", "text/plain") req.Header.Set("Content-Type", "text/plain")
req.Header.Set("Connection", "Upgrade") req.Header.Set("Connection", "Upgrade")

View file

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

View file

@ -6,11 +6,9 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path"
"strings" "strings"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/homedir"
flag "github.com/docker/docker/pkg/mflag" flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/term" "github.com/docker/docker/pkg/term"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
@ -56,8 +54,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
return string(line) return string(line)
} }
cli.LoadConfigFile() authconfig, ok := cli.configFile.AuthConfigs[serverAddress]
authconfig, ok := cli.configFile.Configs[serverAddress]
if !ok { if !ok {
authconfig = registry.AuthConfig{} authconfig = registry.AuthConfig{}
} }
@ -113,12 +110,14 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
authconfig.Password = password authconfig.Password = password
authconfig.Email = email authconfig.Email = email
authconfig.ServerAddress = serverAddress 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 { if statusCode == 401 {
delete(cli.configFile.Configs, serverAddress) delete(cli.configFile.AuthConfigs, serverAddress)
registry.SaveConfig(cli.configFile) if err2 := cli.configFile.Save(); err2 != nil {
fmt.Fprintf(cli.out, "WARNING: could not save config file: %v\n", err2)
}
return err return err
} }
if err != nil { if err != nil {
@ -127,12 +126,15 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
var response types.AuthResponse var response types.AuthResponse
if err := json.NewDecoder(stream).Decode(&response); err != nil { 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 return err
} }
registry.SaveConfig(cli.configFile) if err := cli.configFile.Save(); err != nil {
fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s.\n", path.Join(homedir.Get(), registry.CONFIGFILE)) 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 != "" { if response.Status != "" {
fmt.Fprintf(cli.out, "%s\n", 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) serverAddress = cmd.Arg(0)
} }
cli.LoadConfigFile() if _, ok := cli.configFile.AuthConfigs[serverAddress]; !ok {
if _, ok := cli.configFile.Configs[serverAddress]; !ok {
fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress) fmt.Fprintf(cli.out, "Not logged in to %s\n", serverAddress)
} else { } else {
fmt.Fprintf(cli.out, "Remove login credentials for %s\n", serverAddress) 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) 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 return err
} }
cli.LoadConfigFile()
_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull") _, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
return err return err
} }

View file

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

View file

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

View file

@ -65,6 +65,13 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
if err != nil { if err != nil {
return nil, "", -1, err 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.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
req.URL.Host = cli.addr req.URL.Host = cli.addr
req.URL.Scheme = cli.scheme req.URL.Scheme = cli.scheme
@ -299,7 +306,7 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
sigchan := make(chan os.Signal, 1) sigchan := make(chan os.Signal, 1)
gosignal.Notify(sigchan, signal.SIGWINCH) gosignal.Notify(sigchan, signal.SIGWINCH)
go func() { go func() {
for _ = range sigchan { for range sigchan {
cli.resizeTty(id, isExec) cli.resizeTty(id, isExec)
} }
}() }()

View file

@ -101,8 +101,8 @@ type Builder struct {
// the final configs of the Dockerfile but dont want the layers // the final configs of the Dockerfile but dont want the layers
disableCommit bool disableCommit bool
AuthConfig *registry.AuthConfig AuthConfig *registry.AuthConfig
AuthConfigFile *registry.ConfigFile ConfigFile *registry.ConfigFile
// Deprecated, original writer used for ImagePull. To be removed. // Deprecated, original writer used for ImagePull. To be removed.
OutOld io.Writer OutOld io.Writer

View file

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

View file

@ -150,7 +150,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
OutOld: job.Stdout, OutOld: job.Stdout,
StreamFormatter: sf, StreamFormatter: sf,
AuthConfig: authConfig, AuthConfig: authConfig,
AuthConfigFile: configFile, ConfigFile: configFile,
dockerfileName: dockerfileName, dockerfileName: dockerfileName,
cpuShares: cpuShares, cpuShares: cpuShares,
cpuSetCpus: cpuSetCpus, 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 [Go specification](http://golang.org/pkg/net/http/) for details on these
variables. 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 ## Help
To list the help on any command just execute the command, followed by the `--help` option. 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" "io/ioutil"
"net/http" "net/http"
"os" "os"
"path" "path/filepath"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/homedir"
"github.com/docker/docker/pkg/requestdecorator" "github.com/docker/docker/pkg/requestdecorator"
) )
const ( const (
// Where we store the config file // Where we store the config file
CONFIGFILE = ".dockercfg" CONFIGFILE = "config.json"
OLD_CONFIGFILE = ".dockercfg"
) )
var ( var (
ErrConfigFileMissing = errors.New("The Auth config file is missing") ErrConfigFileMissing = errors.New("The Auth config file is missing")
) )
// Registry Auth Info
type AuthConfig struct { type AuthConfig struct {
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
@ -34,9 +37,11 @@ type AuthConfig struct {
ServerAddress string `json:"serveraddress,omitempty"` ServerAddress string `json:"serveraddress,omitempty"`
} }
// ~/.docker/config.json file info
type ConfigFile struct { type ConfigFile struct {
Configs map[string]AuthConfig `json:"configs,omitempty"` AuthConfigs map[string]AuthConfig `json:"auths"`
rootPath string HttpHeaders map[string]string `json:"HttpHeaders,omitempty"`
filename string // Note: not serialized - for internal use only
} }
type RequestAuthorization struct { type RequestAuthorization struct {
@ -147,18 +152,58 @@ func decodeAuth(authStr string) (string, string, error) {
// load up the auth config information and return values // load up the auth config information and return values
// FIXME: use the internal golang config parser // FIXME: use the internal golang config parser
func LoadConfig(rootPath string) (*ConfigFile, error) { func LoadConfig(configDir string) (*ConfigFile, error) {
configFile := ConfigFile{Configs: make(map[string]AuthConfig), rootPath: rootPath} if configDir == "" {
confFile := path.Join(rootPath, CONFIGFILE) 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 { 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
} }
b, err := ioutil.ReadFile(confFile) b, err := ioutil.ReadFile(confFile)
if err != nil { if err != nil {
return &configFile, err 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") arr := strings.Split(string(b), "\n")
if len(arr) < 2 { if len(arr) < 2 {
return &configFile, fmt.Errorf("The Auth config file is empty") 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.Email = origEmail[1]
authConfig.ServerAddress = IndexServerAddress() authConfig.ServerAddress = IndexServerAddress()
// *TODO: Switch to using IndexServerName() instead? // *TODO: Switch to using IndexServerName() instead?
configFile.Configs[IndexServerAddress()] = authConfig configFile.AuthConfigs[IndexServerAddress()] = authConfig
} else { } else {
for k, authConfig := range configFile.Configs { for k, authConfig := range configFile.AuthConfigs {
authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
if err != nil { if err != nil {
return &configFile, err return &configFile, err
} }
authConfig.Auth = "" authConfig.Auth = ""
authConfig.ServerAddress = k authConfig.ServerAddress = k
configFile.Configs[k] = authConfig configFile.AuthConfigs[k] = authConfig
} }
} }
return &configFile, nil return &configFile, nil
} }
// save the auth config func (configFile *ConfigFile) Save() error {
func SaveConfig(configFile *ConfigFile) error { // Encode sensitive data into a new/temp struct
confFile := path.Join(configFile.rootPath, CONFIGFILE) tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs))
if len(configFile.Configs) == 0 { for k, authConfig := range configFile.AuthConfigs {
os.Remove(confFile)
return nil
}
configs := make(map[string]AuthConfig, len(configFile.Configs))
for k, authConfig := range configFile.Configs {
authCopy := authConfig authCopy := authConfig
authCopy.Auth = encodeAuth(&authCopy) authCopy.Auth = encodeAuth(&authCopy)
authCopy.Username = "" authCopy.Username = ""
authCopy.Password = "" authCopy.Password = ""
authCopy.ServerAddress = "" 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 { if err != nil {
return err 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 { if err != nil {
return err return err
} }
return nil return nil
} }
@ -431,7 +480,7 @@ func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, regis
func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig { func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
configKey := index.GetAuthConfigKey() configKey := index.GetAuthConfigKey()
// First try the happy case // 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 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 // Maybe they have a legacy config file, we will iterate the keys converting
// them to the new format and testing // them to the new format and testing
for registry, config := range config.Configs { for registry, config := range config.AuthConfigs {
if configKey == convertToHostname(registry) { if configKey == convertToHostname(registry) {
return config return config
} }
@ -459,3 +508,7 @@ func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
// When all else fails, return an empty auth config // When all else fails, return an empty auth config
return AuthConfig{} return AuthConfig{}
} }
func (config *ConfigFile) Filename() string {
return config.filename
}

View file

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