Skip to content
This repository has been archived by the owner on Sep 26, 2021. It is now read-only.

shell selection for env #1033

Merged
merged 9 commits into from
Apr 30, 2015
26 changes: 26 additions & 0 deletions commands/commands.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package commands

import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"sort"
"strings"

Expand Down Expand Up @@ -33,6 +35,10 @@ import (
"github.com/docker/machine/utils"
)

var (
ErrUnknownShell = errors.New("unknown shell")
)

type machineConfig struct {
machineName string
machineDir string
Expand Down Expand Up @@ -228,6 +234,10 @@ var Commands = []cli.Command{
Name: "swarm",
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{
Name: "unset, u",
Usage: "Unset variables instead of setting them",
Expand Down Expand Up @@ -644,3 +654,19 @@ func getCertPathInfo(c *cli.Context) libmachine.CertPathInfo {
ClientKeyPath: clientKeyPath,
}
}

func detectShell() (string, error) {
// attempt to get the SHELL env var
shell := filepath.Base(os.Getenv("SHELL"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this wrapped in filepath.Base so that we get the value we expect in case SHELL contains something like /usr/bin/zsh? Just making sure I understand correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct -- this is the current behavior today.

// 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
}
18 changes: 2 additions & 16 deletions commands/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package commands

import (
"fmt"
"os"
"path/filepath"

log "github.com/Sirupsen/logrus"
Expand Down Expand Up @@ -89,21 +88,8 @@ func cmdCreate(c *cli.Context) {
log.Fatalf("error setting active host: %v", err)
}

info := ""
userShell := filepath.Base(os.Getenv("SHELL"))

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)
}
info := fmt.Sprintf("%s env %s", c.App.Name, name)
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
Expand Down
120 changes: 105 additions & 15 deletions commands/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,81 @@ import (
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"text/template"

log "github.com/Sirupsen/logrus"

"github.com/codegangsta/cli"
"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) {
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") {
switch userShell {
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:
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
}
Expand All @@ -31,7 +89,7 @@ func cmdEnv(c *cli.Context) {
}

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
Expand Down Expand Up @@ -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 {
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",
cfg.machineDir, dockerHost, usageHint)
shellCfg.Prefix = "set -x "
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:
fmt.Printf("export DOCKER_TLS_VERIFY=1\nexport DOCKER_CERT_PATH=%q\nexport DOCKER_HOST=%s\n\n%s\n",
cfg.machineDir, dockerHost, usageHint)
shellCfg.Prefix = "export "
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 := ""
switch userShell {
case "fish":
if machineName != "" {
cmd = fmt.Sprintf("eval (docker-machine env %s)", machineName)
cmd = fmt.Sprintf("eval (%s env %s)", appName, machineName)
} else {
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 = "eval (docker-machine env)"
cmd = fmt.Sprintf("%s env --shell=powershell | Invoke-Expression", appName)
}
case "cmd":
cmd = "copy and paste the above values into your command prompt"
default:
if machineName != "" {
cmd = fmt.Sprintf("eval \"$(docker-machine env %s)\"", machineName)
cmd = fmt.Sprintf("eval \"$(%s env %s)\"", appName, machineName)
} else {
cmd = "eval \"$(docker-machine env)\""
cmd = fmt.Sprintf("eval \"$(%s env)\"", appName)
}
}

Expand Down
Loading