Skip to content

Commit 84f0cc1

Browse files
committed
Support docker-env with cri-o runtime
It is very similar to containerd, but with podman for cri-o instead of nerdctl for containerd. Both using ssh sockets.
1 parent fe71ac9 commit 84f0cc1

4 files changed

Lines changed: 114 additions & 9 deletions

File tree

cmd/minikube/cmd/docker-env.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -314,14 +314,20 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc
314314
exit.Message(reason.Usage, err.Error())
315315
}
316316

317-
// for the sake of docker-env command, start nerdctl and nerdctld
318-
if cr == constants.Containerd {
319-
out.WarningT("Using the docker-env command with the containerd runtime is a highly experimental feature, please provide feedback or contribute to make it better")
317+
// for the sake of docker-env command, start nerdctl and nerdctld (or the podman system service, "podmand")
318+
if cr == constants.Containerd || cr == constants.CRIO {
319+
if cr == constants.Containerd {
320+
out.WarningT("Using the docker-env command with the containerd runtime is a highly experimental feature, please provide feedback or contribute to make it better")
321+
322+
startNerdctld(options)
323+
} else if cr == constants.CRIO {
324+
out.WarningT("Using the docker-env command with the cri-o runtime is a highly experimental feature, please provide feedback or contribute to make it better")
320325

321-
startNerdctld(options)
326+
startPodmand(options)
327+
}
322328

323-
// docker-env on containerd depends on nerdctld (https://github.com/afbjorklund/nerdctld) as "docker" daeomn
324-
// and nerdctld daemon must be used with ssh connection (it is set in kicbase image's Dockerfile)
329+
// docker-env on containerd depends on nerdctld (https://github.com/afbjorklund/nerdctld) as "docker" daemon
330+
// and nerdctld (or podman) daemon must be used with ssh connection (it is set in kicbase image's Dockerfile)
325331
// so directly set --ssh-host --ssh-add to true, even user didn't specify them
326332
sshAdd = true
327333
sshHost = true
@@ -672,9 +678,6 @@ func tryDockerConnectivity(bin string, ec DockerEnvConfig) ([]byte, error) {
672678
}
673679

674680
func dockerEnvSupported(containerRuntime, driverName string) error {
675-
if containerRuntime != constants.Docker && containerRuntime != constants.Containerd {
676-
return fmt.Errorf("the docker-env command only supports the docker and containerd runtimes")
677-
}
678681
// we only support containerd-env on the Docker driver
679682
if containerRuntime == constants.Containerd && driverName != driver.Docker {
680683
return fmt.Errorf("the docker-env command only supports the containerd runtime with the docker driver")

cmd/minikube/cmd/start.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,3 +2100,20 @@ func startNerdctld(options *run.CommandOptions) {
21002100
exit.Error(reason.StartNerdctld, fmt.Sprintf("Failed to set up DOCKER_HOST: %s", rest.Output()), err)
21012101
}
21022102
}
2103+
2104+
func startPodmand(options *run.CommandOptions) {
2105+
co := mustload.Running(ClusterFlagValue(), options)
2106+
runner := co.CP.Runner
2107+
2108+
// sudo systemctl start podman.socket
2109+
if rest, err := runner.RunCmd(exec.Command("sudo", "systemctl", "start", "podman.socket")); err != nil {
2110+
exit.Error(reason.StartPodmand, fmt.Sprintf("Failed to enable podman.socket: %s", rest.Output()), err)
2111+
}
2112+
2113+
// set up environment variable on remote machine. docker client uses 'non-login & non-interactive shell' therefore the only way is to modify .bashrc file of user 'docker'
2114+
// insert this at 4th line
2115+
envSetupCommand := exec.Command("/bin/bash", "-c", "sed -i '4i export DOCKER_HOST=unix:///run/podman/podman.sock' .bashrc")
2116+
if rest, err := runner.RunCmd(envSetupCommand); err != nil {
2117+
exit.Error(reason.StartPodmand, fmt.Sprintf("Failed to set up DOCKER_HOST: %s", rest.Output()), err)
2118+
}
2119+
}

pkg/minikube/reason/reason.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ var (
102102
InternalCommandRunner = Kind{ID: "MK_COMMAND_RUNNER", ExitCode: ExProgramError}
103103
// minikube failed to start nerdctld
104104
StartNerdctld = Kind{ID: "MK_START_NERDCTLD", ExitCode: ExProgramError}
105+
// minikube failed to start podmand
106+
StartPodmand = Kind{ID: "MK_START_PODMAND", ExitCode: ExProgramError}
105107
// minikube failed to generate shell command completion for a supported shell
106108
InternalCompletion = Kind{ID: "MK_COMPLETION", ExitCode: ExProgramError}
107109
// minikube failed to set an internal config value

test/integration/docker_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,86 @@ func TestDockerEnvContainerd(t *testing.T) {
255255
t.Fatal("failed to detect image 'local/minikube-dockerenv-containerd-test' in output of docker image ls")
256256
}
257257
}
258+
259+
// TestDockerEnvCrio makes sure that minikube docker-env command works when the runtime is crio
260+
func TestDockerEnvCrio(t *testing.T) {
261+
t.Log("running with", ContainerRuntime(), DockerDriver(), runtime.GOOS, runtime.GOARCH)
262+
if ContainerRuntime() != constants.CRIO || !DockerDriver() || runtime.GOOS != "linux" {
263+
t.Skip("skipping: TestDockerEnvCrio can only be run with the crio runtime on Docker driver")
264+
}
265+
profile := UniqueProfileName("dockerenv")
266+
ctx, cancel := context.WithTimeout(context.Background(), Minutes(30))
267+
defer CleanupWithLogs(t, profile, cancel)
268+
269+
// start the minikube with crio runtime
270+
args := append([]string{"start", "-p", profile}, StartArgs()...)
271+
cmd := exec.CommandContext(ctx, Target(), args...)
272+
startResult, err := Run(t, cmd)
273+
if err != nil {
274+
t.Errorf("failed to start minikube with args: %q : %v", startResult.Command(), err)
275+
}
276+
time.Sleep(time.Second * 10)
277+
278+
// execute 'minikube docker-env --ssh-host --ssh-add' and extract the 'DOCKER_HOST' environment value
279+
cmd = exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("%s docker-env --ssh-host --ssh-add -p %s", Target(), profile))
280+
result, err := Run(t, cmd)
281+
if err != nil {
282+
t.Errorf("failed to execute minikube docker-env --ssh-host --ssh-add, error: %v, output: %s", err, result.Output())
283+
}
284+
285+
output := result.Output()
286+
groups := regexp.MustCompile(`DOCKER_HOST="(\S*)"`).FindStringSubmatch(output)
287+
if len(groups) < 2 {
288+
t.Errorf("DOCKER_HOST doesn't match expected format, output is %s", output)
289+
}
290+
dockerHost := groups[1]
291+
segments := strings.Split(dockerHost, ":")
292+
if len(segments) < 3 {
293+
t.Errorf("DOCKER_HOST doesn't match expected format, output is %s", dockerHost)
294+
}
295+
296+
// get SSH_AUTH_SOCK
297+
groups = regexp.MustCompile(`SSH_AUTH_SOCK=(\S*)`).FindStringSubmatch(output)
298+
if len(groups) < 2 {
299+
t.Errorf("failed to acquire SSH_AUTH_SOCK, output is %s", output)
300+
}
301+
sshAuthSock := groups[1]
302+
// get SSH_AGENT_PID
303+
groups = regexp.MustCompile(`SSH_AGENT_PID=(\S*)`).FindStringSubmatch(output)
304+
if len(groups) < 2 {
305+
t.Errorf("failed to acquire SSH_AUTH_PID, output is %s", output)
306+
}
307+
sshAgentPid := groups[1]
308+
309+
cmd = exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("SSH_AUTH_SOCK=%s SSH_AGENT_PID=%s DOCKER_HOST=%s docker version", sshAuthSock, sshAgentPid, dockerHost))
310+
311+
result, err = Run(t, cmd)
312+
if err != nil {
313+
t.Fatalf("failed to execute 'docker version', error: %v, output: %s", err, result.Output())
314+
}
315+
// if we are really connecting to podman inside node, docker version output should have word 'Podman'
316+
// If everything works properly, in the output of `docker version` you should be able to see something like
317+
/*
318+
Server: Podman Engine
319+
*/
320+
if !strings.Contains(result.Output(), "Podman") {
321+
t.Fatal("failed to detect keyword 'Podman' in output of docker version")
322+
}
323+
324+
// now try to build an image
325+
cmd = exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("SSH_AUTH_SOCK=%s SSH_AGENT_PID=%s DOCKER_HOST=%s DOCKER_BUILDKIT=0 docker build -t local/minikube-dockerenv-crio-test:latest testdata/docker-env", sshAuthSock, sshAgentPid, dockerHost))
326+
result, err = Run(t, cmd)
327+
if err != nil {
328+
t.Errorf("failed to build images, error: %v, output:%s", err, result.Output())
329+
}
330+
331+
// and check whether that image is really available
332+
cmd = exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("SSH_AUTH_SOCK=%s SSH_AGENT_PID=%s DOCKER_HOST=%s docker image ls", sshAuthSock, sshAgentPid, dockerHost))
333+
result, err = Run(t, cmd)
334+
if err != nil {
335+
t.Fatalf("failed to execute 'docker image ls', error: %v, output: %s", err, result.Output())
336+
}
337+
if !strings.Contains(result.Output(), "local/minikube-dockerenv-crio-test") {
338+
t.Fatal("failed to detect image 'local/minikube-dockerenv-crio-test' in output of docker image ls")
339+
}
340+
}

0 commit comments

Comments
 (0)