未验证 提交 c847979c 编写于 作者: R Rod Cloutier 提交者: Jimmi Dyson

docker-env command: Added support for multiple shells with no-proxy and unset flags

上级 e88306d1
......@@ -815,6 +815,11 @@
"Comment": "v0.7.0-62-g6002b41",
"Rev": "6002b411ce820eaf03ac972a7fb354bb56f7aa95"
},
{
"ImportPath": "github.com/docker/machine/libmachine/shell",
"Comment": "v0.7.0-62-g6002b41",
"Rev": "6002b411ce820eaf03ac972a7fb354bb56f7aa95"
},
{
"ImportPath": "github.com/docker/machine/libmachine/ssh",
"Comment": "v0.7.0-62-g6002b41",
......
......@@ -14,47 +14,251 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Part of this code is heavily inspired/copied by the following file:
// github.com/docker/machine/commands/env.go
package cmd
import (
"fmt"
"os"
"strings"
"text/template"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/shell"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/constants"
)
const (
envTmpl = `{{ .Prefix }}DOCKER_TLS_VERIFY{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}DOCKER_HOST{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}DOCKER_CERT_PATH{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ if .NoProxyVar }}{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}{{end}}{{ .UsageHint }}`
)
type ShellConfig struct {
Prefix string
Delimiter string
Suffix string
DockerCertPath string
DockerHost string
DockerTLSVerify string
UsageHint string
NoProxyVar string
NoProxyValue string
}
var (
noProxy bool
forceShell string
unset bool
)
func generateUsageHint(userShell string) string {
cmd := ""
comment := "#"
commandLine := "minikube docker-env"
switch userShell {
case "fish":
cmd = fmt.Sprintf("eval (%s)", commandLine)
case "powershell":
cmd = fmt.Sprintf("& %s | Invoke-Expression", commandLine)
case "cmd":
cmd = fmt.Sprintf("\t@FOR /f \"tokens=*\" %%i IN ('%s') DO @%%i", commandLine)
comment = "REM"
case "emacs":
cmd = fmt.Sprintf("(with-temp-buffer (shell-command \"%s\" (current-buffer)) (eval-buffer))", commandLine)
comment = ";;"
default:
cmd = fmt.Sprintf("eval $(%s)", commandLine)
}
return fmt.Sprintf("%s Run this command to configure your shell: \n%s %s\n", comment, comment, cmd)
}
func shellCfgSet(api libmachine.API) (*ShellConfig, error) {
envMap, err := cluster.GetHostDockerEnv(api)
if err != nil {
return nil, err
}
userShell, err := getShell(forceShell)
if err != nil {
return nil, err
}
shellCfg := &ShellConfig{
DockerCertPath: envMap["DOCKER_CERT_PATH"],
DockerHost: envMap["DOCKER_HOST"],
DockerTLSVerify: envMap["DOCKER_TLS_VERIFY"],
UsageHint: generateUsageHint(userShell),
}
if noProxy {
host, err := api.Load(constants.MachineName)
if err != nil {
return nil, fmt.Errorf("Error getting IP: ", err)
}
ip, err := host.Driver.GetIP()
if err != nil {
return nil, fmt.Errorf("Error getting host IP: %s", err)
}
noProxyVar, noProxyValue := findNoProxyFromEnv()
// add the docker host to the no_proxy list idempotently
switch {
case noProxyValue == "":
noProxyValue = ip
case strings.Contains(noProxyValue, ip):
//ip already in no_proxy list, nothing to do
default:
noProxyValue = fmt.Sprintf("%s,%s", noProxyValue, ip)
}
shellCfg.NoProxyVar = noProxyVar
shellCfg.NoProxyValue = noProxyValue
}
switch userShell {
case "fish":
shellCfg.Prefix = "set -gx "
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 = "="
case "emacs":
shellCfg.Prefix = "(setenv \""
shellCfg.Suffix = "\")\n"
shellCfg.Delimiter = "\" \""
default:
shellCfg.Prefix = "export "
shellCfg.Suffix = "\"\n"
shellCfg.Delimiter = "=\""
}
return shellCfg, nil
}
func shellCfgUnset(api libmachine.API) (*ShellConfig, error) {
userShell, err := getShell(forceShell)
if err != nil {
return nil, err
}
shellCfg := &ShellConfig{
UsageHint: generateUsageHint(userShell),
}
if noProxy {
shellCfg.NoProxyVar, shellCfg.NoProxyValue = findNoProxyFromEnv()
}
switch userShell {
case "fish":
shellCfg.Prefix = "set -e "
shellCfg.Suffix = ";\n"
shellCfg.Delimiter = ""
case "powershell":
shellCfg.Prefix = `Remove-Item Env:\\`
shellCfg.Suffix = "\n"
shellCfg.Delimiter = ""
case "cmd":
shellCfg.Prefix = "SET "
shellCfg.Suffix = "\n"
shellCfg.Delimiter = "="
case "emacs":
shellCfg.Prefix = "(setenv \""
shellCfg.Suffix = ")\n"
shellCfg.Delimiter = "\" nil"
default:
shellCfg.Prefix = "unset "
shellCfg.Suffix = "\n"
shellCfg.Delimiter = ""
}
return shellCfg, nil
}
func executeTemplateStdout(shellCfg *ShellConfig) error {
t := template.New("envConfig")
tmpl, err := t.Parse(envTmpl)
if err != nil {
return err
}
return tmpl.Execute(os.Stdout, shellCfg)
}
func getShell(userShell string) (string, error) {
if userShell != "" {
return userShell, nil
}
return shell.Detect()
}
func findNoProxyFromEnv() (string, string) {
// first check for an existing lower case no_proxy var
noProxyVar := "no_proxy"
noProxyValue := os.Getenv("no_proxy")
// otherwise default to allcaps HTTP_PROXY
if noProxyValue == "" {
noProxyVar = "NO_PROXY"
noProxyValue = os.Getenv("NO_PROXY")
}
return noProxyVar, noProxyValue
}
// envCmd represents the docker-env command
var dockerEnvCmd = &cobra.Command{
Use: "docker-env",
Short: "sets up docker env variables; similar to '$(docker-machine env)'",
Long: `sets up docker env variables; similar to '$(docker-machine env)'`,
Run: func(cmd *cobra.Command, args []string) {
api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs"))
defer api.Close()
envMap, err := cluster.GetHostDockerEnv(api)
if err != nil {
glog.Errorln("Error setting machine env variable(s):", err)
os.Exit(1)
var (
err error
shellCfg *ShellConfig
)
if unset {
shellCfg, err = shellCfgUnset(api)
if err != nil {
glog.Errorln("Error setting machine env variable(s):", err)
os.Exit(1)
}
} else {
shellCfg, err = shellCfgSet(api)
if err != nil {
glog.Errorln("Error setting machine env variable(s):", err)
os.Exit(1)
}
}
fmt.Fprintln(os.Stdout, buildDockerEnvShellOutput(envMap))
},
}
func buildDockerEnvShellOutput(envMap map[string]string) string {
output := ""
for env_name, env_val := range envMap {
output += fmt.Sprintf("export %s=%s\n", env_name, env_val)
}
howToRun := "# Run this command to configure your shell: \n# eval $(minikube docker-env)"
output += howToRun
return output
executeTemplateStdout(shellCfg)
},
}
func init() {
RootCmd.AddCommand(dockerEnvCmd)
dockerEnvCmd.Flags().BoolVar(&noProxy, "no-proxy", false, "Add machine IP to NO_PROXY environment variable")
dockerEnvCmd.Flags().StringVar(&forceShell, "shell", "", "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect")
dockerEnvCmd.Flags().BoolVarP(&unset, "unset", "u", false, "Unset variables instead of setting them")
}
......@@ -11,6 +11,14 @@ sets up docker env variables; similar to '$(docker-machine env)'
minikube docker-env
```
### Options
```
--no-proxy[=false]: Add machine IP to NO_PROXY environment variable
--shell="": Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect
-u, --unset[=false]: Unset variables instead of setting them
```
### Options inherited from parent commands
```
......
// +build !windows
package shell
import (
"errors"
"fmt"
"os"
"path/filepath"
)
var (
ErrUnknownShell = errors.New("Error: Unknown shell")
)
// Detect detects user's current shell.
func Detect() (string, error) {
shell := os.Getenv("SHELL")
if shell == "" {
fmt.Printf("The default lines below are for a sh/bash shell, you can specify the shell you're using, with the --shell flag.\n\n")
return "", ErrUnknownShell
}
if os.Getenv("__fish_bin_dir") != "" {
return "fish", nil
}
return filepath.Base(shell), nil
}
package shell
import (
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
"unsafe"
)
// re-implementation of private function in https://github.com/golang/go/blob/master/src/syscall/syscall_windows.go#L945
func getProcessEntry(pid int) (pe *syscall.ProcessEntry32, err error) {
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
if err != nil {
return nil, err
}
defer syscall.CloseHandle(syscall.Handle(snapshot))
var processEntry syscall.ProcessEntry32
processEntry.Size = uint32(unsafe.Sizeof(processEntry))
err = syscall.Process32First(snapshot, &processEntry)
if err != nil {
return nil, err
}
for {
if processEntry.ProcessID == uint32(pid) {
pe = &processEntry
return
}
err = syscall.Process32Next(snapshot, &processEntry)
if err != nil {
return nil, err
}
}
}
// getNameAndItsPpid returns the exe file name its parent process id.
func getNameAndItsPpid(pid int) (exefile string, parentid int, err error) {
pe, err := getProcessEntry(pid)
if err != nil {
return "", 0, err
}
name := syscall.UTF16ToString(pe.ExeFile[:])
return name, int(pe.ParentProcessID), nil
}
func Detect() (string, error) {
shell := os.Getenv("SHELL")
if shell == "" {
shell, shellppid, err := getNameAndItsPpid(os.Getppid())
if err != nil {
return "cmd", err // defaulting to cmd
}
if strings.Contains(strings.ToLower(shell), "powershell") {
return "powershell", nil
} else if strings.Contains(strings.ToLower(shell), "cmd") {
return "cmd", nil
} else {
shell, _, err := getNameAndItsPpid(shellppid)
if err != nil {
return "cmd", err // defaulting to cmd
}
if strings.Contains(strings.ToLower(shell), "powershell") {
return "powershell", nil
} else if strings.Contains(strings.ToLower(shell), "cmd") {
return "cmd", nil
} else {
fmt.Printf("You can further specify your shell with either 'cmd' or 'powershell' with the --shell flag.\n\n")
return "cmd", nil // this could be either powershell or cmd, defaulting to cmd
}
}
}
if os.Getenv("__fish_bin_dir") != "" {
return "fish", nil
}
return filepath.Base(shell), nil
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册