Skip to content
This repository was archived by the owner on Dec 17, 2021. It is now read-only.

Commit 72f2dde

Browse files
committed
commands/env: Add
1 parent 4b07730 commit 72f2dde

File tree

2 files changed

+963
-0
lines changed

2 files changed

+963
-0
lines changed

commands/env.go

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
package commands
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"runtime"
9+
"strings"
10+
"text/template"
11+
12+
"github.com/docker/machine/commands/mcndirs"
13+
"github.com/docker/machine/libmachine"
14+
"github.com/docker/machine/libmachine/check"
15+
"github.com/docker/machine/libmachine/log"
16+
"github.com/docker/machine/libmachine/shell"
17+
)
18+
19+
const (
20+
envTmpl = `{{ .Prefix }}DOCKER_TLS_VERIFY{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}DOCKER_HOST{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}DOCKER_CERT_PATH{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ .Prefix }}DOCKER_MACHINE_NAME{{ .Delimiter }}{{ .MachineName }}{{ .Suffix }}{{ if .ComposePathsVar }}{{ .Prefix }}COMPOSE_CONVERT_WINDOWS_PATHS{{ .Delimiter }}true{{ .Suffix }}{{end}}{{ if .NoProxyVar }}{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}{{end}}{{ .UsageHint }}`
21+
)
22+
23+
var (
24+
errImproperUnsetEnvArgs = errors.New("Error: Expected no machine name when the -u flag is present")
25+
defaultUsageHinter UsageHintGenerator
26+
runtimeOS = func() string { return runtime.GOOS }
27+
)
28+
29+
func init() {
30+
defaultUsageHinter = &EnvUsageHintGenerator{}
31+
}
32+
33+
type ShellConfig struct {
34+
Prefix string
35+
Delimiter string
36+
Suffix string
37+
DockerCertPath string
38+
DockerHost string
39+
DockerTLSVerify string
40+
UsageHint string
41+
MachineName string
42+
NoProxyVar string
43+
NoProxyValue string
44+
ComposePathsVar bool
45+
}
46+
47+
func cmdEnv(c CommandLine, api libmachine.API) error {
48+
var (
49+
err error
50+
shellCfg *ShellConfig
51+
)
52+
53+
// Ensure that log messages always go to stderr when this command is
54+
// being run (it is intended to be run in a subshell)
55+
log.SetOutWriter(os.Stderr)
56+
57+
if c.Bool("unset") {
58+
shellCfg, err = shellCfgUnset(c, api)
59+
if err != nil {
60+
return err
61+
}
62+
} else {
63+
shellCfg, err = shellCfgSet(c, api)
64+
if err != nil {
65+
return err
66+
}
67+
}
68+
69+
return executeTemplateStdout(shellCfg)
70+
}
71+
72+
func shellCfgSet(c CommandLine, api libmachine.API) (*ShellConfig, error) {
73+
if len(c.Args()) > 1 {
74+
return nil, ErrExpectedOneMachine
75+
}
76+
77+
target, err := targetHost(c, api)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
host, err := api.Load(target)
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
dockerHost, _, err := check.DefaultConnChecker.Check(host, c.Bool("swarm"))
88+
if err != nil {
89+
return nil, fmt.Errorf("Error checking TLS connection: %s", err)
90+
}
91+
92+
userShell, err := getShell(c.String("shell"))
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
shellCfg := &ShellConfig{
98+
DockerCertPath: filepath.Join(mcndirs.GetMachineDir(), host.Name),
99+
DockerHost: dockerHost,
100+
DockerTLSVerify: "1",
101+
UsageHint: defaultUsageHinter.GenerateUsageHint(userShell, os.Args),
102+
MachineName: host.Name,
103+
}
104+
105+
if c.Bool("no-proxy") {
106+
ip, err := host.Driver.GetIP()
107+
if err != nil {
108+
return nil, fmt.Errorf("Error getting host IP: %s", err)
109+
}
110+
111+
noProxyVar, noProxyValue := findNoProxyFromEnv()
112+
113+
// add the docker host to the no_proxy list idempotently
114+
switch {
115+
case noProxyValue == "":
116+
noProxyValue = ip
117+
case strings.Contains(noProxyValue, ip):
118+
//ip already in no_proxy list, nothing to do
119+
default:
120+
noProxyValue = fmt.Sprintf("%s,%s", noProxyValue, ip)
121+
}
122+
123+
shellCfg.NoProxyVar = noProxyVar
124+
shellCfg.NoProxyValue = noProxyValue
125+
}
126+
127+
if runtimeOS() == "windows" {
128+
shellCfg.ComposePathsVar = true
129+
}
130+
131+
switch userShell {
132+
case "fish":
133+
shellCfg.Prefix = "set -gx "
134+
shellCfg.Suffix = "\";\n"
135+
shellCfg.Delimiter = " \""
136+
case "powershell":
137+
shellCfg.Prefix = "$Env:"
138+
shellCfg.Suffix = "\"\n"
139+
shellCfg.Delimiter = " = \""
140+
case "cmd":
141+
shellCfg.Prefix = "SET "
142+
shellCfg.Suffix = "\n"
143+
shellCfg.Delimiter = "="
144+
case "tcsh":
145+
shellCfg.Prefix = "setenv "
146+
shellCfg.Suffix = "\";\n"
147+
shellCfg.Delimiter = " \""
148+
case "emacs":
149+
shellCfg.Prefix = "(setenv \""
150+
shellCfg.Suffix = "\")\n"
151+
shellCfg.Delimiter = "\" \""
152+
default:
153+
shellCfg.Prefix = "export "
154+
shellCfg.Suffix = "\"\n"
155+
shellCfg.Delimiter = "=\""
156+
}
157+
158+
return shellCfg, nil
159+
}
160+
161+
func shellCfgUnset(c CommandLine, api libmachine.API) (*ShellConfig, error) {
162+
if len(c.Args()) != 0 {
163+
return nil, errImproperUnsetEnvArgs
164+
}
165+
166+
userShell, err := getShell(c.String("shell"))
167+
if err != nil {
168+
return nil, err
169+
}
170+
171+
shellCfg := &ShellConfig{
172+
UsageHint: defaultUsageHinter.GenerateUsageHint(userShell, os.Args),
173+
}
174+
175+
if c.Bool("no-proxy") {
176+
shellCfg.NoProxyVar, shellCfg.NoProxyValue = findNoProxyFromEnv()
177+
}
178+
179+
switch userShell {
180+
case "fish":
181+
shellCfg.Prefix = "set -e "
182+
shellCfg.Suffix = ";\n"
183+
shellCfg.Delimiter = ""
184+
case "powershell":
185+
shellCfg.Prefix = `Remove-Item Env:\\`
186+
shellCfg.Suffix = "\n"
187+
shellCfg.Delimiter = ""
188+
case "cmd":
189+
shellCfg.Prefix = "SET "
190+
shellCfg.Suffix = "\n"
191+
shellCfg.Delimiter = "="
192+
case "emacs":
193+
shellCfg.Prefix = "(setenv \""
194+
shellCfg.Suffix = ")\n"
195+
shellCfg.Delimiter = "\" nil"
196+
case "tcsh":
197+
shellCfg.Prefix = "unsetenv "
198+
shellCfg.Suffix = ";\n"
199+
shellCfg.Delimiter = ""
200+
default:
201+
shellCfg.Prefix = "unset "
202+
shellCfg.Suffix = "\n"
203+
shellCfg.Delimiter = ""
204+
}
205+
206+
return shellCfg, nil
207+
}
208+
209+
func executeTemplateStdout(shellCfg *ShellConfig) error {
210+
t := template.New("envConfig")
211+
tmpl, err := t.Parse(envTmpl)
212+
if err != nil {
213+
return err
214+
}
215+
216+
return tmpl.Execute(os.Stdout, shellCfg)
217+
}
218+
219+
func getShell(userShell string) (string, error) {
220+
if userShell != "" {
221+
return userShell, nil
222+
}
223+
return shell.Detect()
224+
}
225+
226+
func findNoProxyFromEnv() (string, string) {
227+
// first check for an existing lower case no_proxy var
228+
noProxyVar := "no_proxy"
229+
noProxyValue := os.Getenv("no_proxy")
230+
231+
// otherwise default to allcaps HTTP_PROXY
232+
if noProxyValue == "" {
233+
noProxyVar = "NO_PROXY"
234+
noProxyValue = os.Getenv("NO_PROXY")
235+
}
236+
return noProxyVar, noProxyValue
237+
}
238+
239+
type UsageHintGenerator interface {
240+
GenerateUsageHint(string, []string) string
241+
}
242+
243+
type EnvUsageHintGenerator struct{}
244+
245+
func (g *EnvUsageHintGenerator) GenerateUsageHint(userShell string, args []string) string {
246+
cmd := ""
247+
comment := "#"
248+
249+
dockerMachinePath := args[0]
250+
if strings.Contains(dockerMachinePath, " ") || strings.Contains(dockerMachinePath, `\`) {
251+
args[0] = fmt.Sprintf("\"%s\"", dockerMachinePath)
252+
}
253+
254+
commandLine := strings.Join(args, " ")
255+
256+
switch userShell {
257+
case "fish":
258+
cmd = fmt.Sprintf("eval (%s)", commandLine)
259+
case "powershell":
260+
cmd = fmt.Sprintf("& %s | Invoke-Expression", commandLine)
261+
case "cmd":
262+
cmd = fmt.Sprintf("\t@FOR /f \"tokens=*\" %%i IN ('%s') DO @%%i", commandLine)
263+
comment = "REM"
264+
case "emacs":
265+
cmd = fmt.Sprintf("(with-temp-buffer (shell-command \"%s\" (current-buffer)) (eval-buffer))", commandLine)
266+
comment = ";;"
267+
case "tcsh":
268+
cmd = fmt.Sprintf("eval `%s`", commandLine)
269+
comment = ":"
270+
default:
271+
cmd = fmt.Sprintf("eval $(%s)", commandLine)
272+
}
273+
274+
return fmt.Sprintf("%s Run this command to configure your shell: \n%s %s\n", comment, comment, cmd)
275+
}

0 commit comments

Comments
 (0)