Browse Source

Add support for DOCKER_CONFIG/--config to specific config file dir

Carry #11675

Aside from what #11675 says, to me a key usecase for this is to support
more than one Docker cli running at the same time but each may have its
own set of config files.

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

+ 1 - 3
api/client/cli.go

@@ -7,13 +7,11 @@ import (
 	"fmt"
 	"io"
 	"net/http"
-	"path/filepath"
 	"reflect"
 	"strings"
 	"text/template"
 
 	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/pkg/homedir"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/sockets"
 	"github.com/docker/docker/pkg/term"
@@ -212,7 +210,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, a
 	}
 	sockets.ConfigureTCPTransport(tr, proto, addr)
 
-	configFile, e := cliconfig.Load(filepath.Join(homedir.Get(), ".docker"))
+	configFile, e := cliconfig.Load(cliconfig.ConfigDir())
 	if e != nil {
 		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
 	}

+ 16 - 1
cliconfig/config.go

@@ -25,9 +25,24 @@ const (
 )
 
 var (
+	configDir            = os.Getenv("DOCKER_CONFIG")
 	ErrConfigFileMissing = errors.New("The Auth config file is missing")
 )
 
+func init() {
+	if configDir == "" {
+		configDir = filepath.Join(homedir.Get(), ".docker")
+	}
+}
+
+func ConfigDir() string {
+	return configDir
+}
+
+func SetConfigDir(dir string) {
+	configDir = dir
+}
+
 // Registry Auth Info
 type AuthConfig struct {
 	Username      string `json:"username,omitempty"`
@@ -56,7 +71,7 @@ func NewConfigFile(fn string) *ConfigFile {
 // FIXME: use the internal golang config parser
 func Load(configDir string) (*ConfigFile, error) {
 	if configDir == "" {
-		configDir = filepath.Join(homedir.Get(), ".docker")
+		configDir = ConfigDir()
 	}
 
 	configFile := ConfigFile{

+ 2 - 2
docker/daemon.go

@@ -13,8 +13,8 @@ import (
 	"github.com/Sirupsen/logrus"
 	apiserver "github.com/docker/docker/api/server"
 	"github.com/docker/docker/autogen/dockerversion"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
-	"github.com/docker/docker/pkg/homedir"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/pidfile"
 	"github.com/docker/docker/pkg/signal"
@@ -39,7 +39,7 @@ func init() {
 
 func migrateKey() (err error) {
 	// Migrate trust key if exists at ~/.docker/key.json and owned by current user
-	oldPath := filepath.Join(homedir.Get(), ".docker", defaultTrustKeyFile)
+	oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
 	newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
 	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
 		defer func() {

+ 5 - 0
docker/docker.go

@@ -10,6 +10,7 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/autogen/dockerversion"
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/reexec"
@@ -43,6 +44,10 @@ func main() {
 		return
 	}
 
+	if *flConfigDir != "" {
+		cliconfig.SetConfigDir(*flConfigDir)
+	}
+
 	if *flLogLevel != "" {
 		lvl, err := logrus.ParseLevel(*flLogLevel)
 		if err != nil {

+ 5 - 4
docker/flags.go

@@ -7,8 +7,8 @@ import (
 	"runtime"
 	"sort"
 
+	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/homedir"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/tlsconfig"
 )
@@ -73,19 +73,20 @@ var (
 
 func init() {
 	if dockerCertPath == "" {
-		dockerCertPath = filepath.Join(homedir.Get(), ".docker")
+		dockerCertPath = cliconfig.ConfigDir()
 	}
 }
 
 func getDaemonConfDir() string {
 	// TODO: update for Windows daemon
 	if runtime.GOOS == "windows" {
-		return filepath.Join(homedir.Get(), ".docker")
+		return cliconfig.ConfigDir()
 	}
 	return "/etc/docker"
 }
 
 var (
+	flConfigDir = flag.String([]string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
 	flVersion   = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
 	flDaemon    = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
 	flDebug     = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
@@ -105,7 +106,7 @@ func setDefaultConfFlag(flag *string, def string) {
 		if *flDaemon {
 			*flag = filepath.Join(getDaemonConfDir(), def)
 		} else {
-			*flag = filepath.Join(homedir.Get(), ".docker", def)
+			*flag = filepath.Join(cliconfig.ConfigDir(), def)
 		}
 	}
 }

+ 17 - 5
docs/reference/commandline/cli.md

@@ -10,7 +10,7 @@ parent = "smn_cli"
 
 # Using the command line
 
-> **Note:** if you are using a remote Docker daemon, such as Boot2Docker, 
+> **Note:** If you are using a remote Docker daemon, such as Boot2Docker,
 > then _do not_ type the `sudo` before the `docker` commands shown in the
 > documentation's examples.
 
@@ -38,6 +38,7 @@ the [installation](/installation) instructions for your operating system.
 For easy reference, the following list of environment variables are supported
 by the `docker` command line:
 
+* `DOCKER_CONFIG` The location of your client configuration files.
 * `DOCKER_CERT_PATH` The location of your authentication keys.
 * `DOCKER_DRIVER` The graph driver to use.
 * `DOCKER_HOST` Daemon socket to connect to.
@@ -60,10 +61,21 @@ 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`
+By default, the Docker command line stores its configuration files in a
+directory called `.docker` within your `HOME` directory. However, you can
+specify a different location via the `DOCKER_CONFIG` environment variable
+or the `--config` command line option. If both are specified, then the
+`--config` option overrides the `DOCKER_CONFIG` environment variable.
+For example:
+
+    docker --config ~/testconfigs/ ps
+
+Instructs Docker to use the configuration files in your `~/testconfigs/`
+directory when running the `ps` command.
+
+Docker manages most of the files in the configuration directory
+and you should not modify them. However, you *can modify* the
+`config.json` file to control certain aspects of how the `docker`
 command behaves.
 
 Currently, you can modify the `docker` command behavior using environment

+ 1 - 0
docs/reference/commandline/daemon.md

@@ -18,6 +18,7 @@ parent = "smn_cli"
       --api-cors-header=""                   Set CORS headers in the remote API
       -b, --bridge=""                        Attach containers to a network bridge
       --bip=""                               Specify network bridge IP
+      --config=~/.docker                     Location of client config files
       -D, --debug=false                      Enable debug mode
       -d, --daemon=false                     Enable daemon mode
       --default-gateway=""                   Container default gateway IPv4 address

+ 82 - 0
integration-cli/docker_cli_config_test.go

@@ -64,3 +64,85 @@ func (s *DockerSuite) TestConfigHttpHeader(c *check.C) {
 		c.Fatalf("Missing/bad header: %q\nout:%v", headers, out)
 	}
 }
+
+func (s *DockerSuite) TestConfigDir(c *check.C) {
+	cDir, _ := ioutil.TempDir("", "fake-home")
+
+	// First make sure pointing to empty dir doesn't generate an error
+	cmd := exec.Command(dockerBinary, "--config", cDir, "ps")
+	out, rc, err := runCommandWithOutput(cmd)
+
+	if rc != 0 || err != nil {
+		c.Fatalf("ps1 didn't work:\nrc:%d\nout%s\nerr:%v", rc, out, err)
+	}
+
+	// Test with env var too
+	cmd = exec.Command(dockerBinary, "ps")
+	cmd.Env = append(os.Environ(), "DOCKER_CONFIG="+cDir)
+	out, rc, err = runCommandWithOutput(cmd)
+
+	if rc != 0 || err != nil {
+		c.Fatalf("ps2 didn't work:\nrc:%d\nout%s\nerr:%v", rc, out, err)
+	}
+
+	// Start a server so we can check to see if the config file was
+	// loaded properly
+	var headers map[string][]string
+
+	server := httptest.NewServer(http.HandlerFunc(
+		func(w http.ResponseWriter, r *http.Request) {
+			headers = r.Header
+		}))
+	defer server.Close()
+
+	// Create a dummy config file in our new config dir
+	data := `{
+		"HttpHeaders": { "MyHeader": "MyValue" }
+	}`
+
+	tmpCfg := filepath.Join(cDir, "config.json")
+	err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
+	if err != nil {
+		c.Fatalf("Err creating file(%s): %v", tmpCfg, err)
+	}
+
+	cmd = exec.Command(dockerBinary, "--config", cDir, "-H="+server.URL[7:], "ps")
+	out, _, _ = runCommandWithOutput(cmd)
+
+	if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
+		c.Fatalf("ps3 - Missing header: %q\nout:%v", headers, out)
+	}
+
+	// Reset headers and try again using env var this time
+	headers = map[string][]string{}
+	cmd = exec.Command(dockerBinary, "-H="+server.URL[7:], "ps")
+	cmd.Env = append(os.Environ(), "DOCKER_CONFIG="+cDir)
+	out, _, _ = runCommandWithOutput(cmd)
+
+	if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
+		c.Fatalf("ps4 - Missing header: %q\nout:%v", headers, out)
+	}
+
+	// Reset headers and make sure flag overrides the env var
+	headers = map[string][]string{}
+	cmd = exec.Command(dockerBinary, "--config", cDir, "-H="+server.URL[7:], "ps")
+	cmd.Env = append(os.Environ(), "DOCKER_CONFIG=MissingDir")
+	out, _, _ = runCommandWithOutput(cmd)
+
+	if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
+		c.Fatalf("ps5 - Missing header: %q\nout:%v", headers, out)
+	}
+
+	// Reset headers and make sure flag overrides the env var.
+	// Almost same as previous but make sure the "MissingDir" isn't
+	// ignore - we don't want to default back to the env var.
+	headers = map[string][]string{}
+	cmd = exec.Command(dockerBinary, "--config", "MissingDir", "-H="+server.URL[7:], "ps")
+	cmd.Env = append(os.Environ(), "DOCKER_CONFIG="+cDir)
+	out, _, _ = runCommandWithOutput(cmd)
+
+	if headers["Myheader"] != nil {
+		c.Fatalf("ps6 - Headers are there but shouldn't be: %q\nout:%v", headers, out)
+	}
+
+}

+ 3 - 0
man/docker.1.md

@@ -35,6 +35,9 @@ To see the man page for a command run **man docker <command>**.
 **--bip**=""
   Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b
 
+**--config**=""
+  Specifies the location of the Docker client configuration files. The default is '~/.docker'.
+
 **-D**, **--debug**=*true*|*false*
   Enable debug mode. Default is false.