diff --git a/pkg/docker/docker_client.go b/pkg/docker/docker_client.go index 321b99cac1..f042efb398 100644 --- a/pkg/docker/docker_client.go +++ b/pkg/docker/docker_client.go @@ -84,28 +84,33 @@ func NewClient(defaultHost string) (dc DockerClient, dockerHostInRemote string, hostKeyCallback := fnssh.NewHostKeyCbk() if dockerHost == "" { - _url, err = url.Parse(defaultHost) - if err != nil { - return - } - _, err = os.Stat(_url.Path) - switch { - case err == nil: - dockerHost = defaultHost - case err != nil && !os.IsNotExist(err): - return - case os.IsNotExist(err) && podmanPresent(): - if runtime.GOOS == "linux" { - // on Linux: spawn temporary podman service - rawClient, dockerHostInRemote, err = newClientWithPodmanService() + // Try to get Docker host from current context + if contextHost := getDockerContextHost(); contextHost != "" { + dockerHost = contextHost + } else { + _url, err = url.Parse(defaultHost) + if err != nil { + return + } + _, err = os.Stat(_url.Path) + switch { + case err == nil: + dockerHost = defaultHost + case err != nil && !os.IsNotExist(err): return - } else { - // on non-Linux: try to use connection to podman machine - dh, dhid := tryGetPodmanRemoteConn() - if dh != "" { - dockerHost, dockerHostSSHIdentity = dh, dhid - hostKeyCallback = func(hostPort string, pubKey ssh.PublicKey) error { - return nil + case os.IsNotExist(err) && podmanPresent(): + if runtime.GOOS == "linux" { + // on Linux: spawn temporary podman service + rawClient, dockerHostInRemote, err = newClientWithPodmanService() + return + } else { + // on non-Linux: try to use connection to podman machine + dh, dhid := tryGetPodmanRemoteConn() + if dh != "" { + dockerHost, dockerHostSSHIdentity = dh, dhid + hostKeyCallback = func(hostPort string, pubKey ssh.PublicKey) error { + return nil + } } } } @@ -284,6 +289,45 @@ func podmanPresent() bool { return err == nil } +// getDockerContextHost tries to get the Docker host from the current Docker context. +// This is useful for Docker Desktop which uses context-specific sockets. +// Returns empty string if unable to determine the context host. +func getDockerContextHost() string { + // Check if docker CLI is available + dockerPath, err := exec.LookPath("docker") + if err != nil { + return "" + } + + // Run 'docker context inspect' to get current context details + cmd := exec.Command(dockerPath, "context", "inspect") + out, err := cmd.CombinedOutput() + if err != nil { + return "" + } + + // Parse the JSON output + var contexts []struct { + Name string + Endpoints struct { + Docker struct { + Host string `json:"Host"` + } `json:"docker"` + } `json:"Endpoints"` + } + + if err := json.Unmarshal(out, &contexts); err != nil { + return "" + } + + // Return the host from the first (current) context + if len(contexts) > 0 && contexts[0].Endpoints.Docker.Host != "" { + return contexts[0].Endpoints.Docker.Host + } + + return "" +} + type clientWithAdditionalCleanup struct { client.APIClient cleanUp func() diff --git a/pkg/docker/docker_client_test.go b/pkg/docker/docker_client_test.go index 066eb94bca..1545040933 100644 --- a/pkg/docker/docker_client_test.go +++ b/pkg/docker/docker_client_test.go @@ -127,3 +127,36 @@ func startMockDaemonUnix(t *testing.T, sock string) { } startMockDaemon(t, l) } + +// TestNewClient_DockerContext tests that Docker context is properly detected +// when DOCKER_HOST is not set +func TestNewClient_DockerContext(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping Docker context test on Windows") + } + + // This test requires docker CLI to be available + // If not available, skip the test + ctx, cancel := context.WithTimeout(t.Context(), time.Second*5) + defer cancel() + + // Unset DOCKER_HOST to force context detection + t.Setenv("DOCKER_HOST", "") + + // Try to create a client - it should use Docker context if available + dockerClient, _, err := docker.NewClient(client.DefaultDockerHost) + if err != nil { + // If docker is not available or not running, skip the test + if err == docker.ErrNoDocker { + t.Skip("Docker not available, skipping context detection test") + } + t.Fatalf("Failed to create Docker client: %v", err) + } + defer dockerClient.Close() + + // Try to ping the daemon to verify the connection works + _, err = dockerClient.Ping(ctx, client.PingOptions{}) + if err != nil { + t.Errorf("Failed to ping Docker daemon via context: %v", err) + } +}