Skip to content

Commit f58b66d

Browse files
committed
Skip stale Docker sockets when probing for the daemon
1 parent c3e3a22 commit f58b66d

2 files changed

Lines changed: 64 additions & 15 deletions

File tree

internal/runtime/docker.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"log"
9+
"net"
910
"net/http"
1011
"os"
1112
"os/exec"
@@ -77,11 +78,20 @@ func findDockerSocket() string {
7778
return probeSocket(vmSocketPaths(home)...)
7879
}
7980

81+
// Dial as well as stat: a socket file may linger after its daemon is gone (e.g. a
82+
// stale ~/.docker/run/docker.sock from an uninstalled Docker Desktop) and would
83+
// otherwise shadow a live socket later in the list.
8084
func probeSocket(candidates ...string) string {
8185
for _, sock := range candidates {
82-
if _, err := os.Stat(sock); err == nil {
83-
return sock
86+
if _, err := os.Stat(sock); err != nil {
87+
continue
88+
}
89+
conn, err := net.DialTimeout("unix", sock, 200*time.Millisecond)
90+
if err != nil {
91+
continue
8492
}
93+
_ = conn.Close()
94+
return sock
8595
}
8696
return ""
8797
}

internal/runtime/docker_test.go

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runtime
22

33
import (
44
"errors"
5+
"net"
56
"os"
67
"path/filepath"
78
"testing"
@@ -11,31 +12,69 @@ import (
1112
"github.com/stretchr/testify/require"
1213
)
1314

14-
func TestProbeSocket_ReturnsFirstExisting(t *testing.T) {
15-
dir := t.TempDir()
15+
// macOS caps Unix socket paths at ~104 chars; t.TempDir() under /var/folders/...
16+
// can exceed that, so tests that bind sockets must use /tmp.
17+
func shortTempDir(t *testing.T) string {
18+
t.Helper()
19+
dir, err := os.MkdirTemp("/tmp", "lstk-*")
20+
require.NoError(t, err)
21+
t.Cleanup(func() { _ = os.RemoveAll(dir) })
22+
return dir
23+
}
24+
25+
func listenUnixSocket(t *testing.T, path string) {
26+
t.Helper()
27+
l, err := net.Listen("unix", path)
28+
require.NoError(t, err)
29+
t.Cleanup(func() { _ = l.Close() })
30+
}
31+
32+
func TestProbeSocket_ReturnsFirstLive(t *testing.T) {
33+
dir := shortTempDir(t)
1634
sock1 := filepath.Join(dir, "first.sock")
1735
sock2 := filepath.Join(dir, "second.sock")
1836

19-
require.NoError(t, os.WriteFile(sock1, nil, 0o600))
20-
require.NoError(t, os.WriteFile(sock2, nil, 0o600))
37+
listenUnixSocket(t, sock1)
38+
listenUnixSocket(t, sock2)
2139

2240
assert.Equal(t, sock1, probeSocket(sock1, sock2))
2341
}
2442

25-
func TestProbeSocket_SkipsMissingAndReturnsExisting(t *testing.T) {
26-
dir := t.TempDir()
43+
func TestProbeSocket_SkipsMissingAndReturnsLive(t *testing.T) {
44+
dir := shortTempDir(t)
2745
missing := filepath.Join(dir, "missing.sock")
28-
existing := filepath.Join(dir, "existing.sock")
46+
live := filepath.Join(dir, "live.sock")
2947

30-
require.NoError(t, os.WriteFile(existing, nil, 0o600))
48+
listenUnixSocket(t, live)
3149

32-
assert.Equal(t, existing, probeSocket(missing, existing))
50+
assert.Equal(t, live, probeSocket(missing, live))
51+
}
52+
53+
func TestProbeSocket_SkipsStaleSocketForLiveOne(t *testing.T) {
54+
dir := shortTempDir(t)
55+
stale := filepath.Join(dir, "stale.sock")
56+
live := filepath.Join(dir, "live.sock")
57+
58+
require.NoError(t, os.WriteFile(stale, nil, 0o600))
59+
listenUnixSocket(t, live)
60+
61+
assert.Equal(t, live, probeSocket(stale, live))
3362
}
3463

3564
func TestProbeSocket_ReturnsEmptyWhenNoneExist(t *testing.T) {
3665
assert.Equal(t, "", probeSocket("/no/such/path.sock", "/also/missing.sock"))
3766
}
3867

68+
func TestProbeSocket_ReturnsEmptyWhenAllStale(t *testing.T) {
69+
dir := shortTempDir(t)
70+
stale1 := filepath.Join(dir, "stale1.sock")
71+
stale2 := filepath.Join(dir, "stale2.sock")
72+
require.NoError(t, os.WriteFile(stale1, nil, 0o600))
73+
require.NoError(t, os.WriteFile(stale2, nil, 0o600))
74+
75+
assert.Equal(t, "", probeSocket(stale1, stale2))
76+
}
77+
3978
func TestProbeSocket_ReturnsEmptyForNoCandidates(t *testing.T) {
4079
assert.Equal(t, "", probeSocket())
4180
}
@@ -166,11 +205,11 @@ func TestFindDockerSocket_ProbesVMSockets(t *testing.T) {
166205

167206
for _, tc := range tests {
168207
t.Run(tc.name, func(t *testing.T) {
169-
tmpDir := t.TempDir()
170-
sock := filepath.Join(tmpDir, filepath.FromSlash(tc.relPath))
208+
home := shortTempDir(t)
209+
sock := filepath.Join(home, filepath.FromSlash(tc.relPath))
171210
require.NoError(t, os.MkdirAll(filepath.Dir(sock), 0o700))
172-
require.NoError(t, os.WriteFile(sock, nil, 0o600))
173-
t.Setenv("HOME", tmpDir)
211+
listenUnixSocket(t, sock)
212+
t.Setenv("HOME", home)
174213

175214
assert.Equal(t, sock, findDockerSocket())
176215
})

0 commit comments

Comments
 (0)