Browse Source

Move CLI config processing out from under registry dir

No logic changes should be in here, just moving things around.

Signed-off-by: Doug Davis <dug@us.ibm.com>
Doug Davis 10 years ago
parent
commit
bb9da6ba92

+ 5 - 4
api/client/cli.go

@@ -15,10 +15,10 @@ import (
 	"text/template"
 	"time"
 
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/pkg/homedir"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/term"
-	"github.com/docker/docker/registry"
 )
 
 // DockerCli represents the docker command line client.
@@ -28,8 +28,9 @@ type DockerCli struct {
 	proto string
 	// addr holds the client address.
 	addr string
-	// configFile holds the configuration file (instance of registry.ConfigFile).
-	configFile *registry.ConfigFile
+
+	// configFile has the client configuration file
+	configFile *cliconfig.ConfigFile
 	// in holds the input stream and closer (io.ReadCloser) for the client.
 	in io.ReadCloser
 	// out holds the output stream (io.Writer) for the client.
@@ -184,7 +185,7 @@ 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"))
+	configFile, e := cliconfig.Load(filepath.Join(homedir.Get(), ".docker"))
 	if e != nil {
 		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
 	}

+ 1 - 1
api/client/create.go

@@ -38,7 +38,7 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error {
 	}
 
 	// Resolve the Auth config relevant for this server
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
+	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
 	buf, err := json.Marshal(authConfig)
 	if err != nil {
 		return err

+ 2 - 1
api/client/login.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/cliconfig"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/registry"
@@ -56,7 +57,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 
 	authconfig, ok := cli.configFile.AuthConfigs[serverAddress]
 	if !ok {
-		authconfig = registry.AuthConfig{}
+		authconfig = cliconfig.AuthConfig{}
 	}
 
 	if username == "" {

+ 1 - 1
api/client/push.go

@@ -28,7 +28,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
 		return err
 	}
 	// Resolve the Auth config relevant for this server
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
+	authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index)
 	// If we're not using a custom registry, we know the restrictions
 	// applied to repository names and can warn the user in advance.
 	// Custom repositories can have different rules, and we must also

+ 4 - 3
api/client/utils.go

@@ -21,6 +21,7 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/autogen/dockerversion"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/engine"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/signal"
@@ -119,7 +120,7 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
 }
 
 func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
-	cmdAttempt := func(authConfig registry.AuthConfig) (io.ReadCloser, int, error) {
+	cmdAttempt := func(authConfig cliconfig.AuthConfig) (io.ReadCloser, int, error) {
 		buf, err := json.Marshal(authConfig)
 		if err != nil {
 			return nil, -1, err
@@ -150,14 +151,14 @@ func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reade
 	}
 
 	// Resolve the Auth config relevant for this server
-	authConfig := cli.configFile.ResolveAuthConfig(index)
+	authConfig := registry.ResolveAuthConfig(cli.configFile, index)
 	body, statusCode, err := cmdAttempt(authConfig)
 	if statusCode == http.StatusUnauthorized {
 		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
 		if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
 			return nil, -1, err
 		}
-		authConfig = cli.configFile.ResolveAuthConfig(index)
+		authConfig = registry.ResolveAuthConfig(cli.configFile, index)
 		return cmdAttempt(authConfig)
 	}
 	return body, statusCode, err

+ 12 - 12
api/server/server.go

@@ -22,6 +22,7 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/builder"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/engine"
@@ -34,7 +35,6 @@ import (
 	"github.com/docker/docker/pkg/stdcopy"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/version"
-	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 )
@@ -239,7 +239,7 @@ func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) {
 }
 
 func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-	var config *registry.AuthConfig
+	var config *cliconfig.AuthConfig
 	err := json.NewDecoder(r.Body).Decode(&config)
 	r.Body.Close()
 	if err != nil {
@@ -728,13 +728,13 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w
 		tag   = r.Form.Get("tag")
 	)
 	authEncoded := r.Header.Get("X-Registry-Auth")
-	authConfig := &registry.AuthConfig{}
+	authConfig := &cliconfig.AuthConfig{}
 	if authEncoded != "" {
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 			// for a pull it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
-			authConfig = &registry.AuthConfig{}
+			authConfig = &cliconfig.AuthConfig{}
 		}
 	}
 
