test: keep AllocateMiniPorts off weed mini default ports

Random allocation could pick 33646 = admin.port (23646) + GrpcPortOffset.
weed mini reserves that as Admin's gRPC port even when the test only
overrides Master/Filer/S3/Iceberg, so the explicit Filer flag failed
with "reserved for gRPC calculation" and TestRisingWaveIcebergCatalog
flaked. Pre-seed the reserved set with every mini default HTTP port
plus its +10000 offset so a random pick (or its own gRPC offset) cannot
land on a service the caller left at its default.
This commit is contained in:
Chris Lu
2026-05-26 16:48:46 -07:00
parent 29eec2f111
commit 4f17c6661a
2 changed files with 48 additions and 2 deletions

View File

@@ -11,6 +11,29 @@ import (
// GrpcPortOffset is the offset weed mini uses to derive gRPC ports from HTTP ports.
const GrpcPortOffset = 10000
// miniDefaultPorts are the weed mini flag defaults (see weed/command/mini.go).
// A test only overrides services it uses; unspecified services still bind
// these defaults, so allocation must avoid handing them out (or any value
// whose gRPC offset would collide with them).
var miniDefaultPorts = []int{
9333, // master.port
8888, // filer.port
9340, // volume.port
8333, // s3.port
8181, // s3.port.iceberg
7333, // webdav.port
23646, // admin.port
}
func reservedMiniPorts() map[int]bool {
r := make(map[int]bool, len(miniDefaultPorts)*2)
for _, p := range miniDefaultPorts {
r[p] = true
r[p+GrpcPortOffset] = true
}
return r
}
// AllocatePorts allocates count unique free ports atomically.
// All listeners are held open until every port is obtained, preventing
// the OS from recycling a port between successive allocations.
@@ -63,7 +86,7 @@ func AllocateMiniPorts(count int) ([]int, error) {
minPort = 10000
maxPort = 55000
)
reserved := make(map[int]bool)
reserved := reservedMiniPorts()
ports := make([]int, 0, count)
var listeners []net.Listener
defer func() {
@@ -135,7 +158,7 @@ func AllocatePortSet(miniCount, regularCount int) (mini []int, regular []int, er
minPort = 10000
maxPort = 55000
)
reserved := make(map[int]bool)
reserved := reservedMiniPorts()
mini = make([]int, 0, miniCount)
var listeners []net.Listener
defer func() {

View File

@@ -2,6 +2,29 @@ package testutil
import "testing"
// AllocateMiniPorts must never hand out a port that weed mini will reserve
// for one of its default services (or that default's gRPC offset). A real
// failure: Filer was given 33646 (Admin default 23646 + GrpcPortOffset),
// which mini then refused as "reserved for gRPC calculation".
func TestAllocateMiniPortsAvoidsMiniDefaults(t *testing.T) {
reserved := reservedMiniPorts()
for iter := 0; iter < 200; iter++ {
ports, err := AllocateMiniPorts(4)
if err != nil {
t.Fatalf("iter %d: AllocateMiniPorts: %v", iter, err)
}
for _, p := range ports {
if reserved[p] {
t.Fatalf("iter %d: allocated port %d is a mini default (or gRPC offset)", iter, p)
}
if reserved[p+GrpcPortOffset] {
t.Fatalf("iter %d: allocated port %d has gRPC offset %d colliding with a mini default",
iter, p, p+GrpcPortOffset)
}
}
}
}
func TestAllocatePortSetNoGrpcCollision(t *testing.T) {
// Run a few iterations to catch the OS-recycles-just-closed-port race
// that previously hit regular ports when the mini gRPC offset was freed