mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									0afd7bde02
								
							
						
					
					
						commit
						daced1d303
					
				
					 9 changed files with 132 additions and 15 deletions
				
			
		|  | @ -7,13 +7,11 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"path/filepath" |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"text/template" | 	"text/template" | ||||||
| 
 | 
 | ||||||
| 	"github.com/docker/docker/cliconfig" | 	"github.com/docker/docker/cliconfig" | ||||||
| 	"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/sockets" | 	"github.com/docker/docker/pkg/sockets" | ||||||
| 	"github.com/docker/docker/pkg/term" | 	"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) | 	sockets.ConfigureTCPTransport(tr, proto, addr) | ||||||
| 
 | 
 | ||||||
| 	configFile, e := cliconfig.Load(filepath.Join(homedir.Get(), ".docker")) | 	configFile, e := cliconfig.Load(cliconfig.ConfigDir()) | ||||||
| 	if e != nil { | 	if e != nil { | ||||||
| 		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) | 		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -25,9 +25,24 @@ const ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
|  | 	configDir            = os.Getenv("DOCKER_CONFIG") | ||||||
| 	ErrConfigFileMissing = errors.New("The Auth config file is missing") | 	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 | // Registry Auth Info | ||||||
| type AuthConfig struct { | type AuthConfig struct { | ||||||
| 	Username      string `json:"username,omitempty"` | 	Username      string `json:"username,omitempty"` | ||||||
|  | @ -56,7 +71,7 @@ func NewConfigFile(fn string) *ConfigFile { | ||||||
| // FIXME: use the internal golang config parser | // FIXME: use the internal golang config parser | ||||||
| func Load(configDir string) (*ConfigFile, error) { | func Load(configDir string) (*ConfigFile, error) { | ||||||
| 	if configDir == "" { | 	if configDir == "" { | ||||||
| 		configDir = filepath.Join(homedir.Get(), ".docker") | 		configDir = ConfigDir() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	configFile := ConfigFile{ | 	configFile := ConfigFile{ | ||||||
|  |  | ||||||
|  | @ -13,8 +13,8 @@ import ( | ||||||
| 	"github.com/Sirupsen/logrus" | 	"github.com/Sirupsen/logrus" | ||||||
| 	apiserver "github.com/docker/docker/api/server" | 	apiserver "github.com/docker/docker/api/server" | ||||||
| 	"github.com/docker/docker/autogen/dockerversion" | 	"github.com/docker/docker/autogen/dockerversion" | ||||||
|  | 	"github.com/docker/docker/cliconfig" | ||||||
| 	"github.com/docker/docker/daemon" | 	"github.com/docker/docker/daemon" | ||||||
| 	"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/pidfile" | 	"github.com/docker/docker/pkg/pidfile" | ||||||
| 	"github.com/docker/docker/pkg/signal" | 	"github.com/docker/docker/pkg/signal" | ||||||
|  | @ -39,7 +39,7 @@ func init() { | ||||||
| 
 | 
 | ||||||
| func migrateKey() (err error) { | func migrateKey() (err error) { | ||||||
| 	// Migrate trust key if exists at ~/.docker/key.json and owned by current user | 	// 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) | 	newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) | ||||||
| 	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { | 	if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { | ||||||
| 		defer func() { | 		defer func() { | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ import ( | ||||||
| 	"github.com/Sirupsen/logrus" | 	"github.com/Sirupsen/logrus" | ||||||
| 	"github.com/docker/docker/api/client" | 	"github.com/docker/docker/api/client" | ||||||
| 	"github.com/docker/docker/autogen/dockerversion" | 	"github.com/docker/docker/autogen/dockerversion" | ||||||
|  | 	"github.com/docker/docker/cliconfig" | ||||||
| 	"github.com/docker/docker/opts" | 	"github.com/docker/docker/opts" | ||||||
| 	flag "github.com/docker/docker/pkg/mflag" | 	flag "github.com/docker/docker/pkg/mflag" | ||||||
| 	"github.com/docker/docker/pkg/reexec" | 	"github.com/docker/docker/pkg/reexec" | ||||||
|  | @ -43,6 +44,10 @@ func main() { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if *flConfigDir != "" { | ||||||
|  | 		cliconfig.SetConfigDir(*flConfigDir) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if *flLogLevel != "" { | 	if *flLogLevel != "" { | ||||||
| 		lvl, err := logrus.ParseLevel(*flLogLevel) | 		lvl, err := logrus.ParseLevel(*flLogLevel) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  |  | ||||||
|  | @ -7,8 +7,8 @@ import ( | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"sort" | 	"sort" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/docker/docker/cliconfig" | ||||||
| 	"github.com/docker/docker/opts" | 	"github.com/docker/docker/opts" | ||||||
| 	"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/tlsconfig" | 	"github.com/docker/docker/pkg/tlsconfig" | ||||||
| ) | ) | ||||||
|  | @ -73,19 +73,20 @@ var ( | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	if dockerCertPath == "" { | 	if dockerCertPath == "" { | ||||||
| 		dockerCertPath = filepath.Join(homedir.Get(), ".docker") | 		dockerCertPath = cliconfig.ConfigDir() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getDaemonConfDir() string { | func getDaemonConfDir() string { | ||||||
| 	// TODO: update for Windows daemon | 	// TODO: update for Windows daemon | ||||||
| 	if runtime.GOOS == "windows" { | 	if runtime.GOOS == "windows" { | ||||||
| 		return filepath.Join(homedir.Get(), ".docker") | 		return cliconfig.ConfigDir() | ||||||
| 	} | 	} | ||||||
| 	return "/etc/docker" | 	return "/etc/docker" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var ( | 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") | 	flVersion   = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") | ||||||
| 	flDaemon    = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode") | 	flDaemon    = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode") | ||||||
| 	flDebug     = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") | 	flDebug     = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") | ||||||
|  | @ -105,7 +106,7 @@ func setDefaultConfFlag(flag *string, def string) { | ||||||
| 		if *flDaemon { | 		if *flDaemon { | ||||||
| 			*flag = filepath.Join(getDaemonConfDir(), def) | 			*flag = filepath.Join(getDaemonConfDir(), def) | ||||||
| 		} else { | 		} else { | ||||||
| 			*flag = filepath.Join(homedir.Get(), ".docker", def) | 			*flag = filepath.Join(cliconfig.ConfigDir(), def) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ parent = "smn_cli" | ||||||
| 
 | 
 | ||||||
| # Using the command line | # 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 | > then _do not_ type the `sudo` before the `docker` commands shown in the | ||||||
| > documentation's examples. | > 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 | For easy reference, the following list of environment variables are supported | ||||||
| by the `docker` command line: | by the `docker` command line: | ||||||
| 
 | 
 | ||||||
|  | * `DOCKER_CONFIG` The location of your client configuration files. | ||||||
| * `DOCKER_CERT_PATH` The location of your authentication keys. | * `DOCKER_CERT_PATH` The location of your authentication keys. | ||||||
| * `DOCKER_DRIVER` The graph driver to use. | * `DOCKER_DRIVER` The graph driver to use. | ||||||
| * `DOCKER_HOST` Daemon socket to connect to. | * `DOCKER_HOST` Daemon socket to connect to. | ||||||
|  | @ -60,10 +61,21 @@ variables. | ||||||
| 
 | 
 | ||||||
| ## Configuration files | ## Configuration files | ||||||
| 
 | 
 | ||||||
| The Docker command line stores its configuration files in a directory called | By default, the Docker command line stores its configuration files in a | ||||||
| `.docker` within your `HOME` directory. Docker manages most of the files in | directory called `.docker` within your `HOME` directory. However, you can | ||||||
| `.docker` and you should not modify them. However, you *can modify* the | specify a different location via the `DOCKER_CONFIG` environment variable | ||||||
| `.docker/config.json` file to control certain aspects of how the `docker` | 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. | command behaves. | ||||||
| 
 | 
 | ||||||
| Currently, you can modify the `docker` command behavior using environment | Currently, you can modify the `docker` command behavior using environment | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ parent = "smn_cli" | ||||||
|       --api-cors-header=""                   Set CORS headers in the remote API |       --api-cors-header=""                   Set CORS headers in the remote API | ||||||
|       -b, --bridge=""                        Attach containers to a network bridge |       -b, --bridge=""                        Attach containers to a network bridge | ||||||
|       --bip=""                               Specify network bridge IP |       --bip=""                               Specify network bridge IP | ||||||
|  |       --config=~/.docker                     Location of client config files | ||||||
|       -D, --debug=false                      Enable debug mode |       -D, --debug=false                      Enable debug mode | ||||||
|       -d, --daemon=false                     Enable daemon mode |       -d, --daemon=false                     Enable daemon mode | ||||||
|       --default-gateway=""                   Container default gateway IPv4 address |       --default-gateway=""                   Container default gateway IPv4 address | ||||||
|  |  | ||||||
|  | @ -64,3 +64,85 @@ func (s *DockerSuite) TestConfigHttpHeader(c *check.C) { | ||||||
| 		c.Fatalf("Missing/bad header: %q\nout:%v", headers, out) | 		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) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -35,6 +35,9 @@ To see the man page for a command run **man docker <command>**. | ||||||
| **--bip**="" | **--bip**="" | ||||||
|   Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b |   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* | **-D**, **--debug**=*true*|*false* | ||||||
|   Enable debug mode. Default is false. |   Enable debug mode. Default is false. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Doug Davis
						Doug Davis