diff --git a/test/fuse_integration/framework_test.go b/test/fuse_integration/framework_test.go index 9fc3e28e6..0c8b5ab39 100644 --- a/test/fuse_integration/framework_test.go +++ b/test/fuse_integration/framework_test.go @@ -86,35 +86,47 @@ func NewFuseTestFramework(t *testing.T, config *TestConfig) *FuseTestFramework { } } -// freePort asks the OS for a free TCP port in a range where the gRPC -// offset (port + 10000) won't collide with well-known ports. -// Stay below the Linux ephemeral floor (32768) so the kernel does not -// reuse the chosen port for an outbound connection between close() here -// and re-bind in the child "weed mini" process. +// grpcPortOffset mirrors weed's HTTP->gRPC port convention (gRPC = HTTP + offset); +// "weed mount" derives the filer gRPC port from the filer HTTP address the same way. +const grpcPortOffset = 10000 + +// freePort returns a free filer HTTP port whose gRPC sibling (port+grpcPortOffset) +// is also free. Both must stay below the Linux ephemeral floor (32768): a gRPC port +// above it can be transiently grabbed by an outbound connection, forcing mini to +// relocate its filer gRPC port while "weed mount" keeps dialing HTTP+10000 — the +// mount then never connects and the test times out. Capping HTTP at 22000 keeps the +// gRPC port at or below 32000. func freePort(t *testing.T) int { t.Helper() const ( minServicePort = 20000 - maxServicePort = 32000 + maxServicePort = 22000 ) portCount := maxServicePort - minServicePort + 1 start := minServicePort + int(time.Now().UnixNano()%int64(portCount)) - for attempt := 0; attempt < 512; attempt++ { + for attempt := 0; attempt < portCount; attempt++ { port := minServicePort + (start-minServicePort+attempt)%portCount - l, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) - if err != nil { - continue + if portIsFree(port) && portIsFree(port+grpcPortOffset) { + return port } - l.Close() - return port } - t.Fatalf("failed to allocate port <= %d after repeated attempts", maxServicePort) + t.Fatalf("failed to allocate a free HTTP/gRPC port pair in [%d,%d]", minServicePort, maxServicePort) return 0 } +// portIsFree reports whether a TCP port can currently be bound on 127.0.0.1. +func portIsFree(port int) bool { + l, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) + if err != nil { + return false + } + l.Close() + return true +} + // Setup starts "weed mini" and mounts the FUSE filesystem. func (f *FuseTestFramework) Setup(config *TestConfig) error { if f.isSetup {