@@ -802,7 +802,7 @@ func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w
 		return err
 	}
 	var (
-		config      *registry.AuthConfig
+		config      *cliconfig.AuthConfig
 		authEncoded = r.Header.Get("X-Registry-Auth")
 		headers     = map[string][]string{}
 	)
@@ -812,7 +812,7 @@ func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w
 		if err := json.NewDecoder(authJson).Decode(&config); err != nil {
 			// for a search it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
-			config = &registry.AuthConfig{}
+			config = &cliconfig.AuthConfig{}
 		}
 	}
 	for k, v := range r.Header {
@@ -841,7 +841,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
 	if err := parseForm(r); err != nil {
 		return err
 	}
-	authConfig := &registry.AuthConfig{}
+	authConfig := &cliconfig.AuthConfig{}
 
 	authEncoded := r.Header.Get("X-Registry-Auth")
 	if authEncoded != "" {
@@ -849,7 +849,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 			// to increase compatibility to existing api it is defaulting to be empty
-			authConfig = &registry.AuthConfig{}
+			authConfig = &cliconfig.AuthConfig{}
 		}
 	} else {
 		// the old format is supported for compatibility if there was no authConfig header
@@ -1263,9 +1263,9 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
 	}
 	var (
 		authEncoded       = r.Header.Get("X-Registry-Auth")
-		authConfig        = &registry.AuthConfig{}
+		authConfig        = &cliconfig.AuthConfig{}
 		configFileEncoded = r.Header.Get("X-Registry-Config")
-		configFile        = &registry.ConfigFile{}
+		configFile        = &cliconfig.ConfigFile{}
 		buildConfig       = builder.NewBuildConfig()
 	)
 
@@ -1278,7 +1278,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 			// for a pull it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
-			authConfig = &registry.AuthConfig{}
+			authConfig = &cliconfig.AuthConfig{}
 		}
 	}
 
@@ -1287,7 +1287,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
 		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
 			// for a pull it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
-			configFile = &registry.ConfigFile{}
+			configFile = &cliconfig.ConfigFile{}
 		}
 	}
 

+ 3 - 3
builder/evaluator.go

@@ -30,13 +30,13 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/builder/command"
 	"github.com/docker/docker/builder/parser"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/tarsum"
-	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 )
@@ -99,8 +99,8 @@ type Builder struct {
 	// the final configs of the Dockerfile but dont want the layers
 	disableCommit bool
 
-	AuthConfig *registry.AuthConfig
-	ConfigFile *registry.ConfigFile
+	AuthConfig *cliconfig.AuthConfig
+	ConfigFile *cliconfig.ConfigFile
 
 	// Deprecated, original writer used for ImagePull. To be removed.
 	OutOld          io.Writer

+ 2 - 1
builder/internals.go

@@ -36,6 +36,7 @@ import (
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/tarsum"
 	"github.com/docker/docker/pkg/urlutil"
+	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 )
 
@@ -443,7 +444,7 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
 		if err != nil {
 			return nil, err
 		}
-		resolvedAuth := b.ConfigFile.ResolveAuthConfig(repoInfo.Index)
+		resolvedAuth := registry.ResolveAuthConfig(b.ConfigFile, repoInfo.Index)
 		pullRegistryAuth = &resolvedAuth
 	}
 

+ 5 - 4
builder/job.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/builder/parser"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/graph"
 	"github.com/docker/docker/pkg/archive"
