mirror of https://github.com/docker/docs.git
Merge pull request #1033 from ehazlett/env-shell-selection
shell selection for env
This commit is contained in:
commit
03c245cf4f
|
@ -1,9 +1,11 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -33,6 +35,10 @@ import (
|
||||||
"github.com/docker/machine/utils"
|
"github.com/docker/machine/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownShell = errors.New("unknown shell")
|
||||||
|
)
|
||||||
|
|
||||||
type machineConfig struct {
|
type machineConfig struct {
|
||||||
machineName string
|
machineName string
|
||||||
machineDir string
|
machineDir string
|
||||||
|
@ -253,6 +259,10 @@ var Commands = []cli.Command{
|
||||||
Name: "swarm",
|
Name: "swarm",
|
||||||
Usage: "Display the Swarm config instead of the Docker daemon",
|
Usage: "Display the Swarm config instead of the Docker daemon",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "shell",
|
||||||
|
Usage: "Force environment to be configured for specified shell",
|
||||||
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "unset, u",
|
Name: "unset, u",
|
||||||
Usage: "Unset variables instead of setting them",
|
Usage: "Unset variables instead of setting them",
|
||||||
|
@ -669,3 +679,19 @@ func getCertPathInfo(c *cli.Context) libmachine.CertPathInfo {
|
||||||
ClientKeyPath: clientKeyPath,
|
ClientKeyPath: clientKeyPath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func detectShell() (string, error) {
|
||||||
|
// attempt to get the SHELL env var
|
||||||
|
shell := filepath.Base(os.Getenv("SHELL"))
|
||||||
|
// none detected; check for windows env
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
log.Printf("On Windows, please specify either 'cmd' or 'powershell' with the --shell flag.\n\n")
|
||||||
|
return "", ErrUnknownShell
|
||||||
|
}
|
||||||
|
|
||||||
|
if shell == "" {
|
||||||
|
return "", ErrUnknownShell
|
||||||
|
}
|
||||||
|
|
||||||
|
return shell, nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
@ -96,21 +95,8 @@ func cmdCreate(c *cli.Context) {
|
||||||
log.Fatalf("error setting active host: %v", err)
|
log.Fatalf("error setting active host: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
info := ""
|
info := fmt.Sprintf("%s env %s", c.App.Name, name)
|
||||||
userShell := filepath.Base(os.Getenv("SHELL"))
|
log.Infof("To point your Docker client at it, run this in your shell: %s", info)
|
||||||
|
|
||||||
switch userShell {
|
|
||||||
case "fish":
|
|
||||||
info = fmt.Sprintf("%s env %s | source", c.App.Name, name)
|
|
||||||
default:
|
|
||||||
info = fmt.Sprintf(`eval "$(%s env %s)"`, c.App.Name, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("%q has been created and is now the active machine.", name)
|
|
||||||
|
|
||||||
if info != "" {
|
|
||||||
log.Infof("To point your Docker client at it, run this in your shell: %s", info)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user has specified a driver, they should not see the flags for all
|
// If the user has specified a driver, they should not see the flags for all
|
||||||
|
|
120
commands/env.go
120
commands/env.go
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
|
||||||
|
@ -13,14 +13,72 @@ import (
|
||||||
"github.com/docker/machine/utils"
|
"github.com/docker/machine/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envTmpl = `{{ .Prefix }}DOCKER_TLS_VERIFY{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}DOCKER_HOST{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}DOCKER_CERT_PATH{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ .UsageHint }}`
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShellConfig struct {
|
||||||
|
Prefix string
|
||||||
|
Delimiter string
|
||||||
|
Suffix string
|
||||||
|
DockerCertPath string
|
||||||
|
DockerHost string
|
||||||
|
DockerTLSVerify string
|
||||||
|
UsageHint string
|
||||||
|
}
|
||||||
|
|
||||||
func cmdEnv(c *cli.Context) {
|
func cmdEnv(c *cli.Context) {
|
||||||
userShell := filepath.Base(os.Getenv("SHELL"))
|
userShell := c.String("shell")
|
||||||
|
if userShell == "" {
|
||||||
|
shell, err := detectShell()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
userShell = shell
|
||||||
|
}
|
||||||
|
|
||||||
|
t := template.New("envConfig")
|
||||||
|
|
||||||
|
usageHint := generateUsageHint(c.App.Name, c.Args().First(), userShell)
|
||||||
|
|
||||||
|
shellCfg := ShellConfig{
|
||||||
|
DockerCertPath: "",
|
||||||
|
DockerHost: "",
|
||||||
|
DockerTLSVerify: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// unset vars
|
||||||
if c.Bool("unset") {
|
if c.Bool("unset") {
|
||||||
switch userShell {
|
switch userShell {
|
||||||
case "fish":
|
case "fish":
|
||||||
fmt.Printf("set -e DOCKER_TLS_VERIFY;\nset -e DOCKER_CERT_PATH;\nset -e DOCKER_HOST;\n")
|
shellCfg.Prefix = "set -e "
|
||||||
|
shellCfg.Delimiter = ""
|
||||||
|
shellCfg.Suffix = ";\n"
|
||||||
|
case "powershell":
|
||||||
|
shellCfg.Prefix = "Remove-Item Env:\\\\"
|
||||||
|
shellCfg.Delimiter = ""
|
||||||
|
shellCfg.Suffix = "\n"
|
||||||
|
case "cmd":
|
||||||
|
// since there is no way to unset vars in cmd just reset to empty
|
||||||
|
shellCfg.DockerCertPath = ""
|
||||||
|
shellCfg.DockerHost = ""
|
||||||
|
shellCfg.DockerTLSVerify = ""
|
||||||
|
shellCfg.Prefix = "set "
|
||||||
|
shellCfg.Delimiter = "="
|
||||||
|
shellCfg.Suffix = "\n"
|
||||||
default:
|
default:
|
||||||
fmt.Println("unset DOCKER_TLS_VERIFY DOCKER_CERT_PATH DOCKER_HOST")
|
shellCfg.Prefix = "unset "
|
||||||
|
shellCfg.Delimiter = " "
|
||||||
|
shellCfg.Suffix = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := t.Parse(envTmpl)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpl.Execute(os.Stdout, shellCfg); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -31,7 +89,7 @@ func cmdEnv(c *cli.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.machineUrl == "" {
|
if cfg.machineUrl == "" {
|
||||||
log.Fatalf("%s is not running. Please start this with docker-machine start %s", cfg.machineName, cfg.machineName)
|
log.Fatalf("%s is not running. Please start this with %s start %s", cfg.machineName, c.App.Name, cfg.machineName)
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerHost := cfg.machineUrl
|
dockerHost := cfg.machineUrl
|
||||||
|
@ -83,32 +141,64 @@ func cmdEnv(c *cli.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usageHint := generateUsageHint(c.Args().First(), userShell)
|
shellCfg = ShellConfig{
|
||||||
|
DockerCertPath: cfg.machineDir,
|
||||||
|
DockerHost: dockerHost,
|
||||||
|
DockerTLSVerify: "1",
|
||||||
|
UsageHint: usageHint,
|
||||||
|
}
|
||||||
|
|
||||||
switch userShell {
|
switch userShell {
|
||||||
case "fish":
|
case "fish":
|
||||||
fmt.Printf("set -x DOCKER_TLS_VERIFY 1;\nset -x DOCKER_CERT_PATH %q;\nset -x DOCKER_HOST %s;\n\n%s\n",
|
shellCfg.Prefix = "set -x "
|
||||||
cfg.machineDir, dockerHost, usageHint)
|
shellCfg.Suffix = "\";\n"
|
||||||
|
shellCfg.Delimiter = " \""
|
||||||
|
case "powershell":
|
||||||
|
shellCfg.Prefix = "$Env:"
|
||||||
|
shellCfg.Suffix = "\"\n"
|
||||||
|
shellCfg.Delimiter = " = \""
|
||||||
|
case "cmd":
|
||||||
|
shellCfg.Prefix = "set "
|
||||||
|
shellCfg.Suffix = "\n"
|
||||||
|
shellCfg.Delimiter = "="
|
||||||
default:
|
default:
|
||||||
fmt.Printf("export DOCKER_TLS_VERIFY=1\nexport DOCKER_CERT_PATH=%q\nexport DOCKER_HOST=%s\n\n%s\n",
|
shellCfg.Prefix = "export "
|
||||||
cfg.machineDir, dockerHost, usageHint)
|
shellCfg.Suffix = "\"\n"
|
||||||
|
shellCfg.Delimiter = "=\""
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := t.Parse(envTmpl)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpl.Execute(os.Stdout, shellCfg); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateUsageHint(machineName string, userShell string) string {
|
func generateUsageHint(appName, machineName, userShell string) string {
|
||||||
cmd := ""
|
cmd := ""
|
||||||
switch userShell {
|
switch userShell {
|
||||||
case "fish":
|
case "fish":
|
||||||
if machineName != "" {
|
if machineName != "" {
|
||||||
cmd = fmt.Sprintf("eval (docker-machine env %s)", machineName)
|
cmd = fmt.Sprintf("eval (%s env %s)", appName, machineName)
|
||||||
} else {
|
} else {
|
||||||
cmd = "eval (docker-machine env)"
|
cmd = fmt.Sprintf("eval (%s env)", appName)
|
||||||
}
|
}
|
||||||
|
case "powershell":
|
||||||
|
if machineName != "" {
|
||||||
|
cmd = fmt.Sprintf("%s env --shell=powershell %s | Invoke-Expression", appName, machineName)
|
||||||
|
} else {
|
||||||
|
cmd = fmt.Sprintf("%s env --shell=powershell | Invoke-Expression", appName)
|
||||||
|
}
|
||||||
|
case "cmd":
|
||||||
|
cmd = "copy and paste the above values into your command prompt"
|
||||||
default:
|
default:
|
||||||
if machineName != "" {
|
if machineName != "" {
|
||||||
cmd = fmt.Sprintf("eval \"$(docker-machine env %s)\"", machineName)
|
cmd = fmt.Sprintf("eval \"$(%s env %s)\"", appName, machineName)
|
||||||
} else {
|
} else {
|
||||||
cmd = "eval \"$(docker-machine env)\""
|
cmd = fmt.Sprintf("eval \"$(%s env)\"", appName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,9 @@ func TestCmdEnvBash(t *testing.T) {
|
||||||
|
|
||||||
set := flag.NewFlagSet("config", 0)
|
set := flag.NewFlagSet("config", 0)
|
||||||
c := cli.NewContext(nil, set, set)
|
c := cli.NewContext(nil, set, set)
|
||||||
|
c.App = &cli.App{
|
||||||
|
Name: "docker-machine-test",
|
||||||
|
}
|
||||||
cmdEnv(c)
|
cmdEnv(c)
|
||||||
|
|
||||||
w.Close()
|
w.Close()
|
||||||
|
@ -103,9 +106,9 @@ func TestCmdEnvBash(t *testing.T) {
|
||||||
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
|
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"DOCKER_TLS_VERIFY": "1",
|
"DOCKER_TLS_VERIFY": "\"1\"",
|
||||||
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
|
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
|
||||||
"DOCKER_HOST": "unix:///var/run/docker.sock",
|
"DOCKER_HOST": "\"unix:///var/run/docker.sock\"",
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range envvars {
|
for k, v := range envvars {
|
||||||
|
@ -181,6 +184,9 @@ func TestCmdEnvFish(t *testing.T) {
|
||||||
|
|
||||||
set := flag.NewFlagSet("config", 0)
|
set := flag.NewFlagSet("config", 0)
|
||||||
c := cli.NewContext(nil, set, set)
|
c := cli.NewContext(nil, set, set)
|
||||||
|
c.App = &cli.App{
|
||||||
|
Name: "docker-machine-test",
|
||||||
|
}
|
||||||
cmdEnv(c)
|
cmdEnv(c)
|
||||||
|
|
||||||
w.Close()
|
w.Close()
|
||||||
|
@ -201,9 +207,111 @@ func TestCmdEnvFish(t *testing.T) {
|
||||||
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
|
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"DOCKER_TLS_VERIFY": "1",
|
"DOCKER_TLS_VERIFY": "\"1\"",
|
||||||
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
|
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
|
||||||
"DOCKER_HOST": "unix:///var/run/docker.sock",
|
"DOCKER_HOST": "\"unix:///var/run/docker.sock\"",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range envvars {
|
||||||
|
if v != expected[k] {
|
||||||
|
t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCmdEnvPowerShell(t *testing.T) {
|
||||||
|
stdout := os.Stdout
|
||||||
|
shell := os.Getenv("SHELL")
|
||||||
|
r, w, _ := os.Pipe()
|
||||||
|
|
||||||
|
os.Stdout = w
|
||||||
|
os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir)
|
||||||
|
os.Setenv("SHELL", "")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
os.Setenv("MACHINE_STORAGE_PATH", "")
|
||||||
|
os.Setenv("SHELL", shell)
|
||||||
|
os.Stdout = stdout
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := clearHosts(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := getTestDriverFlags()
|
||||||
|
|
||||||
|
store, sErr := getTestStore()
|
||||||
|
if sErr != nil {
|
||||||
|
t.Fatal(sErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
mcn, err := libmachine.New(store)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostOptions := &libmachine.HostOptions{
|
||||||
|
EngineOptions: &engine.EngineOptions{},
|
||||||
|
SwarmOptions: &swarm.SwarmOptions{
|
||||||
|
Master: false,
|
||||||
|
Discovery: "",
|
||||||
|
Address: "",
|
||||||
|
Host: "",
|
||||||
|
},
|
||||||
|
AuthOptions: &auth.AuthOptions{},
|
||||||
|
}
|
||||||
|
|
||||||
|
host, err := mcn.Create("test-a", "none", hostOptions, flags)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
host, err = mcn.Get("test-a")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error loading host: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mcn.SetActive(host); err != nil {
|
||||||
|
t.Fatalf("error setting active host: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outStr := make(chan string)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var testOutput bytes.Buffer
|
||||||
|
io.Copy(&testOutput, r)
|
||||||
|
outStr <- testOutput.String()
|
||||||
|
}()
|
||||||
|
|
||||||
|
set := flag.NewFlagSet("config", 0)
|
||||||
|
set.String("shell", "powershell", "")
|
||||||
|
c := cli.NewContext(nil, set, set)
|
||||||
|
c.App = &cli.App{
|
||||||
|
Name: "docker-machine-test",
|
||||||
|
}
|
||||||
|
cmdEnv(c)
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
out := <-outStr
|
||||||
|
|
||||||
|
// parse the output into a map of envvar:value for easier testing below
|
||||||
|
envvars := make(map[string]string)
|
||||||
|
for _, e := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||||
|
if !strings.HasPrefix(e, "$Env") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kv := strings.SplitN(e, " = ", 2)
|
||||||
|
key, value := kv[0], kv[1]
|
||||||
|
envvars[strings.Replace(key, "$Env:", "", 1)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
|
||||||
|
|
||||||
|
expected := map[string]string{
|
||||||
|
"DOCKER_TLS_VERIFY": "\"1\"",
|
||||||
|
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
|
||||||
|
"DOCKER_HOST": "\"unix:///var/run/docker.sock\"",
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range envvars {
|
for k, v := range envvars {
|
||||||
|
|
|
@ -423,7 +423,7 @@ func (d *Driver) Remove() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if s == state.Running {
|
if s == state.Running {
|
||||||
if err := d.Kill(); err != nil {
|
if err := d.Stop(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue