Files
seaweedfs/test/volume_server/framework/cluster_with_filer.go
Chris Lu fbe758efa8 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
2026-04-08 11:30:02 -07:00

93 lines
2.3 KiB
Go

package framework
import (
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"testing"
"github.com/seaweedfs/seaweedfs/test/testutil"
"github.com/seaweedfs/seaweedfs/test/volume_server/matrix"
)
type ClusterWithFiler struct {
*Cluster
filerCmd *exec.Cmd
filerPort int
filerGrpcPort int
}
func StartSingleVolumeClusterWithFiler(t testing.TB, profile matrix.Profile) *ClusterWithFiler {
t.Helper()
baseCluster := StartSingleVolumeCluster(t, profile)
ports, err := testutil.AllocatePorts(2)
if err != nil {
t.Fatalf("allocate filer ports: %v", err)
}
filerDataDir := filepath.Join(baseCluster.baseDir, "filer")
if mkErr := os.MkdirAll(filerDataDir, 0o755); mkErr != nil {
t.Fatalf("create filer data dir: %v", mkErr)
}
logFile, err := os.Create(filepath.Join(baseCluster.logsDir, "filer.log"))
if err != nil {
t.Fatalf("create filer log file: %v", err)
}
filerPort := ports[0]
filerGrpcPort := ports[1]
args := []string{
"-config_dir=" + baseCluster.configDir,
"filer",
"-master=127.0.0.1:" + strconv.Itoa(baseCluster.masterPort),
"-ip=127.0.0.1",
"-port=" + strconv.Itoa(filerPort),
"-port.grpc=" + strconv.Itoa(filerGrpcPort),
"-defaultStoreDir=" + filerDataDir,
}
filerCmd := exec.Command(baseCluster.weedBinary, args...)
filerCmd.Dir = baseCluster.baseDir
filerCmd.Stdout = logFile
filerCmd.Stderr = logFile
if err = filerCmd.Start(); err != nil {
t.Fatalf("start filer: %v", err)
}
if err = baseCluster.waitForTCP(net.JoinHostPort("127.0.0.1", strconv.Itoa(filerGrpcPort))); err != nil {
filerLogTail := baseCluster.tailLog("filer.log")
stopProcess(filerCmd)
t.Fatalf("wait for filer grpc readiness: %v\nfiler log tail:\n%s", err, filerLogTail)
}
t.Cleanup(func() {
stopProcess(filerCmd)
})
return &ClusterWithFiler{
Cluster: baseCluster,
filerCmd: filerCmd,
filerPort: filerPort,
filerGrpcPort: filerGrpcPort,
}
}
func (c *ClusterWithFiler) FilerAddress() string {
return net.JoinHostPort("127.0.0.1", strconv.Itoa(c.filerPort))
}
func (c *ClusterWithFiler) FilerGRPCAddress() string {
return net.JoinHostPort("127.0.0.1", strconv.Itoa(c.filerGrpcPort))
}
func (c *ClusterWithFiler) FilerServerAddress() string {
return fmt.Sprintf("%s.%d", c.FilerAddress(), c.filerGrpcPort)
}