@@ -50,8 +51,8 @@ type Config struct {
 	CpuShares      int64
 	CpuSetCpus     string
 	CpuSetMems     string
-	AuthConfig     *registry.AuthConfig
-	ConfigFile     *registry.ConfigFile
+	AuthConfig     *cliconfig.AuthConfig
+	ConfigFile     *cliconfig.ConfigFile
 
 	Stdout  io.Writer
 	Context io.ReadCloser
@@ -76,8 +77,8 @@ func (b *Config) WaitCancelled() <-chan struct{} {
 
 func NewBuildConfig() *Config {
 	return &Config{
-		AuthConfig: &registry.AuthConfig{},
-		ConfigFile: &registry.ConfigFile{},
+		AuthConfig: &cliconfig.AuthConfig{},
+		ConfigFile: &cliconfig.ConfigFile{},
 		cancelled:  make(chan struct{}),
 	}
 }

+ 208 - 0
cliconfig/config.go

@@ -0,0 +1,208 @@
+package cliconfig
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/docker/docker/pkg/homedir"
+)
+
+const (
+	// Where we store the config file
+	CONFIGFILE     = "config.json"
+	OLD_CONFIGFILE = ".dockercfg"
+
+	// This constant is only used for really old config files when the
+	// URL wasn't saved as part of the config file and it was just
+	// assumed to be this value.
+	DEFAULT_INDEXSERVER = "https://index.docker.io/v1/"
+)
+
+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"`
+	Auth          string `json:"auth"`
+	Email         string `json:"email"`
+	ServerAddress string `json:"serveraddress,omitempty"`
+}
+
+// ~/.docker/config.json file info
+type ConfigFile struct {
+	AuthConfigs map[string]AuthConfig `json:"auths"`
+	HttpHeaders map[string]string     `json:"HttpHeaders,omitempty"`
+	filename    string                // Note: not serialized - for internal use only
+}
+
+func NewConfigFile(fn string) *ConfigFile {
+	return &ConfigFile{
+		AuthConfigs: make(map[string]AuthConfig),
+		HttpHeaders: make(map[string]string),
+		filename:    fn,
+	}
+}
+
+// load up the auth config information and return values
+// FIXME: use the internal golang config parser
+func Load(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.AuthConfigs); err != nil {
+		arr := strings.Split(string(b), "\n")
+		if len(arr) < 2 {
+			return &configFile, fmt.Errorf("The Auth config file is empty")
+		}
+		authConfig := AuthConfig{}
+		origAuth := strings.Split(arr[0], " = ")
+		if len(origAuth) != 2 {
+			return &configFile, fmt.Errorf("Invalid Auth config file")
+		}
+		authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1])
+		if err != nil {
+			return &configFile, err
+		}
+		origEmail := strings.Split(arr[1], " = ")
+		if len(origEmail) != 2 {
+			return &configFile, fmt.Errorf("Invalid Auth config file")
+		}
+		authConfig.Email = origEmail[1]
+		authConfig.ServerAddress = DEFAULT_INDEXSERVER
+		configFile.AuthConfigs[DEFAULT_INDEXSERVER] = authConfig
+	} else {
+		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.AuthConfigs[k] = authConfig
+		}
+	}
+	return &configFile, nil
+}
+
+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 = ""
+		tmpAuthConfigs[k] = authCopy
+	}
+
+	saveAuthConfigs := configFile.AuthConfigs
+	configFile.AuthConfigs = tmpAuthConfigs
+	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
+
+	data, err := json.MarshalIndent(configFile, "", "\t")
+	if err != nil {
+		return err
+	}
+
+	if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
+		return err
+	}
+
+	err = ioutil.WriteFile(configFile.filename, data, 0600)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (config *ConfigFile) Filename() string {
+	return config.filename
+}
+
+// create a base64 encoded auth string to store in config
+func EncodeAuth(authConfig *AuthConfig) string {
+	authStr := authConfig.Username + ":" + authConfig.Password
+	msg := []byte(authStr)
+	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
+	base64.StdEncoding.Encode(encoded, msg)
+	return string(encoded)
+}
+
+// decode the auth string
+func DecodeAuth(authStr string) (string, string, error) {
+	decLen := base64.StdEncoding.DecodedLen(len(authStr))
+	decoded := make([]byte, decLen)
+	authByte := []byte(authStr)
+	n, err := base64.StdEncoding.Decode(decoded, authByte)
+	if err != nil {
+		return "", "", err
+	}
+	if n > decLen {
+		return "", "", fmt.Errorf("Something went wrong decoding auth config")
+	}
+	arr := strings.SplitN(string(decoded), ":", 2)
+	if len(arr) != 2 {
+		return "", "", fmt.Errorf("Invalid auth configuration file")
+	}
+	password := strings.Trim(arr[1], "\x00")
+	return arr[0], password, nil
+}

+ 7 - 7
registry/config_file_test.go → cliconfig/config_file_test.go

