Skip to content

Commit

Permalink
Add support for DOCKER_CONFIG/--config to specific config file dir
Browse files Browse the repository at this point in the history
Carry moby#11675

Aside from what moby#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 <[email protected]>
  • Loading branch information
Doug Davis committed Jul 10, 2015
1 parent 0afd7bd commit daced1d
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 15 deletions.
4 changes: 1 addition & 3 deletions api/client/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
Expand Down
17 changes: 16 additions & 1 deletion cliconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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{
Expand Down
4 changes: 2 additions & 2 deletions docker/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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() {
Expand Down
5 changes: 5 additions & 0 deletions docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -43,6 +44,10 @@ func main() {
return
}

if *flConfigDir != "" {
cliconfig.SetConfigDir(*flConfigDir)
}

if *flLogLevel != "" {
lvl, err := logrus.ParseLevel(*flLogLevel)
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions docker/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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")
Expand All @@ -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)
}
}
}
Expand Down
22 changes: 17 additions & 5 deletions docs/reference/commandline/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/reference/commandline/daemon.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 82 additions & 0 deletions integration-cli/docker_cli_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 3 additions & 0 deletions man/docker.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down

0 comments on commit daced1d

Please sign in to comment.