From 716d793990ab8f1ab63183de8d097203205b7fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 23 Oct 2020 11:44:33 +0200 Subject: [PATCH 1/3] Add support for podman v2 to podman-env command Podman v1 (like 1.9.3) uses the varlink bridge, while podman v2 (like 2.1.x) uses a REST API. Both are using ssh for their transport, but the environment variables exported are different. --- cmd/minikube/cmd/podman-env.go | 104 +++++++++++++++++++++++++--- cmd/minikube/cmd/podman-env_test.go | 15 +++- pkg/minikube/constants/constants.go | 4 ++ 3 files changed, 111 insertions(+), 12 deletions(-) diff --git a/cmd/minikube/cmd/podman-env.go b/cmd/minikube/cmd/podman-env.go index 1d4d93c6daab..b444b6c3582f 100644 --- a/cmd/minikube/cmd/podman-env.go +++ b/cmd/minikube/cmd/podman-env.go @@ -39,12 +39,14 @@ import ( "k8s.io/minikube/pkg/minikube/shell" ) -var podmanEnvTmpl = fmt.Sprintf("{{ .Prefix }}%s{{ .Delimiter }}{{ .VarlinkBridge }}{{ .Suffix }}{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubePodmanProfile }}{{ .Suffix }}{{ .UsageHint }}", constants.PodmanVarlinkBridgeEnv, constants.MinikubeActivePodmanEnv) +var podmanEnvTmpl = fmt.Sprintf("{{ if .VarlinkBridge }}{{ .Prefix }}%s{{ .Delimiter }}{{ .VarlinkBridge }}{{ .Suffix }}{{end}}{{ if .ContainerHost }}{{ .Prefix }}%s{{ .Delimiter }}{{ .ContainerHost }}{{ .Suffix }}{{end}}{{ if .ContainerSSHKey }}{{ .Prefix }}%s{{ .Delimiter }}{{ .ContainerSSHKey}}{{ .Suffix }}{{ end }}{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubePodmanProfile }}{{ .Suffix }}{{ .UsageHint }}", constants.PodmanVarlinkBridgeEnv, constants.PodmanContainerHostEnv, constants.PodmanContainerSSHKeyEnv, constants.MinikubeActivePodmanEnv) // PodmanShellConfig represents the shell config for Podman type PodmanShellConfig struct { shell.Config VarlinkBridge string + ContainerHost string + ContainerSSHKey string MinikubePodmanProfile string } @@ -59,17 +61,24 @@ func podmanShellCfgSet(ec PodmanEnvConfig, envMap map[string]string) *PodmanShel Config: *shell.CfgSet(ec.EnvConfig, usgPlz, usgCmd), } s.VarlinkBridge = envMap[constants.PodmanVarlinkBridgeEnv] + s.ContainerHost = envMap[constants.PodmanContainerHostEnv] + s.ContainerSSHKey = envMap[constants.PodmanContainerSSHKeyEnv] s.MinikubePodmanProfile = envMap[constants.MinikubeActivePodmanEnv] return s } -// isPodmanAvailable checks if Podman is available -func isPodmanAvailable(r command.Runner) bool { +// isVarlinkAvailable checks if varlink command is available +func isVarlinkAvailable(r command.Runner) bool { if _, err := r.RunCmd(exec.Command("which", "varlink")); err != nil { return false } + return true +} + +// isPodmanAvailable checks if podman command is available +func isPodmanAvailable(r command.Runner) bool { if _, err := r.RunCmd(exec.Command("which", "podman")); err != nil { return false } @@ -130,11 +139,25 @@ var podmanEnvCmd = &cobra.Command{ exit.Message(reason.Usage, `The podman-env command is incompatible with multi-node clusters. Use the 'registry' add-on: https://minikube.sigs.k8s.io/docs/handbook/registry/`) } - if ok := isPodmanAvailable(co.CP.Runner); !ok { + r := co.CP.Runner + if ok := isPodmanAvailable(r); !ok { exit.Message(reason.EnvPodmanUnavailable, `The podman service within '{{.cluster}}' is not active`, out.V{"cluster": cname}) } - client, err := createExternalSSHClient(co.CP.Host.Driver) + varlink := isVarlinkAvailable(r) + + d := co.CP.Host.Driver + client, err := createExternalSSHClient(d) + if err != nil { + exit.Error(reason.IfSSHClient, "Error getting ssh client", err) + } + + hostname, err := d.GetSSHHostname() + if err != nil { + exit.Error(reason.IfSSHClient, "Error getting ssh client", err) + } + + port, err := d.GetSSHPort() if err != nil { exit.Error(reason.IfSSHClient, "Error getting ssh client", err) } @@ -143,7 +166,12 @@ var podmanEnvCmd = &cobra.Command{ EnvConfig: sh, profile: cname, driver: driverName, + varlink: varlink, client: client, + username: d.GetSSHUsername(), + hostname: hostname, + port: port, + keypath: d.GetSSHKeyPath(), } if ec.Shell == "" { @@ -162,9 +190,14 @@ var podmanEnvCmd = &cobra.Command{ // PodmanEnvConfig encapsulates all external inputs into shell generation for Podman type PodmanEnvConfig struct { shell.EnvConfig - profile string - driver string - client *ssh.ExternalClient + profile string + driver string + varlink bool + client *ssh.ExternalClient + username string + hostname string + port int + keypath string } // podmanSetScript writes out a shell-compatible 'podman-env' script @@ -175,10 +208,34 @@ func podmanSetScript(ec PodmanEnvConfig, w io.Writer) error { // podmanUnsetScript writes out a shell-compatible 'podman-env unset' script func podmanUnsetScript(ec PodmanEnvConfig, w io.Writer) error { - vars := []string{ + // podman v1 + vars1 := []string{ constants.PodmanVarlinkBridgeEnv, + } + // podman v2 + vars2 := []string{ + constants.PodmanContainerHostEnv, + constants.PodmanContainerSSHKeyEnv, + } + // common + vars0 := []string{ constants.MinikubeActivePodmanEnv, } + + var vars []string + if ec.client != nil || ec.hostname != "" { + // getting ec.varlink needs a running machine + if ec.varlink { + vars = vars1 + } else { + vars = vars2 + } + } else { + // just unset *all* of the variables instead + vars = vars1 + vars = append(vars, vars2...) + } + vars = append(vars, vars0...) return shell.UnsetScript(ec.EnvConfig, w, vars) } @@ -190,12 +247,37 @@ func podmanBridge(client *ssh.ExternalClient) string { return strings.Join(command, " ") } +// podmanURL returns the url to use in a var for accessing the podman socket over ssh +func podmanURL(username string, hostname string, port int) string { + path := "/run/podman/podman.sock" + return fmt.Sprintf("ssh://%s@%s:%d%s", username, hostname, port, path) +} + // podmanEnvVars gets the necessary podman env variables to allow the use of minikube's podman service func podmanEnvVars(ec PodmanEnvConfig) map[string]string { - env := map[string]string{ - constants.PodmanVarlinkBridgeEnv: podmanBridge(ec.client), + // podman v1 + env1 := map[string]string{ + constants.PodmanVarlinkBridgeEnv: podmanBridge(ec.client), + } + // podman v2 + env2 := map[string]string{ + constants.PodmanContainerHostEnv: podmanURL(ec.username, ec.hostname, ec.port), + constants.PodmanContainerSSHKeyEnv: ec.keypath, + } + //common + env0 := map[string]string{ constants.MinikubeActivePodmanEnv: ec.profile, } + + var env map[string]string + if ec.varlink { + env = env1 + } else { + env = env2 + } + for k, v := range env0 { + env[k] = v + } return env } diff --git a/cmd/minikube/cmd/podman-env_test.go b/cmd/minikube/cmd/podman-env_test.go index 088867193a6e..6dbb1eb32919 100644 --- a/cmd/minikube/cmd/podman-env_test.go +++ b/cmd/minikube/cmd/podman-env_test.go @@ -41,7 +41,7 @@ func TestGeneratePodmanScripts(t *testing.T) { }{ { "bash", - PodmanEnvConfig{profile: "bash", driver: "kvm2", client: newFakeClient()}, + PodmanEnvConfig{profile: "bash", driver: "kvm2", varlink: true, client: newFakeClient()}, nil, `export PODMAN_VARLINK_BRIDGE="/usr/bin/ssh root@host -- sudo varlink -A \'podman varlink \\\$VARLINK_ADDRESS\' bridge" export MINIKUBE_ACTIVE_PODMAN="bash" @@ -50,6 +50,19 @@ export MINIKUBE_ACTIVE_PODMAN="bash" # eval $(minikube -p bash podman-env) `, `unset PODMAN_VARLINK_BRIDGE MINIKUBE_ACTIVE_PODMAN +`, + }, + { + "bash", + PodmanEnvConfig{profile: "bash", driver: "kvm2", client: newFakeClient(), username: "root", hostname: "host", port: 22}, + nil, + `export CONTAINER_HOST="ssh://root@host:22/run/podman/podman.sock" +export MINIKUBE_ACTIVE_PODMAN="bash" + +# To point your shell to minikube's podman service, run: +# eval $(minikube -p bash podman-env) +`, + `unset CONTAINER_HOST CONTAINER_SSHKEY MINIKUBE_ACTIVE_PODMAN `, }, } diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index fd330a96d973..4145ebb280d9 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -68,6 +68,10 @@ const ( MinikubeActiveDockerdEnv = "MINIKUBE_ACTIVE_DOCKERD" // PodmanVarlinkBridgeEnv is used for podman settings PodmanVarlinkBridgeEnv = "PODMAN_VARLINK_BRIDGE" + // PodmanContainerHostEnv is used for podman settings + PodmanContainerHostEnv = "CONTAINER_HOST" + // PodmanContainerSSHKeyEnv is used for podman settings + PodmanContainerSSHKeyEnv = "CONTAINER_SSHKEY" // MinikubeActivePodmanEnv holds the podman service that the user's shell is pointing at // value would be profile or empty if pointing to the user's host. MinikubeActivePodmanEnv = "MINIKUBE_ACTIVE_PODMAN" From 9f983a8f9904dee3f36789bf39a575670a25d95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sun, 1 Nov 2020 14:45:50 +0100 Subject: [PATCH 2/3] Refactor podmanUnsetScript with a helper function --- cmd/minikube/cmd/podman-env.go | 62 +++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/cmd/minikube/cmd/podman-env.go b/cmd/minikube/cmd/podman-env.go index b444b6c3582f..5c2f64787fb0 100644 --- a/cmd/minikube/cmd/podman-env.go +++ b/cmd/minikube/cmd/podman-env.go @@ -208,34 +208,7 @@ func podmanSetScript(ec PodmanEnvConfig, w io.Writer) error { // podmanUnsetScript writes out a shell-compatible 'podman-env unset' script func podmanUnsetScript(ec PodmanEnvConfig, w io.Writer) error { - // podman v1 - vars1 := []string{ - constants.PodmanVarlinkBridgeEnv, - } - // podman v2 - vars2 := []string{ - constants.PodmanContainerHostEnv, - constants.PodmanContainerSSHKeyEnv, - } - // common - vars0 := []string{ - constants.MinikubeActivePodmanEnv, - } - - var vars []string - if ec.client != nil || ec.hostname != "" { - // getting ec.varlink needs a running machine - if ec.varlink { - vars = vars1 - } else { - vars = vars2 - } - } else { - // just unset *all* of the variables instead - vars = vars1 - vars = append(vars, vars2...) - } - vars = append(vars, vars0...) + vars := podmanEnvNames(ec) return shell.UnsetScript(ec.EnvConfig, w, vars) } @@ -281,6 +254,39 @@ func podmanEnvVars(ec PodmanEnvConfig) map[string]string { return env } +// podmanEnvNames gets the necessary podman env variables to reset after using minikube's podman service +func podmanEnvNames(ec PodmanEnvConfig) []string { + // podman v1 + vars1 := []string{ + constants.PodmanVarlinkBridgeEnv, + } + // podman v2 + vars2 := []string{ + constants.PodmanContainerHostEnv, + constants.PodmanContainerSSHKeyEnv, + } + // common + vars0 := []string{ + constants.MinikubeActivePodmanEnv, + } + + var vars []string + if ec.client != nil || ec.hostname != "" { + // getting ec.varlink needs a running machine + if ec.varlink { + vars = vars1 + } else { + vars = vars2 + } + } else { + // just unset *all* of the variables instead + vars = vars1 + vars = append(vars, vars2...) + } + vars = append(vars, vars0...) + return vars +} + func init() { podmanEnvCmd.Flags().StringVar(&shell.ForceShell, "shell", "", "Force environment to be configured for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh], default is auto-detect") podmanEnvCmd.Flags().BoolVarP(&podmanUnset, "unset", "u", false, "Unset variables instead of setting them") From bfefa72815d0581c03592c483284c1bd95134aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sun, 1 Nov 2020 14:53:46 +0100 Subject: [PATCH 3/3] Split template for podman v1 and v2 into two vars --- cmd/minikube/cmd/podman-env.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/minikube/cmd/podman-env.go b/cmd/minikube/cmd/podman-env.go index 5c2f64787fb0..a0290d78e909 100644 --- a/cmd/minikube/cmd/podman-env.go +++ b/cmd/minikube/cmd/podman-env.go @@ -39,7 +39,9 @@ import ( "k8s.io/minikube/pkg/minikube/shell" ) -var podmanEnvTmpl = fmt.Sprintf("{{ if .VarlinkBridge }}{{ .Prefix }}%s{{ .Delimiter }}{{ .VarlinkBridge }}{{ .Suffix }}{{end}}{{ if .ContainerHost }}{{ .Prefix }}%s{{ .Delimiter }}{{ .ContainerHost }}{{ .Suffix }}{{end}}{{ if .ContainerSSHKey }}{{ .Prefix }}%s{{ .Delimiter }}{{ .ContainerSSHKey}}{{ .Suffix }}{{ end }}{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubePodmanProfile }}{{ .Suffix }}{{ .UsageHint }}", constants.PodmanVarlinkBridgeEnv, constants.PodmanContainerHostEnv, constants.PodmanContainerSSHKeyEnv, constants.MinikubeActivePodmanEnv) +var podmanEnv1Tmpl = fmt.Sprintf("{{ .Prefix }}%s{{ .Delimiter }}{{ .VarlinkBridge }}{{ .Suffix }}{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubePodmanProfile }}{{ .Suffix }}{{ .UsageHint }}", constants.PodmanVarlinkBridgeEnv, constants.MinikubeActivePodmanEnv) + +var podmanEnv2Tmpl = fmt.Sprintf("{{ .Prefix }}%s{{ .Delimiter }}{{ .ContainerHost }}{{ .Suffix }}{{ if .ContainerSSHKey }}{{ .Prefix }}%s{{ .Delimiter }}{{ .ContainerSSHKey}}{{ .Suffix }}{{ end }}{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubePodmanProfile }}{{ .Suffix }}{{ .UsageHint }}", constants.PodmanContainerHostEnv, constants.PodmanContainerSSHKeyEnv, constants.MinikubeActivePodmanEnv) // PodmanShellConfig represents the shell config for Podman type PodmanShellConfig struct { @@ -202,6 +204,12 @@ type PodmanEnvConfig struct { // podmanSetScript writes out a shell-compatible 'podman-env' script func podmanSetScript(ec PodmanEnvConfig, w io.Writer) error { + var podmanEnvTmpl string + if ec.varlink { + podmanEnvTmpl = podmanEnv1Tmpl + } else { + podmanEnvTmpl = podmanEnv2Tmpl + } envVars := podmanEnvVars(ec) return shell.SetScript(ec.EnvConfig, w, podmanEnvTmpl, podmanShellCfgSet(ec, envVars)) }