@@ -1,4 +1,4 @@
-package registry
+package cliconfig
 
 import (
 	"io/ioutil"
@@ -14,7 +14,7 @@ import (
 func TestMissingFile(t *testing.T) {
 	tmpHome, _ := ioutil.TempDir("", "config-test")
 
-	config, err := LoadConfig(tmpHome)
+	config, err := Load(tmpHome)
 	if err != nil {
 		t.Fatalf("Failed loading on missing file: %q", err)
 	}
@@ -36,7 +36,7 @@ func TestSaveFileToDirs(t *testing.T) {
 
 	tmpHome += "/.docker"
 
-	config, err := LoadConfig(tmpHome)
+	config, err := Load(tmpHome)
 	if err != nil {
 		t.Fatalf("Failed loading on missing file: %q", err)
 	}
@@ -58,7 +58,7 @@ func TestEmptyFile(t *testing.T) {
 	fn := filepath.Join(tmpHome, CONFIGFILE)
 	ioutil.WriteFile(fn, []byte(""), 0600)
 
-	_, err := LoadConfig(tmpHome)
+	_, err := Load(tmpHome)
 	if err == nil {
 		t.Fatalf("Was supposed to fail")
 	}
@@ -69,7 +69,7 @@ func TestEmptyJson(t *testing.T) {
 	fn := filepath.Join(tmpHome, CONFIGFILE)
 	ioutil.WriteFile(fn, []byte("{}"), 0600)
 
-	config, err := LoadConfig(tmpHome)
+	config, err := Load(tmpHome)
 	if err != nil {
 		t.Fatalf("Failed loading on empty json file: %q", err)
 	}
@@ -104,7 +104,7 @@ func TestOldJson(t *testing.T) {
 	js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
 	ioutil.WriteFile(fn, []byte(js), 0600)
 
-	config, err := LoadConfig(tmpHome)
+	config, err := Load(tmpHome)
 	if err != nil {
 		t.Fatalf("Failed loading on empty json file: %q", err)
 	}
@@ -133,7 +133,7 @@ func TestNewJson(t *testing.T) {
 	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
 	ioutil.WriteFile(fn, []byte(js), 0600)
 
-	config, err := LoadConfig(tmpHome)
+	config, err := Load(tmpHome)
 	if err != nil {
 		t.Fatalf("Failed loading on empty json file: %q", err)
 	}

+ 2 - 1
graph/pull.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/digest"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
@@ -23,7 +24,7 @@ import (
 type ImagePullConfig struct {
 	Parallel    bool
 	MetaHeaders map[string][]string
-	AuthConfig  *registry.AuthConfig
+	AuthConfig  *cliconfig.AuthConfig
 	Json        bool
 	OutStream   io.Writer
 }

+ 2 - 1
graph/push.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/digest"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/progressreader"
 	"github.com/docker/docker/pkg/streamformatter"
@@ -26,7 +27,7 @@ var ErrV2RegistryUnavailable = errors.New("error v2 registry unavailable")
 
 type ImagePushConfig struct {
 	MetaHeaders map[string][]string
-	AuthConfig  *registry.AuthConfig
+	AuthConfig  *cliconfig.AuthConfig
 	Tag         string
 	Json        bool
 	OutStream   io.Writer

+ 2 - 2
integration/runtime_test.go

@@ -18,6 +18,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	apiserver "github.com/docker/docker/api/server"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/engine"
@@ -28,7 +29,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 )
@@ -135,7 +135,7 @@ func setupBaseImage() {
 		imagePullConfig := &graph.ImagePullConfig{
 			Parallel:   true,
 			OutStream:  ioutils.NopWriteCloser(os.Stdout),
-			AuthConfig: &registry.AuthConfig{},
+			AuthConfig: &cliconfig.AuthConfig{},
 		}
 		d := getDaemon(eng)
 		if err := d.Repositories().Pull(unitTestImageName, "", imagePullConfig); err != nil {

+ 12 - 198
registry/auth.go

@@ -1,51 +1,21 @@
 package registry
 
 import (
-	"encoding/base64"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io/ioutil"
 	"net/http"
-	"os"
-	"path/filepath"
 	"strings"
 	"sync"
 	"time"
 
 	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/pkg/homedir"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/pkg/requestdecorator"
 )
 
-const (
-	// Where we store the config file
-	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"`
-	Auth          string `json:"auth"`
-	Email         string `json:"email"`
-	ServerAddress string `json:"serveraddress,omitempty"`
-}
-
-// ~/.docker/config.json file info
-type ConfigFile struct {
-	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 {
-	authConfig       *AuthConfig
+	authConfig       *cliconfig.AuthConfig
 	registryEndpoint *Endpoint
 	resource         string
 	scope            string
@@ -56,7 +26,7 @@ type RequestAuthorization struct {
 	tokenExpiration time.Time
 }
 
-func NewRequestAuthorization(authConfig *AuthConfig, registryEndpoint *Endpoint, resource, scope string, actions []string) *RequestAuthorization {
+func NewRequestAuthorization(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, resource, scope string, actions []string) *RequestAuthorization {
 	return &RequestAuthorization{
 		authConfig:       authConfig,
 		registryEndpoint: registryEndpoint,
@@ -121,160 +91,8 @@ func (auth *RequestAuthorization) Authorize(req *http.Request) error {
 	return nil
 }
 
-// create a base64 encoded auth string to store in config
-func encodeAuth(authConfig *AuthConfig) string {
-	authStr := authConfig.Username + ":" + authConfig.Password
-	msg := []byte(authStr)
-	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
-	base64.StdEncoding.Encode(encoded, msg)
-	return string(encoded)
-}
-
-// decode the auth string
-func decodeAuth(authStr string) (string, string, error) {
-	decLen := base64.StdEncoding.DecodedLen(len(authStr))
-	decoded := make([]byte, decLen)
-	authByte := []byte(authStr)
-	n, err := base64.StdEncoding.Decode(decoded, authByte)
-	if err != nil {
-		return "", "", err
-	}
-	if n > decLen {
-		return "", "", fmt.Errorf("Something went wrong decoding auth config")
-	}
-	arr := strings.SplitN(string(decoded), ":", 2)
-	if len(arr) != 2 {
-		return "", "", fmt.Errorf("Invalid auth configuration file")
-	}
-	password := strings.Trim(arr[1], "\x00")
-	return arr[0], password, nil
-}
-
-// load up the auth config information and return values
-// FIXME: use the internal golang config parser
-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.AuthConfigs); err != nil {
-		arr := strings.Split(string(b), "\n")
-		if len(arr) < 2 {
-			return &configFile, fmt.Errorf("The Auth config file is empty")
-		}
-		authConfig := AuthConfig{}
-		origAuth := strings.Split(arr[0], " = ")
-		if len(origAuth) != 2 {
-			return &configFile, fmt.Errorf("Invalid Auth config file")
-		}
-		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
-		if err != nil {
-			return &configFile, err
-		}
-		origEmail := strings.Split(arr[1], " = ")
-		if len(origEmail) != 2 {
-			return &configFile, fmt.Errorf("Invalid Auth config file")
-		}
-		authConfig.Email = origEmail[1]
-		authConfig.ServerAddress = IndexServerAddress()
-		// *TODO: Switch to using IndexServerName() instead?
-		configFile.AuthConfigs[IndexServerAddress()] = authConfig
-	} else {
-		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.AuthConfigs[k] = authConfig
-		}
-	}
-	return &configFile, nil
-}
-
-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 = ""
-		tmpAuthConfigs[k] = authCopy
-	}
-
-	saveAuthConfigs := configFile.AuthConfigs
-	configFile.AuthConfigs = tmpAuthConfigs
-	defer func() { configFile.AuthConfigs = saveAuthConfigs }()
-
-	data, err := json.MarshalIndent(configFile, "", "\t")
-	if err != nil {
-		return err
-	}
-
-	if err := os.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil {
-		return err
-	}
-
-	err = ioutil.WriteFile(configFile.filename, data, 0600)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
 // Login tries to register/login to the registry server.
-func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
+func Login(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
 	// Separates the v2 registry login logic from the v1 logic.
 	if registryEndpoint.Version == APIVersion2 {
 		return loginV2(authConfig, registryEndpoint, factory)
@@ -283,7 +101,7 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestd
 }
 
 // loginV1 tries to register/login to the v1 registry server.
-func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
+func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
 	var (
 		status        string
 		reqBody       []byte
@@ -396,7 +214,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *reques
 // now, users should create their account through other means like directly from a web page
 // served by the v2 registry service provider. Whether this will be supported in the future
 // is to be determined.
-func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
+func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
 	logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
 	var (
 		err       error
@@ -429,7 +247,7 @@ func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *reques
 	return "", fmt.Errorf("no successful auth challenge for %s - errors: %s", registryEndpoint, allErrors)
 }
 
-func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
+func tryV2BasicAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
 	req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
 	if err != nil {
 		return err
@@ -450,7 +268,7 @@ func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, regis
 	return nil
 }
 
-func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
+func tryV2TokenAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
 	token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client, factory)
 	if err != nil {
 		return err
@@ -477,7 +295,7 @@ func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, regis
 }
 
 // this method matches a auth configuration to a server address or a url
-func (config *ConfigFile) ResolveAuthConfig(index *IndexInfo) AuthConfig {
+func ResolveAuthConfig(config *cliconfig.ConfigFile, index *IndexInfo) cliconfig.AuthConfig {
 	configKey := index.GetAuthConfigKey()
 	// First try the happy case
 	if c, found := config.AuthConfigs[configKey]; found || index.Official {
@@ -499,16 +317,12 @@ 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.AuthConfigs {
+	for registry, ac := range config.AuthConfigs {
 		if configKey == convertToHostname(registry) {
-			return config
+			return ac
 		}
 	}
 
 	// When all else fails, return an empty auth config
-	return AuthConfig{}
-}
-
-func (config *ConfigFile) Filename() string {
-	return config.filename
+	return cliconfig.AuthConfig{}
 }

+ 21 - 22
registry/auth_test.go

@@ -5,14 +5,16 @@ import (
 	"os"
 	"path/filepath"
 	"testing"
+
+	"github.com/docker/docker/cliconfig"
 )
 
 func TestEncodeAuth(t *testing.T) {
-	newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
-	authStr := encodeAuth(newAuthConfig)
-	decAuthConfig := &AuthConfig{}
+	newAuthConfig := &cliconfig.AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
+	authStr := cliconfig.EncodeAuth(newAuthConfig)
+	decAuthConfig := &cliconfig.AuthConfig{}
 	var err error
-	decAuthConfig.Username, decAuthConfig.Password, err = decodeAuth(authStr)
+	decAuthConfig.Username, decAuthConfig.Password, err = cliconfig.DecodeAuth(authStr)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -27,19 +29,16 @@ func TestEncodeAuth(t *testing.T) {
 	}
 }
 
-func setupTempConfigFile() (*ConfigFile, error) {
+func setupTempConfigFile() (*cliconfig.ConfigFile, error) {
 	root, err := ioutil.TempDir("", "docker-test-auth")
 	if err != nil {
 		return nil, err
 	}
-	root = filepath.Join(root, CONFIGFILE)
-	configFile := &ConfigFile{
-		AuthConfigs: make(map[string]AuthConfig),
-		filename:    root,
-	}
+	root = filepath.Join(root, cliconfig.CONFIGFILE)
+	configFile := cliconfig.NewConfigFile(root)
 
 	for _, registry := range []string{"testIndex", IndexServerAddress()} {
-		configFile.AuthConfigs[registry] = AuthConfig{
+		configFile.AuthConfigs[registry] = cliconfig.AuthConfig{
 			Username: "docker-user",
 			Password: "docker-pass",
 			Email:    "docker@docker.io",
@@ -54,7 +53,7 @@ func TestSameAuthDataPostSave(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer os.RemoveAll(configFile.filename)
+	defer os.RemoveAll(configFile.Filename())
 
 	err = configFile.Save()
 	if err != nil {
@@ -81,7 +80,7 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer os.RemoveAll(configFile.filename)
+	defer os.RemoveAll(configFile.Filename())
 
 	indexConfig := configFile.AuthConfigs[IndexServerAddress()]
 
@@ -92,10 +91,10 @@ func TestResolveAuthConfigIndexServer(t *testing.T) {
 		Official: false,
 	}
 
-	resolved := configFile.ResolveAuthConfig(officialIndex)
+	resolved := ResolveAuthConfig(configFile, officialIndex)
 	assertEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to return IndexServerAddress()")
 
-	resolved = configFile.ResolveAuthConfig(privateIndex)
+	resolved = ResolveAuthConfig(configFile, privateIndex)
 	assertNotEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to not return IndexServerAddress()")
 }
 
@@ -104,26 +103,26 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer os.RemoveAll(configFile.filename)
+	defer os.RemoveAll(configFile.Filename())
 
-	registryAuth := AuthConfig{
+	registryAuth := cliconfig.AuthConfig{
 		Username: "foo-user",
 		Password: "foo-pass",
 		Email:    "foo@example.com",
 	}
-	localAuth := AuthConfig{
+	localAuth := cliconfig.AuthConfig{
 		Username: "bar-user",
 		Password: "bar-pass",
 		Email:    "bar@example.com",
 	}
-	officialAuth := AuthConfig{
+	officialAuth := cliconfig.AuthConfig{
 		Username: "baz-user",
 		Password: "baz-pass",
 		Email:    "baz@example.com",
 	}
 	configFile.AuthConfigs[IndexServerAddress()] = officialAuth
 
-	expectedAuths := map[string]AuthConfig{
+	expectedAuths := map[string]cliconfig.AuthConfig{
 		"registry.example.com": registryAuth,
 		"localhost:8000":       localAuth,
 		"registry.com":         localAuth,
@@ -160,12 +159,12 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 		}
 		for _, registry := range registries {
 			configFile.AuthConfigs[registry] = configured
-			resolved := configFile.ResolveAuthConfig(index)
+			resolved := ResolveAuthConfig(configFile, index)
 			if resolved.Email != configured.Email {
 				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
 			}
 			delete(configFile.AuthConfigs, registry)
-			resolved = configFile.ResolveAuthConfig(index)
+			resolved = ResolveAuthConfig(configFile, index)
 			if resolved.Email == configured.Email {
 				t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)
 			}

+ 3 - 2
registry/registry_test.go

@@ -7,6 +7,7 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/pkg/requestdecorator"
 )
 
@@ -20,7 +21,7 @@ const (
 )
 
 func spawnTestRegistrySession(t *testing.T) *Session {
-	authConfig := &AuthConfig{}
+	authConfig := &cliconfig.AuthConfig{}
 	endpoint, err := NewEndpoint(makeIndex("/v1/"))
 	if err != nil {
 		t.Fatal(err)
@@ -33,7 +34,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
 }
 
 func TestPublicSession(t *testing.T) {
-	authConfig := &AuthConfig{}
+	authConfig := &cliconfig.AuthConfig{}
 
 	getSessionDecorators := func(index *IndexInfo) int {
 		endpoint, err := NewEndpoint(index)

+ 4 - 2
registry/service.go

@@ -1,5 +1,7 @@
 package registry
 
+import "github.com/docker/docker/cliconfig"
+
 type Service struct {
 	Config *ServiceConfig
 }
@@ -15,7 +17,7 @@ func NewService(options *Options) *Service {
 // Auth contacts the public registry with the provided credentials,
 // and returns OK if authentication was sucessful.
 // It can be used to verify the validity of a client's credentials.
-func (s *Service) Auth(authConfig *AuthConfig) (string, error) {
+func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
 	addr := authConfig.ServerAddress
 	if addr == "" {
 		// Use the official registry address if not specified.
@@ -35,7 +37,7 @@ func (s *Service) Auth(authConfig *AuthConfig) (string, error) {
 
 // Search queries the public registry for images matching the specified
 // search terms, and returns the results.
-func (s *Service) Search(term string, authConfig *AuthConfig, headers map[string][]string) (*SearchResults, error) {
+func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
 	repoInfo, err := s.ResolveRepository(term)
 	if err != nil {
 		return nil, err

+ 5 - 4
registry/session.go

@@ -18,20 +18,21 @@ import (
 	"time"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/requestdecorator"
 	"github.com/docker/docker/pkg/tarsum"
 )
 
 type Session struct {
-	authConfig    *AuthConfig
+	authConfig    *cliconfig.AuthConfig
 	reqFactory    *requestdecorator.RequestFactory
 	indexEndpoint *Endpoint
 	jar           *cookiejar.Jar
 	timeout       TimeoutType
 }
 
-func NewSession(authConfig *AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
+func NewSession(authConfig *cliconfig.AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
 	r = &Session{
 		authConfig:    authConfig,
 		indexEndpoint: endpoint,
@@ -600,12 +601,12 @@ func (r *Session) SearchRepositories(term string) (*SearchResults, error) {
 	return result, err
 }
 
-func (r *Session) GetAuthConfig(withPasswd bool) *AuthConfig {
+func (r *Session) GetAuthConfig(withPasswd bool) *cliconfig.AuthConfig {
 	password := ""
 	if withPasswd {
 		password = r.authConfig.Password
 	}
-	return &AuthConfig{
+	return &cliconfig.AuthConfig{
 		Username: r.authConfig.Username,
 		Password: password,
 		Email:    r.authConfig.Email,