test: consolidate port allocation into shared test/testutil package (#8982)

* test: consolidate port allocation into shared test/testutil package

Move duplicated port allocation logic from 15+ test files into a single
shared package at test/testutil/. This fixes a port collision bug where
independently allocated ports could overlap via the gRPC offset
(port+10000), causing weed mini to reject the configuration.

The shared package provides:
- AllocatePorts: atomic allocation of N unique ports
- AllocateMiniPorts/MustFreeMiniPorts: gRPC-offset-aware allocation
  that prevents port A+10000 == port B collisions
- WaitForPort, WaitForService, FindBindIP, WriteIAMConfig, HasDocker

* test: address review feedback and fix FUSE build

- Revert fuse_integration change: it has its own go.mod and cannot
  import the shared testutil package
- AllocateMiniPorts: hold all listeners open until the entire batch is
  allocated, preventing race conditions where other processes steal ports
- HasDocker: add 5s context timeout to avoid hanging on stalled Docker
- WaitForService: only treat 2xx HTTP status codes as ready

* test: use global rand in AllocateMiniPorts for better seeding

Go 1.20+ auto-seeds the global rand generator. Using it avoids
identical sequences when multiple tests call at the same nanosecond.

* test: revert WaitForService status code check

S3 endpoints return non-2xx (e.g. 403) on bare GET requests, so
requiring 2xx caused the S3 integration test to time out. Any HTTP
response is sufficient proof that the service is running.

* test: fix gofmt formatting in s3tables test files
This commit is contained in:
Chris Lu authored and GitHub committed 2026-04-08 11:30:02 -07:00
1 parent ac12a735c7
commit fbe758efa8
26 files changed
+308 -716

No files matched your search

@@ -4,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
@@ -17,6 +16,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/seaweedfs/seaweedfs/test/testutil"
"github.com/seaweedfs/seaweedfs/test/volume_server/framework"
"github.com/seaweedfs/seaweedfs/test/volume_server/matrix"
"github.com/seaweedfs/seaweedfs/weed/pb/remote_pb"
@@ -24,29 +24,6 @@ import (
)
// findAvailablePort finds a free TCP port on localhost.
func findAvailablePort() (int, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
port := l.Addr().(*net.TCPAddr).Port
l.Close()
return port, nil
}
// waitForPort waits until a TCP port is listening, up to timeout.
func waitForPort(port int, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), 500*time.Millisecond)
if err == nil {
conn.Close()
return nil
}
time.Sleep(200 * time.Millisecond)
}
return fmt.Errorf("port %d not listening after %v", port, timeout)
}
// startWeedMini starts a weed mini subprocess and returns the S3 endpoint and cleanup func.
func startWeedMini(t *testing.T) (s3Endpoint string, cleanup func()) {
@@ -60,10 +37,11 @@ func startWeedMini(t *testing.T) (s3Endpoint string, cleanup func()) {
}
}
miniMasterPort, _ := findAvailablePort()
miniVolumePort, _ := findAvailablePort()
miniFilerPort, _ := findAvailablePort()
miniS3Port, _ := findAvailablePort()
miniPorts := testutil.MustFreeMiniPorts(t, []string{"Master", "Volume", "Filer", "S3"})
miniMasterPort := miniPorts[0]
miniVolumePort := miniPorts[1]
miniFilerPort := miniPorts[2]
miniS3Port := miniPorts[3]
miniDir := t.TempDir()
os.WriteFile(filepath.Join(miniDir, "security.toml"), []byte("# empty\n"), 0644)
@@ -89,11 +67,11 @@ func startWeedMini(t *testing.T) (s3Endpoint string, cleanup func()) {
t.Fatalf("start weed mini: %v", err)
}
if err := waitForPort(miniS3Port, 30*time.Second); err != nil {
if !testutil.WaitForPort(miniS3Port, 30*time.Second) {
cancel()
miniCmd.Wait()
logFile.Close()
t.Fatalf("weed mini S3 not ready: %v", err)
t.Fatalf("weed mini S3 not ready on port %d", miniS3Port)
}
t.Logf("weed mini S3 ready on port %d", miniS3Port)