Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add podman-env for connecting with podman-remote #6351

Merged
merged 7 commits into from
Feb 11, 2020
Merged
Prev Previous commit
Next Next commit
Move shell handling functions to separate package
afbjorklund committed Feb 9, 2020
commit e7ea03ecbd02a8eb5afcf50a09109b64c322405a
182 changes: 30 additions & 152 deletions cmd/minikube/cmd/env.go
Original file line number Diff line number Diff line change
@@ -26,10 +26,8 @@ import (
"os"
"strconv"
"strings"
"text/template"

"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/shell"
"github.com/docker/machine/libmachine/state"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -44,70 +42,24 @@ import (
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/shell"
)

var envTmpl = fmt.Sprintf("{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubeDockerdProfile }}{{ .Suffix }}{{ if .NoProxyVar }}{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}{{end}}{{ .UsageHint }}", constants.DockerTLSVerifyEnv, constants.DockerHostEnv, constants.DockerCertPathEnv, constants.MinikubeActiveDockerdEnv)

const (
fishSetPfx = "set -gx "
fishSetSfx = "\"\n"
fishSetDelim = " \""

fishUnsetPfx = "set -e "
fishUnsetSfx = "\n"

psSetPfx = "$Env:"
psSetSfx = "\"\n"
psSetDelim = " = \""

psUnsetPfx = `Remove-Item Env:\\`
psUnsetSfx = "\n"

cmdSetPfx = "SET "
cmdSetSfx = "\n"
cmdSetDelim = "="

cmdUnsetPfx = "SET "
cmdUnsetSfx = "\n"
cmdUnsetDelim = "="

emacsSetPfx = "(setenv \""
emacsSetSfx = "\")\n"
emacsSetDelim = "\" \""

emacsUnsetPfx = "(setenv \""
emacsUnsetSfx = ")\n"
emacsUnsetDelim = "\" nil"

bashSetPfx = "export "
bashSetSfx = "\"\n"
bashSetDelim = "=\""

bashUnsetPfx = "unset "
bashUnsetSfx = "\n"

nonePfx = ""
noneSfx = "\n"
noneDelim = "="
)

// ShellConfig represents the shell config
type ShellConfig struct {
Prefix string
Delimiter string
Suffix string
// DockerShellConfig represents the shell config for Docker
type DockerShellConfig struct {
shell.ShellConfig
DockerCertPath string
DockerHost string
DockerTLSVerify string
MinikubeDockerdProfile string
UsageHint string
NoProxyVar string
NoProxyValue string
}

var (
noProxy bool
forceShell string
unset bool
defaultNoProxyGetter NoProxyGetter
)
@@ -120,40 +72,13 @@ type NoProxyGetter interface {
// EnvNoProxyGetter gets the no_proxy variable, using environment
type EnvNoProxyGetter struct{}

func generateUsageHint(profile, sh string) string {
// shellCfgSet generates context variables for "docker-env"
func shellCfgSet(ec DockerEnvConfig, envMap map[string]string) *DockerShellConfig {
profile := ec.profile
const usgPlz = "To point your shell to minikube's docker-daemon, run:"
var usgCmd = fmt.Sprintf("minikube -p %s docker-env", profile)
var usageHintMap = map[string]string{
"bash": fmt.Sprintf(`
# %s
# eval $(%s)
`, usgPlz, usgCmd),
"fish": fmt.Sprintf(`
# %s
# eval (%s)
`, usgPlz, usgCmd),
"powershell": fmt.Sprintf(`# %s
# & %s | Invoke-Expression
`, usgPlz, usgCmd),
"cmd": fmt.Sprintf(`REM %s
REM @FOR /f "tokens=*" %%i IN ('%s') DO @%%i
`, usgPlz, usgCmd),
"emacs": fmt.Sprintf(`;; %s
;; (with-temp-buffer (shell-command "%s" (current-buffer)) (eval-buffer))
`, usgPlz, usgCmd),
}

hint, ok := usageHintMap[sh]
if !ok {
return usageHintMap["bash"]
}
return hint
}

// shellCfgSet generates context variables for "docker-env"
func shellCfgSet(ec EnvConfig, envMap map[string]string) *ShellConfig {
s := &ShellConfig{
UsageHint: generateUsageHint(ec.profile, ec.shell),
s := &DockerShellConfig{
ShellConfig: *shell.ShellCfgSet(ec.EnvConfig, usgPlz, usgCmd),
}
s.DockerCertPath = envMap[constants.DockerCertPathEnv]
s.DockerHost = envMap[constants.DockerHostEnv]
@@ -177,33 +102,6 @@ func shellCfgSet(ec EnvConfig, envMap map[string]string) *ShellConfig {
s.NoProxyValue = noProxyValue
}

switch ec.shell {
case "fish":
s.Prefix = fishSetPfx
s.Suffix = fishSetSfx
s.Delimiter = fishSetDelim
case "powershell":
s.Prefix = psSetPfx
s.Suffix = psSetSfx
s.Delimiter = psSetDelim
case "cmd":
s.Prefix = cmdSetPfx
s.Suffix = cmdSetSfx
s.Delimiter = cmdSetDelim
case "emacs":
s.Prefix = emacsSetPfx
s.Suffix = emacsSetSfx
s.Delimiter = emacsSetDelim
case "none":
s.Prefix = nonePfx
s.Suffix = noneSfx
s.Delimiter = noneDelim
s.UsageHint = ""
default:
s.Prefix = bashSetPfx
s.Suffix = bashSetSfx
s.Delimiter = bashSetDelim
}
return s
}

@@ -282,17 +180,20 @@ var dockerEnvCmd = &cobra.Command{
exit.WithError("Error getting host IP", err)
}

ec := EnvConfig{
profile: profile,
driver: host.DriverName,
shell: forceShell,
hostIP: hostIP,
certsDir: localpath.MakeMiniPath("certs"),
noProxy: noProxy,
sh := shell.EnvConfig{
Shell: shell.ForceShell,
}
ec := DockerEnvConfig{
EnvConfig: sh,
profile: profile,
driver: host.DriverName,
hostIP: hostIP,
certsDir: localpath.MakeMiniPath("certs"),
noProxy: noProxy,
}

if ec.shell == "" {
ec.shell, err = shell.Detect()
if ec.Shell == "" {
ec.Shell, err = shell.Detect()
if err != nil {
exit.WithError("Error detecting shell", err)
}
@@ -311,28 +212,27 @@ var dockerEnvCmd = &cobra.Command{
},
}

// EnvConfig encapsulates all external inputs into shell generation
type EnvConfig struct {
// DockerEnvConfig encapsulates all external inputs into shell generation for Docker
type DockerEnvConfig struct {
shell.EnvConfig
profile string
shell string
driver string
hostIP string
certsDir string
noProxy bool
}

// setScript writes out a shell-compatible 'docker-env' script
func setScript(ec EnvConfig, w io.Writer) error {
tmpl := template.Must(template.New("envConfig").Parse(envTmpl))
func setScript(ec DockerEnvConfig, w io.Writer) error {
envVars, err := dockerEnvVars(ec)
if err != nil {
return err
}
return tmpl.Execute(w, shellCfgSet(ec, envVars))
return shell.SetScript(ec.EnvConfig, w, envTmpl, shellCfgSet(ec, envVars))
}

// setScript writes out a shell-compatible 'docker-env unset' script
func unsetScript(ec EnvConfig, w io.Writer) error {
func unsetScript(ec DockerEnvConfig, w io.Writer) error {
vars := []string{
constants.DockerTLSVerifyEnv,
constants.DockerHostEnv,
@@ -347,29 +247,7 @@ func unsetScript(ec EnvConfig, w io.Writer) error {
}
}

var sb strings.Builder
switch ec.shell {
case "fish":
for _, v := range vars {
sb.WriteString(fmt.Sprintf("%s%s%s", fishUnsetPfx, v, fishUnsetSfx))
}
case "powershell":
sb.WriteString(fmt.Sprintf("%s%s%s", psUnsetPfx, strings.Join(vars, " Env:\\\\"), psUnsetSfx))
case "cmd":
for _, v := range vars {
sb.WriteString(fmt.Sprintf("%s%s%s%s", cmdUnsetPfx, v, cmdUnsetDelim, cmdUnsetSfx))
}
case "emacs":
for _, v := range vars {
sb.WriteString(fmt.Sprintf("%s%s%s%s", emacsUnsetPfx, v, emacsUnsetDelim, emacsUnsetSfx))
}
case "none":
sb.WriteString(fmt.Sprintf("%s%s%s", nonePfx, strings.Join(vars, " "), noneSfx))
default:
sb.WriteString(fmt.Sprintf("%s%s%s", bashUnsetPfx, strings.Join(vars, " "), bashUnsetSfx))
}
_, err := w.Write([]byte(sb.String()))
return err
return shell.UnsetScript(ec.EnvConfig, w, vars)
}

// dockerURL returns a the docker endpoint URL for an ip/port pair.
@@ -378,7 +256,7 @@ func dockerURL(ip string, port int) string {
}

// dockerEnvVars gets the necessary docker env variables to allow the use of minikube's docker daemon
func dockerEnvVars(ec EnvConfig) (map[string]string, error) {
func dockerEnvVars(ec DockerEnvConfig) (map[string]string, error) {
env := map[string]string{
constants.DockerTLSVerifyEnv: "1",
constants.DockerHostEnv: dockerURL(ec.hostIP, constants.DockerDaemonPort),
@@ -399,6 +277,6 @@ func dockerEnvVars(ec EnvConfig) (map[string]string, error) {
func init() {
defaultNoProxyGetter = &EnvNoProxyGetter{}
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().StringVar(&shell.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")
}
34 changes: 23 additions & 11 deletions cmd/minikube/cmd/env_test.go
Original file line number Diff line number Diff line change
@@ -34,13 +34,15 @@ func (f FakeNoProxyGetter) GetNoProxyVar() (string, string) {

func TestGenerateScripts(t *testing.T) {
var tests = []struct {
config EnvConfig
shell string
config DockerEnvConfig
noProxyGetter *FakeNoProxyGetter
wantSet string
wantUnset string
}{
{
EnvConfig{profile: "bash", shell: "bash", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs"},
"bash",
DockerEnvConfig{profile: "bash", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs"},
nil,
`export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://127.0.0.1:2376"
@@ -54,7 +56,8 @@ export MINIKUBE_ACTIVE_DOCKERD="bash"
`,
},
{
EnvConfig{profile: "ipv6", shell: "bash", driver: "kvm2", hostIP: "fe80::215:5dff:fe00:a903", certsDir: "/certs"},
"bash",
DockerEnvConfig{profile: "ipv6", driver: "kvm2", hostIP: "fe80::215:5dff:fe00:a903", certsDir: "/certs"},
nil,
`export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://[fe80::215:5dff:fe00:a903]:2376"
@@ -68,7 +71,8 @@ export MINIKUBE_ACTIVE_DOCKERD="ipv6"
`,
},
{
EnvConfig{profile: "fish", shell: "fish", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs"},
"fish",
DockerEnvConfig{profile: "fish", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs"},
nil,
`set -gx DOCKER_TLS_VERIFY "1"
set -gx DOCKER_HOST "tcp://127.0.0.1:2376"
@@ -85,7 +89,8 @@ set -e MINIKUBE_ACTIVE_DOCKERD
`,
},
{
EnvConfig{profile: "powershell", shell: "powershell", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs"},
"powershell",
DockerEnvConfig{profile: "powershell", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs"},
nil,
`$Env:DOCKER_TLS_VERIFY = "1"
$Env:DOCKER_HOST = "tcp://192.168.0.1:2376"
@@ -99,7 +104,8 @@ $Env:MINIKUBE_ACTIVE_DOCKERD = "powershell"
`,
},
{
EnvConfig{profile: "cmd", shell: "cmd", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs"},
"cmd",
DockerEnvConfig{profile: "cmd", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs"},
nil,
`SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://192.168.0.1:2376
@@ -116,7 +122,8 @@ SET MINIKUBE_ACTIVE_DOCKERD=
`,
},
{
EnvConfig{profile: "emacs", shell: "emacs", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs"},
"emacs",
DockerEnvConfig{profile: "emacs", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs"},
nil,
`(setenv "DOCKER_TLS_VERIFY" "1")
(setenv "DOCKER_HOST" "tcp://192.168.0.1:2376")
@@ -132,7 +139,8 @@ SET MINIKUBE_ACTIVE_DOCKERD=
`,
},
{
EnvConfig{profile: "bash-no-proxy", shell: "bash", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs", noProxy: true},
"bash",
DockerEnvConfig{profile: "bash-no-proxy", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs", noProxy: true},
&FakeNoProxyGetter{"NO_PROXY", "127.0.0.1"},
`export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://127.0.0.1:2376"
@@ -148,7 +156,8 @@ export NO_PROXY="127.0.0.1"
`,
},
{
EnvConfig{profile: "bash-no-proxy-lower", shell: "bash", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs", noProxy: true},
"bash",
DockerEnvConfig{profile: "bash-no-proxy-lower", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs", noProxy: true},
&FakeNoProxyGetter{"no_proxy", "127.0.0.1"},
`export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://127.0.0.1:2376"
@@ -164,7 +173,8 @@ export no_proxy="127.0.0.1"
`,
},
{
EnvConfig{profile: "powershell-no-proxy-idempotent", shell: "powershell", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs", noProxy: true},
"powershell",
DockerEnvConfig{profile: "powershell-no-proxy-idempotent", driver: "hyperv", hostIP: "192.168.0.1", certsDir: "/certs", noProxy: true},
&FakeNoProxyGetter{"no_proxy", "192.168.0.1"},
`$Env:DOCKER_TLS_VERIFY = "1"
$Env:DOCKER_HOST = "tcp://192.168.0.1:2376"
@@ -179,7 +189,8 @@ $Env:no_proxy = "192.168.0.1"
`,
},
{
EnvConfig{profile: "sh-no-proxy-add", shell: "bash", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs", noProxy: true},
"bash",
DockerEnvConfig{profile: "sh-no-proxy-add", driver: "kvm2", hostIP: "127.0.0.1", certsDir: "/certs", noProxy: true},
&FakeNoProxyGetter{"NO_PROXY", "192.168.0.1,10.0.0.4"},
`export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://127.0.0.1:2376"
@@ -197,6 +208,7 @@ export NO_PROXY="192.168.0.1,10.0.0.4,127.0.0.1"
}
for _, tc := range tests {
t.Run(tc.config.profile, func(t *testing.T) {
tc.config.EnvConfig.Shell = tc.shell
defaultNoProxyGetter = tc.noProxyGetter
var b []byte
buf := bytes.NewBuffer(b)
193 changes: 193 additions & 0 deletions pkg/minikube/shell/shell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
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 shell

import (
"fmt"
"io"
"strings"
"text/template"

"github.com/docker/machine/libmachine/shell"
)

const (
fishSetPfx = "set -gx "
fishSetSfx = "\"\n"
fishSetDelim = " \""

fishUnsetPfx = "set -e "
fishUnsetSfx = "\n"

psSetPfx = "$Env:"
psSetSfx = "\"\n"
psSetDelim = " = \""

psUnsetPfx = `Remove-Item Env:\\`
psUnsetSfx = "\n"

cmdSetPfx = "SET "
cmdSetSfx = "\n"
cmdSetDelim = "="

cmdUnsetPfx = "SET "
cmdUnsetSfx = "\n"
cmdUnsetDelim = "="

emacsSetPfx = "(setenv \""
emacsSetSfx = "\")\n"
emacsSetDelim = "\" \""

emacsUnsetPfx = "(setenv \""
emacsUnsetSfx = ")\n"
emacsUnsetDelim = "\" nil"

bashSetPfx = "export "
bashSetSfx = "\"\n"
bashSetDelim = "=\""

bashUnsetPfx = "unset "
bashUnsetSfx = "\n"

nonePfx = ""
noneSfx = "\n"
noneDelim = "="
)

// ShellConfig represents the shell config
type ShellConfig struct {
Prefix string
Delimiter string
Suffix string
UsageHint string
}

var (
// ForceShell forces a shell name
ForceShell string
unset bool
)

// Detect detects user's current shell.
func Detect() (string, error) {
return shell.Detect()
}

func generateUsageHint(sh, usgPlz, usgCmd string) string {
var usageHintMap = map[string]string{
"bash": fmt.Sprintf(`
# %s
# eval $(%s)
`, usgPlz, usgCmd),
"fish": fmt.Sprintf(`
# %s
# eval (%s)
`, usgPlz, usgCmd),
"powershell": fmt.Sprintf(`# %s
# & %s | Invoke-Expression
`, usgPlz, usgCmd),
"cmd": fmt.Sprintf(`REM %s
REM @FOR /f "tokens=*" %%i IN ('%s') DO @%%i
`, usgPlz, usgCmd),
"emacs": fmt.Sprintf(`;; %s
;; (with-temp-buffer (shell-command "%s" (current-buffer)) (eval-buffer))
`, usgPlz, usgCmd),
}

hint, ok := usageHintMap[sh]
if !ok {
return usageHintMap["bash"]
}
return hint
}

// ShellCfgSet generates context variables for shell
func ShellCfgSet(ec EnvConfig, plz, cmd string) *ShellConfig {
s := &ShellConfig{
UsageHint: generateUsageHint(ec.Shell, plz, cmd),
}

switch ec.Shell {
case "fish":
s.Prefix = fishSetPfx
s.Suffix = fishSetSfx
s.Delimiter = fishSetDelim
case "powershell":
s.Prefix = psSetPfx
s.Suffix = psSetSfx
s.Delimiter = psSetDelim
case "cmd":
s.Prefix = cmdSetPfx
s.Suffix = cmdSetSfx
s.Delimiter = cmdSetDelim
case "emacs":
s.Prefix = emacsSetPfx
s.Suffix = emacsSetSfx
s.Delimiter = emacsSetDelim
case "none":
s.Prefix = nonePfx
s.Suffix = noneSfx
s.Delimiter = noneDelim
s.UsageHint = ""
default:
s.Prefix = bashSetPfx
s.Suffix = bashSetSfx
s.Delimiter = bashSetDelim
}
return s
}

// EnvConfig encapsulates all external inputs into shell generation
type EnvConfig struct {
Shell string
}

// SetScript writes out a shell-compatible set script
func SetScript(ec EnvConfig, w io.Writer, envTmpl string, data interface{}) error {
tmpl := template.Must(template.New("envConfig").Parse(envTmpl))
return tmpl.Execute(w, data)
}

// UnsetScript writes out a shell-compatible unset script
func UnsetScript(ec EnvConfig, w io.Writer, vars []string) error {
var sb strings.Builder
switch ec.Shell {
case "fish":
for _, v := range vars {
sb.WriteString(fmt.Sprintf("%s%s%s", fishUnsetPfx, v, fishUnsetSfx))
}
case "powershell":
sb.WriteString(fmt.Sprintf("%s%s%s", psUnsetPfx, strings.Join(vars, " Env:\\\\"), psUnsetSfx))
case "cmd":
for _, v := range vars {
sb.WriteString(fmt.Sprintf("%s%s%s%s", cmdUnsetPfx, v, cmdUnsetDelim, cmdUnsetSfx))
}
case "emacs":
for _, v := range vars {
sb.WriteString(fmt.Sprintf("%s%s%s%s", emacsUnsetPfx, v, emacsUnsetDelim, emacsUnsetSfx))
}
case "none":
sb.WriteString(fmt.Sprintf("%s%s%s", nonePfx, strings.Join(vars, " "), noneSfx))
default:
sb.WriteString(fmt.Sprintf("%s%s%s", bashUnsetPfx, strings.Join(vars, " "), bashUnsetSfx))
}
_, err := w.Write([]byte(sb.String()))
return err
}