From a1f2849c7eccb32916dec8034e51a09b8152d22e Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Fri, 11 Jul 2025 05:30:45 +0000 Subject: [PATCH 1/7] dockerEnv vs podmanEnv detection --- cmd/minikube/cmd/docker-env.go | 49 ++++++++++++++++++++- cmd/minikube/cmd/docker-env_test.go | 41 +++++++++++++++++ site/content/en/docs/commands/podman-env.md | 10 ++++- 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/cmd/minikube/cmd/docker-env.go b/cmd/minikube/cmd/docker-env.go index 6b65b21bcd42..87b494cdbade 100644 --- a/cmd/minikube/cmd/docker-env.go +++ b/cmd/minikube/cmd/docker-env.go @@ -121,6 +121,8 @@ type DockerShellConfig struct { SSHAgentPID string } +// DockerBackendDetector is a function variable for backend detection, allowing injection/mocking in tests. +var DockerBackendDetector = DetectDockerBackend var ( noProxy bool sshHost bool @@ -382,6 +384,18 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc sshAgentPID: co.Config.SSHAgentPID, } + // Detect backend (docker vs podman) and set ec.driver accordingly. + // This allows robust detection and is injectable/mocked for tests. + backend, err := DockerBackendDetector(func(name string, args ...string) ([]byte, error) { + cmd := exec.Command(name, args...) + return cmd.CombinedOutput() + }) + if err != nil { + klog.Warningf("Failed to detect Docker backend, defaulting to driver: %v", err) + } else { + ec.driver = backend + } + dockerPath, err := exec.LookPath("docker") if err != nil { klog.Warningf("Unable to find docker in path - skipping connectivity check: %v", err) @@ -586,7 +600,8 @@ func dockerEnvVars(ec DockerEnvConfig) map[string]string { } else { rt = envTCP } - if os.Getenv(constants.MinikubeActiveDockerdEnv) == "" { + // Only add "existing" envs if the driver is podman + if ec.driver == "podman" && os.Getenv(constants.MinikubeActiveDockerdEnv) == "" { for _, env := range constants.DockerDaemonEnvs { if v := oci.InitialEnv(env); v != "" { key := constants.MinikubeExistingPrefix + env @@ -688,4 +703,36 @@ func init() { 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().StringVarP(&outputFormat, "output", "o", "", "One of 'text', 'yaml' or 'json'.") dockerEnvCmd.Flags().BoolVarP(&dockerUnset, "unset", "u", false, "Unset variables instead of setting them") +} + /* +DetectDockerBackend probes the Docker CLI backend by running `docker info` and parsing the output +for Podman signatures. Returns "docker" or "podman" as backend type. + +The detection logic looks for "Podman Engine" or "podman" in the "Server" or "Operating System" fields. +The commandRunner argument allows injection/mocking for tests. +*/ +func DetectDockerBackend(commandRunner func(name string, args ...string) ([]byte, error)) (string, error) { + output, err := commandRunner("docker", "info", "--format", "{{json .}}") + if err != nil { + return "", fmt.Errorf("failed to run docker info: %w", err) + } + // Try to parse as JSON and look for podman signatures + var info map[string]interface{} + if err := json.Unmarshal(output, &info); err == nil { + // Check "ServerVersion" or "OperatingSystem" fields for podman + if osField, ok := info["OperatingSystem"].(string); ok && strings.Contains(strings.ToLower(osField), "podman") { + return "podman", nil + } + if sv, ok := info["ServerVersion"].(string); ok && strings.Contains(strings.ToLower(sv), "podman") { + return "podman", nil + } + if sv, ok := info["Server"].(string); ok && strings.Contains(strings.ToLower(sv), "podman") { + return "podman", nil + } + } + // Fallback: look for "Podman" in raw output + if strings.Contains(strings.ToLower(string(output)), "podman") { + return "podman", nil + } + return "docker", nil } diff --git a/cmd/minikube/cmd/docker-env_test.go b/cmd/minikube/cmd/docker-env_test.go index c980da4eb363..077b67887f52 100644 --- a/cmd/minikube/cmd/docker-env_test.go +++ b/cmd/minikube/cmd/docker-env_test.go @@ -429,6 +429,17 @@ SSH_AGENT_PID: "29228" } for _, tc := range tests { t.Run(tc.config.profile, func(t *testing.T) { + origDetector := DockerBackendDetector + defer func() { DockerBackendDetector = origDetector }() + + // If this is the podman scenario, mock the backend detector + if tc.config.profile == "podmandriver" { + DockerBackendDetector = func(_ func(string, ...string) ([]byte, error)) (string, error) { + return "podman", nil + } + tc.config.driver = "docker" // initial value, will be overridden by detector + } + tc.config.EnvConfig.Shell = tc.shell // set global variable outputFormat = tc.output @@ -454,6 +465,36 @@ SSH_AGENT_PID: "29228" }) } + + // Add a Podman scenario test case + t.Run("podmandriver", func(t *testing.T) { + origDetector := DockerBackendDetector + defer func() { DockerBackendDetector = origDetector }() + DockerBackendDetector = func(_ func(string, ...string) ([]byte, error)) (string, error) { + return "podman", nil + } + config := DockerEnvConfig{profile: "podmandriver", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs"} + config.EnvConfig.Shell = "bash" + var b []byte + buf := bytes.NewBuffer(b) + if err := dockerSetScript(config, buf); err != nil { + t.Errorf("setScript(%+v) error: %v", config, err) + } + got := buf.String() + // Podman should still output the same envs, but may add "existing" envs if present in the environment. + // For this test, just check the main envs are present. + wantSet := `export DOCKER_TLS_VERIFY="1" +export DOCKER_HOST="tcp://127.0.0.1:32842" +export DOCKER_CERT_PATH="/certs" +export MINIKUBE_ACTIVE_DOCKERD="podmandriver" + +# To point your shell to minikube's docker-daemon, run: +# eval $(minikube -p podmandriver docker-env) +` + if diff := cmp.Diff(wantSet, got); diff != "" { + t.Errorf("setScript(podmandriver) mismatch (-want +got):\n%s\n\nraw output:\n%s\nquoted: %q", diff, got, got) + } + }) } func TestValidDockerProxy(t *testing.T) { diff --git a/site/content/en/docs/commands/podman-env.md b/site/content/en/docs/commands/podman-env.md index f44cc4032f38..6cad0ca9a75f 100644 --- a/site/content/en/docs/commands/podman-env.md +++ b/site/content/en/docs/commands/podman-env.md @@ -4,6 +4,14 @@ description: > Configure environment to use minikube's Podman service --- +## Requirements + +- **Podman version 4.9.2 or newer is required.** +- Support for Podman v3 and varlink-based communication has been removed. The `podman-env` command now configures your environment to use the Podman REST API socket, as required by Podman v4+. + +{{% pageinfo color="warning" %}} +**Note:** If you are using an older version of Podman, please upgrade to at least v4.9.2 to use `minikube podman-env`. Legacy varlink-based workflows are no longer supported. +{{% /pageinfo %}} ## minikube podman-env @@ -30,7 +38,7 @@ minikube podman-env [flags] --add_dir_header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) -b, --bootstrapper string The name of the cluster bootstrapper that will set up the Kubernetes cluster. (default "kubeadm") - -h, --help + -h, --help --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log_dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log_file string If non-empty, use this log file (no effect when -logtostderr=true) From 7723b9f1aa6694ab53a9b236fa8b9a696ca9fe14 Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Tue, 5 Aug 2025 03:18:01 +0000 Subject: [PATCH 2/7] Remove podman detection from docker-env per review feedback - Remove DockerBackendDetector and DetectDockerBackend function - Remove podman-specific logic from dockerEnvVars - Simplify docker-env to only handle Docker API compatibility --- .gitignore | 1 + cmd/minikube/cmd/docker-env.go | 48 +--------------------------------- 2 files changed, 2 insertions(+), 47 deletions(-) diff --git a/.gitignore b/.gitignore index b5396bc93667..12e39288bd95 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ hack/legacy_fill_db/gopogh_filldb_log.txt hack/legacy_fill_db/out/output_summary.json hack/legacy_fill_db/out/output.html hack/go-licenses +._b00t_.toml diff --git a/cmd/minikube/cmd/docker-env.go b/cmd/minikube/cmd/docker-env.go index 87b494cdbade..72cf9ba92607 100644 --- a/cmd/minikube/cmd/docker-env.go +++ b/cmd/minikube/cmd/docker-env.go @@ -121,8 +121,6 @@ type DockerShellConfig struct { SSHAgentPID string } -// DockerBackendDetector is a function variable for backend detection, allowing injection/mocking in tests. -var DockerBackendDetector = DetectDockerBackend var ( noProxy bool sshHost bool @@ -384,17 +382,6 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc sshAgentPID: co.Config.SSHAgentPID, } - // Detect backend (docker vs podman) and set ec.driver accordingly. - // This allows robust detection and is injectable/mocked for tests. - backend, err := DockerBackendDetector(func(name string, args ...string) ([]byte, error) { - cmd := exec.Command(name, args...) - return cmd.CombinedOutput() - }) - if err != nil { - klog.Warningf("Failed to detect Docker backend, defaulting to driver: %v", err) - } else { - ec.driver = backend - } dockerPath, err := exec.LookPath("docker") if err != nil { @@ -600,8 +587,7 @@ func dockerEnvVars(ec DockerEnvConfig) map[string]string { } else { rt = envTCP } - // Only add "existing" envs if the driver is podman - if ec.driver == "podman" && os.Getenv(constants.MinikubeActiveDockerdEnv) == "" { + if os.Getenv(constants.MinikubeActiveDockerdEnv) == "" { for _, env := range constants.DockerDaemonEnvs { if v := oci.InitialEnv(env); v != "" { key := constants.MinikubeExistingPrefix + env @@ -703,36 +689,4 @@ func init() { 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().StringVarP(&outputFormat, "output", "o", "", "One of 'text', 'yaml' or 'json'.") dockerEnvCmd.Flags().BoolVarP(&dockerUnset, "unset", "u", false, "Unset variables instead of setting them") -} - /* -DetectDockerBackend probes the Docker CLI backend by running `docker info` and parsing the output -for Podman signatures. Returns "docker" or "podman" as backend type. - -The detection logic looks for "Podman Engine" or "podman" in the "Server" or "Operating System" fields. -The commandRunner argument allows injection/mocking for tests. -*/ -func DetectDockerBackend(commandRunner func(name string, args ...string) ([]byte, error)) (string, error) { - output, err := commandRunner("docker", "info", "--format", "{{json .}}") - if err != nil { - return "", fmt.Errorf("failed to run docker info: %w", err) - } - // Try to parse as JSON and look for podman signatures - var info map[string]interface{} - if err := json.Unmarshal(output, &info); err == nil { - // Check "ServerVersion" or "OperatingSystem" fields for podman - if osField, ok := info["OperatingSystem"].(string); ok && strings.Contains(strings.ToLower(osField), "podman") { - return "podman", nil - } - if sv, ok := info["ServerVersion"].(string); ok && strings.Contains(strings.ToLower(sv), "podman") { - return "podman", nil - } - if sv, ok := info["Server"].(string); ok && strings.Contains(strings.ToLower(sv), "podman") { - return "podman", nil - } - } - // Fallback: look for "Podman" in raw output - if strings.Contains(strings.ToLower(string(output)), "podman") { - return "podman", nil - } - return "docker", nil } From 2f07bfdee98d04a79aa6a666e2d58783ea9d26e3 Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Tue, 5 Aug 2025 03:30:28 +0000 Subject: [PATCH 3/7] Refactor podman-env to use Docker API compatibility per review feedback - Change podman-env to use Docker client against Podman's Docker-compatible socket - Remove SSH-based connectivity (Docker client doesn't support SSH keys) - Simplify podman-env to use standard Docker environment variables - Update tests to match new Docker API compatibility approach - Update documentation to clarify Docker client usage - Remove podman-specific test cases from docker-env_test.go This addresses the core review feedback about API compatibility issues and provides a cleaner separation between docker-env and podman-env. --- cmd/minikube/cmd/docker-env_test.go | 40 ---- cmd/minikube/cmd/podman-env.go | 230 ++++++-------------- cmd/minikube/cmd/podman-env_test.go | 37 ++-- site/content/en/docs/commands/podman-env.md | 27 ++- 4 files changed, 112 insertions(+), 222 deletions(-) diff --git a/cmd/minikube/cmd/docker-env_test.go b/cmd/minikube/cmd/docker-env_test.go index 077b67887f52..7fa19b49362d 100644 --- a/cmd/minikube/cmd/docker-env_test.go +++ b/cmd/minikube/cmd/docker-env_test.go @@ -429,16 +429,6 @@ SSH_AGENT_PID: "29228" } for _, tc := range tests { t.Run(tc.config.profile, func(t *testing.T) { - origDetector := DockerBackendDetector - defer func() { DockerBackendDetector = origDetector }() - - // If this is the podman scenario, mock the backend detector - if tc.config.profile == "podmandriver" { - DockerBackendDetector = func(_ func(string, ...string) ([]byte, error)) (string, error) { - return "podman", nil - } - tc.config.driver = "docker" // initial value, will be overridden by detector - } tc.config.EnvConfig.Shell = tc.shell // set global variable @@ -465,36 +455,6 @@ SSH_AGENT_PID: "29228" }) } - - // Add a Podman scenario test case - t.Run("podmandriver", func(t *testing.T) { - origDetector := DockerBackendDetector - defer func() { DockerBackendDetector = origDetector }() - DockerBackendDetector = func(_ func(string, ...string) ([]byte, error)) (string, error) { - return "podman", nil - } - config := DockerEnvConfig{profile: "podmandriver", driver: "docker", hostIP: "127.0.0.1", port: 32842, certsDir: "/certs"} - config.EnvConfig.Shell = "bash" - var b []byte - buf := bytes.NewBuffer(b) - if err := dockerSetScript(config, buf); err != nil { - t.Errorf("setScript(%+v) error: %v", config, err) - } - got := buf.String() - // Podman should still output the same envs, but may add "existing" envs if present in the environment. - // For this test, just check the main envs are present. - wantSet := `export DOCKER_TLS_VERIFY="1" -export DOCKER_HOST="tcp://127.0.0.1:32842" -export DOCKER_CERT_PATH="/certs" -export MINIKUBE_ACTIVE_DOCKERD="podmandriver" - -# To point your shell to minikube's docker-daemon, run: -# eval $(minikube -p podmandriver docker-env) -` - if diff := cmp.Diff(wantSet, got); diff != "" { - t.Errorf("setScript(podmandriver) mismatch (-want +got):\n%s\n\nraw output:\n%s\nquoted: %q", diff, got, got) - } - }) } func TestValidDockerProxy(t *testing.T) { diff --git a/cmd/minikube/cmd/podman-env.go b/cmd/minikube/cmd/podman-env.go index e282000542c7..08b286a284e0 100644 --- a/cmd/minikube/cmd/podman-env.go +++ b/cmd/minikube/cmd/podman-env.go @@ -21,53 +21,48 @@ import ( "io" "os" "os/exec" - "strings" - "github.com/docker/machine/libmachine/drivers" - "github.com/docker/machine/libmachine/ssh" "github.com/spf13/cobra" "k8s.io/minikube/pkg/drivers/kic/oci" "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/driver" "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/localpath" "k8s.io/minikube/pkg/minikube/mustload" "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/reason" "k8s.io/minikube/pkg/minikube/shell" ) -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 }}"+ +var podmanEnvTmpl = fmt.Sprintf( + "{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}"+ + "{{ if .DockerTLSVerify }}"+ + "{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}"+ + "{{ end }}"+ + "{{ if .DockerCertPath }}"+ + "{{ .Prefix }}%s{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}"+ "{{ end }}"+ - "{{ if .ExistingContainerHost }}"+ - "{{ .Prefix }}%s{{ .Delimiter }}{{ .ExistingContainerHost }}{{ .Suffix }}"+ + "{{ if .ExistingDockerHost }}"+ + "{{ .Prefix }}%s{{ .Delimiter }}{{ .ExistingDockerHost }}{{ .Suffix }}"+ "{{ end }}"+ "{{ .Prefix }}%s{{ .Delimiter }}{{ .MinikubePodmanProfile }}{{ .Suffix }}"+ "{{ .UsageHint }}", - constants.PodmanContainerHostEnv, - constants.PodmanContainerSSHKeyEnv, - constants.ExistingContainerHostEnv, + constants.DockerHostEnv, + constants.DockerTLSVerifyEnv, + constants.DockerCertPathEnv, + constants.ExistingDockerHostEnv, constants.MinikubeActivePodmanEnv) // PodmanShellConfig represents the shell config for Podman type PodmanShellConfig struct { shell.Config - VarlinkBridge string - ContainerHost string - ContainerSSHKey string + DockerHost string + DockerTLSVerify string + DockerCertPath string MinikubePodmanProfile string - ExistingContainerHost string + ExistingDockerHost string } var podmanUnset bool @@ -75,31 +70,22 @@ var podmanUnset bool // podmanShellCfgSet generates context variables for "podman-env" func podmanShellCfgSet(ec PodmanEnvConfig, envMap map[string]string) *PodmanShellConfig { profile := ec.profile - const usgPlz = "To point your shell to minikube's podman service, run:" + const usgPlz = "To point your shell to minikube's podman docker-compatible service, run:" usgCmd := fmt.Sprintf("minikube -p %s podman-env", profile) s := &PodmanShellConfig{ Config: *shell.CfgSet(ec.EnvConfig, usgPlz, usgCmd), } - s.VarlinkBridge = envMap[constants.PodmanVarlinkBridgeEnv] - s.ContainerHost = envMap[constants.PodmanContainerHostEnv] - s.ContainerSSHKey = envMap[constants.PodmanContainerSSHKeyEnv] + s.DockerHost = envMap[constants.DockerHostEnv] + s.DockerTLSVerify = envMap[constants.DockerTLSVerifyEnv] + s.DockerCertPath = envMap[constants.DockerCertPathEnv] - s.ExistingContainerHost = envMap[constants.ExistingContainerHostEnv] + s.ExistingDockerHost = envMap[constants.ExistingDockerHostEnv] s.MinikubePodmanProfile = envMap[constants.MinikubeActivePodmanEnv] return s } -// 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 { @@ -109,35 +95,11 @@ func isPodmanAvailable(r command.Runner) bool { return true } -func createExternalSSHClient(d drivers.Driver) (*ssh.ExternalClient, error) { - sshBinaryPath, err := exec.LookPath("ssh") - if err != nil { - return &ssh.ExternalClient{}, err - } - - addr, err := d.GetSSHHostname() - if err != nil { - return &ssh.ExternalClient{}, err - } - - port, err := d.GetSSHPort() - if err != nil { - return &ssh.ExternalClient{}, err - } - - auth := &ssh.Auth{} - if d.GetSSHKeyPath() != "" { - auth.Keys = []string{d.GetSSHKeyPath()} - } - - return ssh.NewExternalClient(sshBinaryPath, d.GetSSHUsername(), addr, port, auth) -} - // podmanEnvCmd represents the podman-env command var podmanEnvCmd = &cobra.Command{ Use: "podman-env", Short: "Configure environment to use minikube's Podman service", - Long: `Sets up podman env variables; similar to '$(podman-machine env)'.`, + Long: `Sets up Docker client env variables to use minikube's Podman Docker-compatible service.`, Run: func(_ *cobra.Command, _ []string) { sh := shell.EnvConfig{ Shell: shell.ForceShell, @@ -177,37 +139,38 @@ var podmanEnvCmd = &cobra.Command{ exit.Message(reason.EnvPodmanUnavailable, `The podman service within '{{.cluster}}' is not active`, out.V{"cluster": cname}) } - 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) + hostIP := co.CP.IP.String() + + // Use Docker API compatibility - podman supports Docker API on port 2376 + port := constants.DockerDaemonPort + noProxy := false + + // Check if we need to use SSH tunnel for remote access + sshHost := false + if driver.NeedsPortForward(driverName) { + sshHost = true + sshPort, err := d.GetSSHPort() + if err != nil { + exit.Error(reason.IfSSHClient, "Error getting ssh port", err) + } + hostIP = "127.0.0.1" + _ = sshPort // We'll use SSH tunnel if needed } ec := PodmanEnvConfig{ EnvConfig: sh, profile: cname, driver: driverName, - varlink: varlink, - client: client, - username: d.GetSSHUsername(), - hostname: hostname, + ssh: sshHost, + hostIP: hostIP, port: port, - keypath: d.GetSSHKeyPath(), + certsDir: localpath.MakeMiniPath("certs"), + noProxy: noProxy, } if ec.Shell == "" { + var err error ec.Shell, err = shell.Detect() if err != nil { exit.Error(reason.InternalShellDetect, "Error detecting shell", err) @@ -225,22 +188,15 @@ type PodmanEnvConfig struct { shell.EnvConfig profile string driver string - varlink bool - client *ssh.ExternalClient - username string - hostname string + ssh bool + hostIP string port int - keypath string + certsDir string + noProxy bool } // 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(w, podmanEnvTmpl, podmanShellCfgSet(ec, envVars)) } @@ -251,85 +207,43 @@ func podmanUnsetScript(ec PodmanEnvConfig, w io.Writer) error { return shell.UnsetScript(ec.EnvConfig, w, vars) } -// podmanBridge returns the command to use in a var for accessing the podman varlink bridge over ssh -func podmanBridge(client *ssh.ExternalClient) string { - cmd := []string{client.BinaryPath} - cmd = append(cmd, client.BaseArgs...) - cmd = append(cmd, "--", "sudo", "varlink", "-A", `\'podman varlink \\\$VARLINK_ADDRESS\'`, "bridge") - return strings.Join(cmd, " ") -} - -// 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 +// podmanEnvVars gets the necessary Docker-compatible env variables for podman service func podmanEnvVars(ec PodmanEnvConfig) map[string]string { - // 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 + var rt string + if ec.ssh { + rt = fmt.Sprintf("tcp://%s:%d", ec.hostIP, ec.port) } else { - env = env2 + rt = fmt.Sprintf("tcp://%s:%d", ec.hostIP, ec.port) } - for k, v := range env0 { - env[k] = v + + env := map[string]string{ + constants.DockerHostEnv: rt, + constants.DockerTLSVerifyEnv: "1", + constants.DockerCertPathEnv: ec.certsDir, + constants.MinikubeActivePodmanEnv: ec.profile, } + + // Save existing Docker env if not already using minikube if os.Getenv(constants.MinikubeActivePodmanEnv) == "" { - e := constants.PodmanContainerHostEnv - if v := oci.InitialEnv(e); v != "" { - key := constants.ExistingContainerHostEnv - env[key] = v + for _, envVar := range constants.DockerDaemonEnvs { + if v := oci.InitialEnv(envVar); v != "" { + key := constants.MinikubeExistingPrefix + envVar + env[key] = v + } } } return env } -// podmanEnvNames gets the necessary podman env variables to reset after using minikube's podman service +// podmanEnvNames gets the necessary Docker 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{ + vars := []string{ + constants.DockerHostEnv, + constants.DockerTLSVerifyEnv, + constants.DockerCertPathEnv, 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 } diff --git a/cmd/minikube/cmd/podman-env_test.go b/cmd/minikube/cmd/podman-env_test.go index 7215fe9a1fd0..c01b83e85768 100644 --- a/cmd/minikube/cmd/podman-env_test.go +++ b/cmd/minikube/cmd/podman-env_test.go @@ -20,17 +20,9 @@ import ( "bytes" "testing" - "github.com/docker/machine/libmachine/ssh" "github.com/google/go-cmp/cmp" ) -func newFakeClient() *ssh.ExternalClient { - return &ssh.ExternalClient{ - BaseArgs: []string{"root@host"}, - BinaryPath: "/usr/bin/ssh", - } -} - func TestGeneratePodmanScripts(t *testing.T) { var tests = []struct { shell string @@ -41,38 +33,45 @@ func TestGeneratePodmanScripts(t *testing.T) { }{ { "bash", - PodmanEnvConfig{profile: "bash", driver: "kvm2", varlink: true, client: newFakeClient()}, + PodmanEnvConfig{profile: "bash", driver: "kvm2", ssh: false, hostIP: "127.0.0.1", port: 2376, certsDir: "/certs", noProxy: false}, nil, - `export PODMAN_VARLINK_BRIDGE="/usr/bin/ssh root@host -- sudo varlink -A \'podman varlink \\\$VARLINK_ADDRESS\' bridge" + `export DOCKER_HOST="tcp://127.0.0.1:2376" +export DOCKER_TLS_VERIFY="1" +export DOCKER_CERT_PATH="/certs" export MINIKUBE_ACTIVE_PODMAN="bash" -# To point your shell to minikube's podman service, run: +# To point your shell to minikube's podman docker-compatible service, run: # eval $(minikube -p bash podman-env) `, - `unset PODMAN_VARLINK_BRIDGE; + `unset DOCKER_HOST; +unset DOCKER_TLS_VERIFY; +unset DOCKER_CERT_PATH; unset MINIKUBE_ACTIVE_PODMAN; `, }, { "bash", - PodmanEnvConfig{profile: "bash", driver: "kvm2", client: newFakeClient(), username: "root", hostname: "host", port: 22}, + PodmanEnvConfig{profile: "bash", driver: "kvm2", ssh: true, hostIP: "127.0.0.1", port: 2376, certsDir: "/certs", noProxy: false}, nil, - `export CONTAINER_HOST="ssh://root@host:22/run/podman/podman.sock" + `export DOCKER_HOST="tcp://127.0.0.1:2376" +export DOCKER_TLS_VERIFY="1" +export DOCKER_CERT_PATH="/certs" export MINIKUBE_ACTIVE_PODMAN="bash" -# To point your shell to minikube's podman service, run: +# To point your shell to minikube's podman docker-compatible service, run: # eval $(minikube -p bash podman-env) `, - `unset CONTAINER_HOST; -unset CONTAINER_SSHKEY; + `unset DOCKER_HOST; +unset DOCKER_TLS_VERIFY; +unset DOCKER_CERT_PATH; unset MINIKUBE_ACTIVE_PODMAN; `, }, } + 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) if err := podmanSetScript(tc.config, buf); err != nil { @@ -94,4 +93,4 @@ unset MINIKUBE_ACTIVE_PODMAN; }) } -} +} \ No newline at end of file diff --git a/site/content/en/docs/commands/podman-env.md b/site/content/en/docs/commands/podman-env.md index 6cad0ca9a75f..1739c7647ce9 100644 --- a/site/content/en/docs/commands/podman-env.md +++ b/site/content/en/docs/commands/podman-env.md @@ -7,24 +7,41 @@ description: > ## Requirements - **Podman version 4.9.2 or newer is required.** -- Support for Podman v3 and varlink-based communication has been removed. The `podman-env` command now configures your environment to use the Podman REST API socket, as required by Podman v4+. +- **Docker client is required** - `podman-env` uses Docker's client to communicate with Podman's Docker-compatible API. +- The `podman-env` command configures Docker client environment variables to connect to minikube's Podman service via its Docker-compatible API. -{{% pageinfo color="warning" %}} -**Note:** If you are using an older version of Podman, please upgrade to at least v4.9.2 to use `minikube podman-env`. Legacy varlink-based workflows are no longer supported. +{{% pageinfo color="info" %}} +**Note:** This command sets up standard Docker environment variables (`DOCKER_HOST`, `DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`) to connect to Podman's Docker-compatible socket. Use the regular `docker` command-line tool to interact with minikube's Podman service. {{% /pageinfo %}} ## minikube podman-env -Configure environment to use minikube's Podman service +Configure environment to use minikube's Podman service via Docker API compatibility ### Synopsis -Sets up podman env variables; similar to '$(podman-machine env)'. +Sets up Docker client env variables to use minikube's Podman Docker-compatible service. ```shell minikube podman-env [flags] ``` +### Usage + +After running `minikube podman-env`, you can use the regular Docker client to interact with minikube's Podman service: + +```shell +# Configure your shell +eval $(minikube podman-env) + +# Now use docker commands as usual - they will connect to Podman +docker images +docker build -t myapp . +docker run myapp +``` + +This approach provides Docker API compatibility while using Podman as the container runtime inside minikube. + ### Options ``` From 9457fa1220b96eb01eae2b1652eea9e9a4c076ca Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Tue, 5 Aug 2025 03:39:39 +0000 Subject: [PATCH 4/7] Fix whitespace in docker-env files Remove unnecessary blank lines that were accidentally introduced when removing podman detection logic. --- cmd/minikube/cmd/docker-env.go | 1 - cmd/minikube/cmd/docker-env_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/cmd/minikube/cmd/docker-env.go b/cmd/minikube/cmd/docker-env.go index 72cf9ba92607..6b65b21bcd42 100644 --- a/cmd/minikube/cmd/docker-env.go +++ b/cmd/minikube/cmd/docker-env.go @@ -382,7 +382,6 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc sshAgentPID: co.Config.SSHAgentPID, } - dockerPath, err := exec.LookPath("docker") if err != nil { klog.Warningf("Unable to find docker in path - skipping connectivity check: %v", err) diff --git a/cmd/minikube/cmd/docker-env_test.go b/cmd/minikube/cmd/docker-env_test.go index 7fa19b49362d..c980da4eb363 100644 --- a/cmd/minikube/cmd/docker-env_test.go +++ b/cmd/minikube/cmd/docker-env_test.go @@ -429,7 +429,6 @@ SSH_AGENT_PID: "29228" } for _, tc := range tests { t.Run(tc.config.profile, func(t *testing.T) { - tc.config.EnvConfig.Shell = tc.shell // set global variable outputFormat = tc.output From 0dda53997430ba1764960915d41c1b977b03c724 Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Tue, 5 Aug 2025 04:31:53 +0000 Subject: [PATCH 5/7] Add Docker runtime support to podman-env command Extend podman-env compatibility to support both crio and docker container runtimes, providing users with more deployment flexibility while maintaining the core Docker API compatibility approach that eliminates API version conflicts. This change allows podman-env to work with: - Podman driver + crio runtime (original support) - Podman driver + docker runtime (new support) Both configurations use the same Docker API compatibility approach that was implemented to address reviewer feedback about SSH-based connectivity issues and API version mismatches. --- cmd/minikube/cmd/podman-env.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/minikube/cmd/podman-env.go b/cmd/minikube/cmd/podman-env.go index 08b286a284e0..819af7680f5b 100644 --- a/cmd/minikube/cmd/podman-env.go +++ b/cmd/minikube/cmd/podman-env.go @@ -129,9 +129,10 @@ 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 co.Config.KubernetesConfig.ContainerRuntime != constants.CRIO { - exit.Message(reason.Usage, `The podman-env command is only compatible with the "crio" runtime, but this cluster was configured to use the "{{.runtime}}" runtime.`, - out.V{"runtime": co.Config.KubernetesConfig.ContainerRuntime}) + cr := co.Config.KubernetesConfig.ContainerRuntime + if cr != constants.CRIO && cr != constants.Docker { + exit.Message(reason.Usage, `The podman-env command is only compatible with the "crio" and "docker" runtimes, but this cluster was configured to use the "{{.runtime}}" runtime.`, + out.V{"runtime": cr}) } r := co.CP.Runner From bfcdf260a9753d303003b2d8d1fb59cff7c68251 Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Tue, 5 Aug 2025 04:45:57 +0000 Subject: [PATCH 6/7] Update podman-env documentation with local development example Add practical example showing how to build images directly in minikube and deploy them to Kubernetes without needing a separate registry. This demonstrates the key value proposition of the Docker API compatibility approach. --- site/content/en/docs/commands/podman-env.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/site/content/en/docs/commands/podman-env.md b/site/content/en/docs/commands/podman-env.md index 1739c7647ce9..d205066f3c0e 100644 --- a/site/content/en/docs/commands/podman-env.md +++ b/site/content/en/docs/commands/podman-env.md @@ -42,6 +42,21 @@ docker run myapp This approach provides Docker API compatibility while using Podman as the container runtime inside minikube. +### Building Images for Local Development + +You can build images directly in minikube and deploy them without a separate registry: + +```shell +# Configure environment +eval $(minikube podman-env) + +# Build image directly in minikube +docker build -t my-local-app . + +# Deploy to Kubernetes without registry +kubectl run my-app --image=my-local-app --image-pull-policy=Never +``` + ### Options ``` From e1e355ccfeccf23591d708d9eb0f9854a5fe6af1 Mon Sep 17 00:00:00 2001 From: elasticdotventures Date: Thu, 7 Aug 2025 06:46:56 +0000 Subject: [PATCH 7/7] refactor: address reviewer feedback by removing podman binary detection --- cmd/minikube/cmd/podman-env.go | 20 +++++--------------- cmd/minikube/cmd/podman-env_test.go | 4 ++-- site/content/en/docs/commands/podman-env.md | 2 +- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/cmd/minikube/cmd/podman-env.go b/cmd/minikube/cmd/podman-env.go index 819af7680f5b..a860aa808b1f 100644 --- a/cmd/minikube/cmd/podman-env.go +++ b/cmd/minikube/cmd/podman-env.go @@ -70,7 +70,7 @@ var podmanUnset bool // podmanShellCfgSet generates context variables for "podman-env" func podmanShellCfgSet(ec PodmanEnvConfig, envMap map[string]string) *PodmanShellConfig { profile := ec.profile - const usgPlz = "To point your shell to minikube's podman docker-compatible service, run:" + const usgPlz = "To point your shell to minikube's Podman Docker-compatible service, run:" usgCmd := fmt.Sprintf("minikube -p %s podman-env", profile) s := &PodmanShellConfig{ Config: *shell.CfgSet(ec.EnvConfig, usgPlz, usgCmd), @@ -86,20 +86,13 @@ func podmanShellCfgSet(ec PodmanEnvConfig, envMap map[string]string) *PodmanShel return s } -// 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 - } - - return true -} // podmanEnvCmd represents the podman-env command var podmanEnvCmd = &cobra.Command{ Use: "podman-env", - Short: "Configure environment to use minikube's Podman service", - Long: `Sets up Docker client env variables to use minikube's Podman Docker-compatible service.`, + Short: "Configure environment to use minikube's Podman Docker-compatible service", + Long: `Sets up Docker client env variables to use minikube's Podman Docker-compatible service. +Uses Docker client against Podman's Docker API compatibility - no podman binary required.`, Run: func(_ *cobra.Command, _ []string) { sh := shell.EnvConfig{ Shell: shell.ForceShell, @@ -136,14 +129,11 @@ var podmanEnvCmd = &cobra.Command{ } 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}) - } d := co.CP.Host.Driver hostIP := co.CP.IP.String() - // Use Docker API compatibility - podman supports Docker API on port 2376 + // Use Docker API compatibility - Docker client connects to Docker-compatible API port := constants.DockerDaemonPort noProxy := false diff --git a/cmd/minikube/cmd/podman-env_test.go b/cmd/minikube/cmd/podman-env_test.go index c01b83e85768..25c0d5156f85 100644 --- a/cmd/minikube/cmd/podman-env_test.go +++ b/cmd/minikube/cmd/podman-env_test.go @@ -40,7 +40,7 @@ export DOCKER_TLS_VERIFY="1" export DOCKER_CERT_PATH="/certs" export MINIKUBE_ACTIVE_PODMAN="bash" -# To point your shell to minikube's podman docker-compatible service, run: +# To point your shell to minikube's Podman Docker-compatible service, run: # eval $(minikube -p bash podman-env) `, `unset DOCKER_HOST; @@ -58,7 +58,7 @@ export DOCKER_TLS_VERIFY="1" export DOCKER_CERT_PATH="/certs" export MINIKUBE_ACTIVE_PODMAN="bash" -# To point your shell to minikube's podman docker-compatible service, run: +# To point your shell to minikube's Podman Docker-compatible service, run: # eval $(minikube -p bash podman-env) `, `unset DOCKER_HOST; diff --git a/site/content/en/docs/commands/podman-env.md b/site/content/en/docs/commands/podman-env.md index d205066f3c0e..b540c4e2d75b 100644 --- a/site/content/en/docs/commands/podman-env.md +++ b/site/content/en/docs/commands/podman-env.md @@ -6,7 +6,7 @@ description: > ## Requirements -- **Podman version 4.9.2 or newer is required.** +- **Recent Podman version with Docker API compatibility is required.** - **Docker client is required** - `podman-env` uses Docker's client to communicate with Podman's Docker-compatible API. - The `podman-env` command configures Docker client environment variables to connect to minikube's Podman service via its Docker-compatible API.