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>
| ... | ... |
@@ -7,13 +7,11 @@ import ( |
| 7 | 7 |
"fmt" |
| 8 | 8 |
"io" |
| 9 | 9 |
"net/http" |
| 10 |
- "path/filepath" |
|
| 11 | 10 |
"reflect" |
| 12 | 11 |
"strings" |
| 13 | 12 |
"text/template" |
| 14 | 13 |
|
| 15 | 14 |
"github.com/docker/docker/cliconfig" |
| 16 |
- "github.com/docker/docker/pkg/homedir" |
|
| 17 | 15 |
flag "github.com/docker/docker/pkg/mflag" |
| 18 | 16 |
"github.com/docker/docker/pkg/sockets" |
| 19 | 17 |
"github.com/docker/docker/pkg/term" |
| ... | ... |
@@ -212,7 +210,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, a |
| 212 | 212 |
} |
| 213 | 213 |
sockets.ConfigureTCPTransport(tr, proto, addr) |
| 214 | 214 |
|
| 215 |
- configFile, e := cliconfig.Load(filepath.Join(homedir.Get(), ".docker")) |
|
| 215 |
+ configFile, e := cliconfig.Load(cliconfig.ConfigDir()) |
|
| 216 | 216 |
if e != nil {
|
| 217 | 217 |
fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) |
| 218 | 218 |
} |
| ... | ... |
@@ -25,9 +25,24 @@ const ( |
| 25 | 25 |
) |
| 26 | 26 |
|
| 27 | 27 |
var ( |
| 28 |
+ configDir = os.Getenv("DOCKER_CONFIG")
|
|
| 28 | 29 |
ErrConfigFileMissing = errors.New("The Auth config file is missing")
|
| 29 | 30 |
) |
| 30 | 31 |
|
| 32 |
+func init() {
|
|
| 33 |
+ if configDir == "" {
|
|
| 34 |
+ configDir = filepath.Join(homedir.Get(), ".docker") |
|
| 35 |
+ } |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func ConfigDir() string {
|
|
| 39 |
+ return configDir |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func SetConfigDir(dir string) {
|
|
| 43 |
+ configDir = dir |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 31 | 46 |
// Registry Auth Info |
| 32 | 47 |
type AuthConfig struct {
|
| 33 | 48 |
Username string `json:"username,omitempty"` |
| ... | ... |
@@ -56,7 +71,7 @@ func NewConfigFile(fn string) *ConfigFile {
|
| 56 | 56 |
// FIXME: use the internal golang config parser |
| 57 | 57 |
func Load(configDir string) (*ConfigFile, error) {
|
| 58 | 58 |
if configDir == "" {
|
| 59 |
- configDir = filepath.Join(homedir.Get(), ".docker") |
|
| 59 |
+ configDir = ConfigDir() |
|
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 | 62 |
configFile := ConfigFile{
|
| ... | ... |
@@ -13,8 +13,8 @@ import ( |
| 13 | 13 |
"github.com/Sirupsen/logrus" |
| 14 | 14 |
apiserver "github.com/docker/docker/api/server" |
| 15 | 15 |
"github.com/docker/docker/autogen/dockerversion" |
| 16 |
+ "github.com/docker/docker/cliconfig" |
|
| 16 | 17 |
"github.com/docker/docker/daemon" |
| 17 |
- "github.com/docker/docker/pkg/homedir" |
|
| 18 | 18 |
flag "github.com/docker/docker/pkg/mflag" |
| 19 | 19 |
"github.com/docker/docker/pkg/pidfile" |
| 20 | 20 |
"github.com/docker/docker/pkg/signal" |
| ... | ... |
@@ -39,7 +39,7 @@ func init() {
|
| 39 | 39 |
|
| 40 | 40 |
func migrateKey() (err error) {
|
| 41 | 41 |
// Migrate trust key if exists at ~/.docker/key.json and owned by current user |
| 42 |
- oldPath := filepath.Join(homedir.Get(), ".docker", defaultTrustKeyFile) |
|
| 42 |
+ oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) |
|
| 43 | 43 |
newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) |
| 44 | 44 |
if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
|
| 45 | 45 |
defer func() {
|
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/Sirupsen/logrus" |
| 11 | 11 |
"github.com/docker/docker/api/client" |
| 12 | 12 |
"github.com/docker/docker/autogen/dockerversion" |
| 13 |
+ "github.com/docker/docker/cliconfig" |
|
| 13 | 14 |
"github.com/docker/docker/opts" |
| 14 | 15 |
flag "github.com/docker/docker/pkg/mflag" |
| 15 | 16 |
"github.com/docker/docker/pkg/reexec" |
| ... | ... |
@@ -43,6 +44,10 @@ func main() {
|
| 43 | 43 |
return |
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 |
+ if *flConfigDir != "" {
|
|
| 47 |
+ cliconfig.SetConfigDir(*flConfigDir) |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 46 | 50 |
if *flLogLevel != "" {
|
| 47 | 51 |
lvl, err := logrus.ParseLevel(*flLogLevel) |
| 48 | 52 |
if err != nil {
|
| ... | ... |
@@ -7,8 +7,8 @@ import ( |
| 7 | 7 |
"runtime" |
| 8 | 8 |
"sort" |
| 9 | 9 |
|
| 10 |
+ "github.com/docker/docker/cliconfig" |
|
| 10 | 11 |
"github.com/docker/docker/opts" |
| 11 |
- "github.com/docker/docker/pkg/homedir" |
|
| 12 | 12 |
flag "github.com/docker/docker/pkg/mflag" |
| 13 | 13 |
"github.com/docker/docker/pkg/tlsconfig" |
| 14 | 14 |
) |
| ... | ... |
@@ -73,19 +73,20 @@ var ( |
| 73 | 73 |
|
| 74 | 74 |
func init() {
|
| 75 | 75 |
if dockerCertPath == "" {
|
| 76 |
- dockerCertPath = filepath.Join(homedir.Get(), ".docker") |
|
| 76 |
+ dockerCertPath = cliconfig.ConfigDir() |
|
| 77 | 77 |
} |
| 78 | 78 |
} |
| 79 | 79 |
|
| 80 | 80 |
func getDaemonConfDir() string {
|
| 81 | 81 |
// TODO: update for Windows daemon |
| 82 | 82 |
if runtime.GOOS == "windows" {
|
| 83 |
- return filepath.Join(homedir.Get(), ".docker") |
|
| 83 |
+ return cliconfig.ConfigDir() |
|
| 84 | 84 |
} |
| 85 | 85 |
return "/etc/docker" |
| 86 | 86 |
} |
| 87 | 87 |
|
| 88 | 88 |
var ( |
| 89 |
+ flConfigDir = flag.String([]string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
|
|
| 89 | 90 |
flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
|
| 90 | 91 |
flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
|
| 91 | 92 |
flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
|
| ... | ... |
@@ -105,7 +106,7 @@ func setDefaultConfFlag(flag *string, def string) {
|
| 105 | 105 |
if *flDaemon {
|
| 106 | 106 |
*flag = filepath.Join(getDaemonConfDir(), def) |
| 107 | 107 |
} else {
|
| 108 |
- *flag = filepath.Join(homedir.Get(), ".docker", def) |
|
| 108 |
+ *flag = filepath.Join(cliconfig.ConfigDir(), def) |
|
| 109 | 109 |
} |
| 110 | 110 |
} |
| 111 | 111 |
} |
| ... | ... |
@@ -10,7 +10,7 @@ parent = "smn_cli" |
| 10 | 10 |
|
| 11 | 11 |
# Using the command line |
| 12 | 12 |
|
| 13 |
-> **Note:** if you are using a remote Docker daemon, such as Boot2Docker, |
|
| 13 |
+> **Note:** If you are using a remote Docker daemon, such as Boot2Docker, |
|
| 14 | 14 |
> then _do not_ type the `sudo` before the `docker` commands shown in the |
| 15 | 15 |
> documentation's examples. |
| 16 | 16 |
|
| ... | ... |
@@ -38,6 +38,7 @@ the [installation](/installation) instructions for your operating system. |
| 38 | 38 |
For easy reference, the following list of environment variables are supported |
| 39 | 39 |
by the `docker` command line: |
| 40 | 40 |
|
| 41 |
+* `DOCKER_CONFIG` The location of your client configuration files. |
|
| 41 | 42 |
* `DOCKER_CERT_PATH` The location of your authentication keys. |
| 42 | 43 |
* `DOCKER_DRIVER` The graph driver to use. |
| 43 | 44 |
* `DOCKER_HOST` Daemon socket to connect to. |
| ... | ... |
@@ -60,10 +61,21 @@ variables. |
| 60 | 60 |
|
| 61 | 61 |
## Configuration files |
| 62 | 62 |
|
| 63 |
-The Docker command line stores its configuration files in a directory called |
|
| 64 |
-`.docker` within your `HOME` directory. Docker manages most of the files in |
|
| 65 |
-`.docker` and you should not modify them. However, you *can modify* the |
|
| 66 |
-`.docker/config.json` file to control certain aspects of how the `docker` |
|
| 63 |
+By default, the Docker command line stores its configuration files in a |
|
| 64 |
+directory called `.docker` within your `HOME` directory. However, you can |
|
| 65 |
+specify a different location via the `DOCKER_CONFIG` environment variable |
|
| 66 |
+or the `--config` command line option. If both are specified, then the |
|
| 67 |
+`--config` option overrides the `DOCKER_CONFIG` environment variable. |
|
| 68 |
+For example: |
|
| 69 |
+ |
|
| 70 |
+ docker --config ~/testconfigs/ ps |
|
| 71 |
+ |
|
| 72 |
+Instructs Docker to use the configuration files in your `~/testconfigs/` |
|
| 73 |
+directory when running the `ps` command. |
|
| 74 |
+ |
|
| 75 |
+Docker manages most of the files in the configuration directory |
|
| 76 |
+and you should not modify them. However, you *can modify* the |
|
| 77 |
+`config.json` file to control certain aspects of how the `docker` |
|
| 67 | 78 |
command behaves. |
| 68 | 79 |
|
| 69 | 80 |
Currently, you can modify the `docker` command behavior using environment |
| ... | ... |
@@ -18,6 +18,7 @@ parent = "smn_cli" |
| 18 | 18 |
--api-cors-header="" Set CORS headers in the remote API |
| 19 | 19 |
-b, --bridge="" Attach containers to a network bridge |
| 20 | 20 |
--bip="" Specify network bridge IP |
| 21 |
+ --config=~/.docker Location of client config files |
|
| 21 | 22 |
-D, --debug=false Enable debug mode |
| 22 | 23 |
-d, --daemon=false Enable daemon mode |
| 23 | 24 |
--default-gateway="" Container default gateway IPv4 address |
| ... | ... |
@@ -64,3 +64,85 @@ func (s *DockerSuite) TestConfigHttpHeader(c *check.C) {
|
| 64 | 64 |
c.Fatalf("Missing/bad header: %q\nout:%v", headers, out)
|
| 65 | 65 |
} |
| 66 | 66 |
} |
| 67 |
+ |
|
| 68 |
+func (s *DockerSuite) TestConfigDir(c *check.C) {
|
|
| 69 |
+ cDir, _ := ioutil.TempDir("", "fake-home")
|
|
| 70 |
+ |
|
| 71 |
+ // First make sure pointing to empty dir doesn't generate an error |
|
| 72 |
+ cmd := exec.Command(dockerBinary, "--config", cDir, "ps") |
|
| 73 |
+ out, rc, err := runCommandWithOutput(cmd) |
|
| 74 |
+ |
|
| 75 |
+ if rc != 0 || err != nil {
|
|
| 76 |
+ c.Fatalf("ps1 didn't work:\nrc:%d\nout%s\nerr:%v", rc, out, err)
|
|
| 77 |
+ } |
|
| 78 |
+ |
|
| 79 |
+ // Test with env var too |
|
| 80 |
+ cmd = exec.Command(dockerBinary, "ps") |
|
| 81 |
+ cmd.Env = append(os.Environ(), "DOCKER_CONFIG="+cDir) |
|
| 82 |
+ out, rc, err = runCommandWithOutput(cmd) |
|
| 83 |
+ |
|
| 84 |
+ if rc != 0 || err != nil {
|
|
| 85 |
+ c.Fatalf("ps2 didn't work:\nrc:%d\nout%s\nerr:%v", rc, out, err)
|
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 88 |
+ // Start a server so we can check to see if the config file was |
|
| 89 |
+ // loaded properly |
|
| 90 |
+ var headers map[string][]string |
|
| 91 |
+ |
|
| 92 |
+ server := httptest.NewServer(http.HandlerFunc( |
|
| 93 |
+ func(w http.ResponseWriter, r *http.Request) {
|
|
| 94 |
+ headers = r.Header |
|
| 95 |
+ })) |
|
| 96 |
+ defer server.Close() |
|
| 97 |
+ |
|
| 98 |
+ // Create a dummy config file in our new config dir |
|
| 99 |
+ data := `{
|
|
| 100 |
+ "HttpHeaders": { "MyHeader": "MyValue" }
|
|
| 101 |
+ }` |
|
| 102 |
+ |
|
| 103 |
+ tmpCfg := filepath.Join(cDir, "config.json") |
|
| 104 |
+ err = ioutil.WriteFile(tmpCfg, []byte(data), 0600) |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ c.Fatalf("Err creating file(%s): %v", tmpCfg, err)
|
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ cmd = exec.Command(dockerBinary, "--config", cDir, "-H="+server.URL[7:], "ps") |
|
| 110 |
+ out, _, _ = runCommandWithOutput(cmd) |
|
| 111 |
+ |
|
| 112 |
+ if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
|
|
| 113 |
+ c.Fatalf("ps3 - Missing header: %q\nout:%v", headers, out)
|
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ // Reset headers and try again using env var this time |
|
| 117 |
+ headers = map[string][]string{}
|
|
| 118 |
+ cmd = exec.Command(dockerBinary, "-H="+server.URL[7:], "ps") |
|
| 119 |
+ cmd.Env = append(os.Environ(), "DOCKER_CONFIG="+cDir) |
|
| 120 |
+ out, _, _ = runCommandWithOutput(cmd) |
|
| 121 |
+ |
|
| 122 |
+ if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
|
|
| 123 |
+ c.Fatalf("ps4 - Missing header: %q\nout:%v", headers, out)
|
|
| 124 |
+ } |
|
| 125 |
+ |
|
| 126 |
+ // Reset headers and make sure flag overrides the env var |
|
| 127 |
+ headers = map[string][]string{}
|
|
| 128 |
+ cmd = exec.Command(dockerBinary, "--config", cDir, "-H="+server.URL[7:], "ps") |
|
| 129 |
+ cmd.Env = append(os.Environ(), "DOCKER_CONFIG=MissingDir") |
|
| 130 |
+ out, _, _ = runCommandWithOutput(cmd) |
|
| 131 |
+ |
|
| 132 |
+ if headers["Myheader"] == nil || headers["Myheader"][0] != "MyValue" {
|
|
| 133 |
+ c.Fatalf("ps5 - Missing header: %q\nout:%v", headers, out)
|
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ // Reset headers and make sure flag overrides the env var. |
|
| 137 |
+ // Almost same as previous but make sure the "MissingDir" isn't |
|
| 138 |
+ // ignore - we don't want to default back to the env var. |
|
| 139 |
+ headers = map[string][]string{}
|
|
| 140 |
+ cmd = exec.Command(dockerBinary, "--config", "MissingDir", "-H="+server.URL[7:], "ps") |
|
| 141 |
+ cmd.Env = append(os.Environ(), "DOCKER_CONFIG="+cDir) |
|
| 142 |
+ out, _, _ = runCommandWithOutput(cmd) |
|
| 143 |
+ |
|
| 144 |
+ if headers["Myheader"] != nil {
|
|
| 145 |
+ c.Fatalf("ps6 - Headers are there but shouldn't be: %q\nout:%v", headers, out)
|
|
| 146 |
+ } |
|
| 147 |
+ |
|
| 148 |
+} |
| ... | ... |
@@ -35,6 +35,9 @@ To see the man page for a command run **man docker <command>**. |
| 35 | 35 |
**--bip**="" |
| 36 | 36 |
Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b |
| 37 | 37 |
|
| 38 |
+**--config**="" |
|
| 39 |
+ Specifies the location of the Docker client configuration files. The default is '~/.docker'. |
|
| 40 |
+ |
|
| 38 | 41 |
**-D**, **--debug**=*true*|*false* |
| 39 | 42 |
Enable debug mode. Default is false. |
| 40 | 43 |
|