From f8966cb3ad6ab13bb3164caeba3f48895a432e36 Mon Sep 17 00:00:00 2001 From: carole-lavillonniere Date: Mon, 27 Apr 2026 16:05:05 +0200 Subject: [PATCH] fix(runtime): probe ~/.docker/run/docker.sock for Docker Desktop Docker Desktop on macOS uses ~/.docker/run/docker.sock when the /var/run/docker.sock symlink is absent. Extract vmSocketPaths() as single source of truth for findDockerSocket() and isVM(); fixes Lima host socket missing from isVM(). Closes DRG-754 --- internal/runtime/docker.go | 30 ++++++------ internal/runtime/docker_test.go | 84 +++++++++++++++++---------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/internal/runtime/docker.go b/internal/runtime/docker.go index d0e456a9..7bb050d0 100644 --- a/internal/runtime/docker.go +++ b/internal/runtime/docker.go @@ -57,20 +57,24 @@ func NewDockerRuntime(dockerHost string) (*DockerRuntime, error) { return &DockerRuntime{client: cli}, nil } +func vmSocketPaths(home string) []string { + return []string{ + filepath.Join(home, ".docker", "run", "docker.sock"), + filepath.Join(home, ".colima", "default", "docker.sock"), + filepath.Join(home, ".colima", "docker.sock"), + filepath.Join(home, ".orbstack", "run", "docker.sock"), + filepath.Join(home, ".lima", "docker", "sock", "docker.sock"), + } +} + func findDockerSocket() string { - // Lima VM: Docker socket is natively available at the standard path. - // Lima sets LIMA_INSTANCE inside the VM. + // Lima sets LIMA_INSTANCE inside the VM; the socket is at the standard path natively. if os.Getenv("LIMA_INSTANCE") != "" { return "/var/run/docker.sock" } home, _ := os.UserHomeDir() - return probeSocket( - filepath.Join(home, ".colima", "default", "docker.sock"), - filepath.Join(home, ".colima", "docker.sock"), - filepath.Join(home, ".orbstack", "run", "docker.sock"), - filepath.Join(home, ".lima", "docker", "sock", "docker.sock"), - ) + return probeSocket(vmSocketPaths(home)...) } func probeSocket(candidates ...string) string { @@ -82,21 +86,15 @@ func probeSocket(candidates ...string) string { return "" } -// isVM reports whether the Docker daemon is running inside a VM (e.g., Colima, OrbStack). +// isVM reports whether the Docker daemon is running inside a VM (e.g., Docker Desktop, Colima, OrbStack, Lima). // In these cases the socket is remapped inside the VM and the container sees it at // /var/run/docker.sock even if the CLI connects via a user-scoped socket path. func (d *DockerRuntime) isVM() bool { host := d.client.DaemonHost() if strings.HasPrefix(host, "unix://") { socketPath := strings.TrimPrefix(host, "unix://") - // Check for known VM-based Docker socket locations home, _ := os.UserHomeDir() - vmSockets := []string{ - filepath.Join(home, ".colima", "default", "docker.sock"), - filepath.Join(home, ".colima", "docker.sock"), - filepath.Join(home, ".orbstack", "run", "docker.sock"), - } - for _, vmSock := range vmSockets { + for _, vmSock := range vmSocketPaths(home) { if socketPath == vmSock { return true } diff --git a/internal/runtime/docker_test.go b/internal/runtime/docker_test.go index 5644ea5d..bfc9037c 100644 --- a/internal/runtime/docker_test.go +++ b/internal/runtime/docker_test.go @@ -87,45 +87,35 @@ func TestSocketPath_VMDetection(t *testing.T) { t.Cleanup(func() { _ = os.RemoveAll(home) }) t.Setenv("HOME", home) - t.Run("colima socket exists returns remapped path", func(t *testing.T) { - colimaSock := filepath.Join(home, ".colima", "default", "docker.sock") - require.NoError(t, os.MkdirAll(filepath.Dir(colimaSock), 0o755)) - f, err := os.Create(colimaSock) - require.NoError(t, err) - require.NoError(t, f.Close()) - t.Cleanup(func() { - require.NoError(t, os.Remove(colimaSock)) - }) - - cli, err := client.NewClientWithOpts(client.WithHost("unix://" + colimaSock)) - require.NoError(t, err) - rt := &DockerRuntime{client: cli} - assert.Equal(t, "/var/run/docker.sock", rt.SocketPath()) - }) + vmTests := []struct { + name string + relPath string + }{ + {"docker desktop", ".docker/run/docker.sock"}, + {"colima", ".colima/default/docker.sock"}, + {"orbstack", ".orbstack/run/docker.sock"}, + {"lima host", ".lima/docker/sock/docker.sock"}, + } - t.Run("orbstack socket exists returns remapped path", func(t *testing.T) { - orbstackSock := filepath.Join(home, ".orbstack", "run", "docker.sock") - require.NoError(t, os.MkdirAll(filepath.Dir(orbstackSock), 0o755)) - f, err := os.Create(orbstackSock) - require.NoError(t, err) - require.NoError(t, f.Close()) - t.Cleanup(func() { - require.NoError(t, os.Remove(orbstackSock)) + for _, tc := range vmTests { + t.Run(tc.name+" socket returns remapped path", func(t *testing.T) { + sock := filepath.Join(home, filepath.FromSlash(tc.relPath)) + require.NoError(t, os.MkdirAll(filepath.Dir(sock), 0o755)) + require.NoError(t, os.WriteFile(sock, nil, 0o600)) + t.Cleanup(func() { require.NoError(t, os.Remove(sock)) }) + + cli, err := client.NewClientWithOpts(client.WithHost("unix://" + sock)) + require.NoError(t, err) + rt := &DockerRuntime{client: cli} + assert.Equal(t, "/var/run/docker.sock", rt.SocketPath()) }) + } - cli, err := client.NewClientWithOpts(client.WithHost("unix://" + orbstackSock)) - require.NoError(t, err) - rt := &DockerRuntime{client: cli} - assert.Equal(t, "/var/run/docker.sock", rt.SocketPath()) - }) - - t.Run("rootless socket exists returns actual path", func(t *testing.T) { + t.Run("rootless socket returns actual path", func(t *testing.T) { // Use a non-VM socket path (short path to avoid Docker client limit) rootlessSock := "/tmp/lstk-docker.sock" require.NoError(t, os.WriteFile(rootlessSock, nil, 0o600)) - t.Cleanup(func() { - require.NoError(t, os.Remove(rootlessSock)) - }) + t.Cleanup(func() { require.NoError(t, os.Remove(rootlessSock)) }) cli, err := client.NewClientWithOpts(client.WithHost("unix://" + rootlessSock)) require.NoError(t, err) @@ -161,16 +151,28 @@ func TestFindDockerSocket_LimaVM(t *testing.T) { assert.Equal(t, "/var/run/docker.sock", sock) } -func TestFindDockerSocket_IncludesLimaPathOnHost(t *testing.T) { +func TestFindDockerSocket_ProbesVMSockets(t *testing.T) { t.Setenv("LIMA_INSTANCE", "") - tmpDir := t.TempDir() - limaSock := filepath.Join(tmpDir, ".lima", "docker", "sock", "docker.sock") - require.NoError(t, os.MkdirAll(filepath.Dir(limaSock), 0o700)) - require.NoError(t, os.WriteFile(limaSock, nil, 0o600)) + tests := []struct { + name string + relPath string + }{ + {"docker desktop", ".docker/run/docker.sock"}, + {"colima", ".colima/default/docker.sock"}, + {"orbstack", ".orbstack/run/docker.sock"}, + {"lima host", ".lima/docker/sock/docker.sock"}, + } - t.Setenv("HOME", tmpDir) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tmpDir := t.TempDir() + sock := filepath.Join(tmpDir, filepath.FromSlash(tc.relPath)) + require.NoError(t, os.MkdirAll(filepath.Dir(sock), 0o700)) + require.NoError(t, os.WriteFile(sock, nil, 0o600)) + t.Setenv("HOME", tmpDir) - result := findDockerSocket() - assert.Equal(t, limaSock, result) + assert.Equal(t, sock, findDockerSocket()) + }) + } }