From 5d5ea63b3f7b27aa73e1e24f7f67db15e85bb7e3 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 17 Aug 2026 14:46:25 -0700 Subject: [PATCH] Fix what the Go 1.26 language bump breaks (#10794) * worker: log the balance move stage through a constant format string Go 1.26's printf analyzer now follows printf wrappers reached through an interface, so passing the stage straight to Logger.Info is a vet failure. * s3api: bracket the IPv6 host in the signature test URL A bare IPv6 literal is legal in a Host header but never in a URL. Go 1.26 stopped parsing it leniently, so carry the two forms separately and set r.Host to the value the client would actually have signed. * mini: bracket IPv6 addresses in the readiness probe URLs An IPv6-only host hands mini a bare literal, and %s:%d pasted it into a URL unbracketed. Under Go 1.26 that URL no longer parses, so waiting for the admin server never succeeds and mini refuses to start. --- weed/command/mini.go | 6 +++--- weed/s3api/auto_signature_v4_test.go | 13 ++++++++++++- weed/worker/tasks/balance/balance_task.go | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/weed/command/mini.go b/weed/command/mini.go index c02a6c274..c5ce98bfa 100644 --- a/weed/command/mini.go +++ b/weed/command/mini.go @@ -1473,7 +1473,7 @@ func startMiniService(name string, fn func(), port int) { // waitForServiceReady pings the service HTTP endpoint to check if it's ready // to accept connections, and reports the transition to the progress board. func waitForServiceReady(name string, port int, bindIp string) { - address := fmt.Sprintf("http://%s:%d", bindIp, port) + address := "http://" + util.JoinHostPort(bindIp, port) healthAddr := getHealthCheckAddr(address) maxAttempts := 30 // 30 * 200ms = 6 seconds max wait attempt := 0 @@ -1595,7 +1595,7 @@ func startMiniAdminWithWorker(allServicesReady chan struct{}) { }() // Wait for admin server's HTTP port to be ready before launching worker - adminAddr := fmt.Sprintf("http://%s:%d", bindIp, *miniAdminOptions.port) + adminAddr := "http://" + util.JoinHostPort(bindIp, *miniAdminOptions.port) if err := waitForAdminServerReady(ctx, adminAddr); err != nil { // If the parent context was cancelled (e.g. a previous in-process // mini run is being torn down), bail out gracefully instead of @@ -1900,7 +1900,7 @@ func printWelcomeMessage() { // Best-effort: any error returns ok=false so the welcome banner simply omits // the lines. func miniVolumeCounts() (max, free int64, ok bool) { - url := getHealthCheckAddr(fmt.Sprintf("http://%s:%d/dir/status", *miniIp, *miniMasterOptions.port)) + url := getHealthCheckAddr("http://" + util.JoinHostPort(*miniIp, *miniMasterOptions.port) + "/dir/status") client := &http.Client{Timeout: 2 * time.Second} resp, err := client.Get(url) if err != nil { diff --git a/weed/s3api/auto_signature_v4_test.go b/weed/s3api/auto_signature_v4_test.go index 8d8ba104f..2b34b9dc6 100644 --- a/weed/s3api/auto_signature_v4_test.go +++ b/weed/s3api/auto_signature_v4_test.go @@ -423,6 +423,7 @@ func TestSignatureV4WithoutProxy(t *testing.T) { tests := []struct { name string host string + urlHost string // request-URL form of host, when host is not valid in a URL proto string expectedHost string }{ @@ -489,12 +490,14 @@ func TestSignatureV4WithoutProxy(t *testing.T) { { name: "IPv6 HTTP without port", host: "::1", + urlHost: "[::1]", proto: "http", expectedHost: "::1", }, { name: "IPv6 HTTPS without port", host: "::1", + urlHost: "[::1]", proto: "https", expectedHost: "::1", }, @@ -504,11 +507,19 @@ func TestSignatureV4WithoutProxy(t *testing.T) { t.Run(tt.name, func(t *testing.T) { iam := newTestIAM() + // A bare IPv6 literal is legal in a Host header but not in a URL, so the + // two forms are carried separately. + urlHost := tt.urlHost + if urlHost == "" { + urlHost = tt.host + } + // Create a request - r, err := newTestRequest("GET", tt.proto+"://"+tt.host+"/test-bucket/test-object", 0, nil) + r, err := newTestRequest("GET", tt.proto+"://"+urlHost+"/test-bucket/test-object", 0, nil) if err != nil { t.Fatalf("Failed to create test request: %v", err) } + r.Host = tt.host // Set the mux variables manually since we're not going through the actual router r = mux.SetURLVars(r, map[string]string{ diff --git a/weed/worker/tasks/balance/balance_task.go b/weed/worker/tasks/balance/balance_task.go index 7bf1a1b93..87d78091d 100644 --- a/weed/worker/tasks/balance/balance_task.go +++ b/weed/worker/tasks/balance/balance_task.go @@ -81,7 +81,7 @@ func (t *BalanceTask) Execute(ctx context.Context, params *worker_pb.TaskParams) IoBytePerSecond: balanceParams.IoBytePerSecond, Progress: func(percent float64, stage string) { t.ReportProgress(percent) - t.GetLogger().Info(stage) + t.GetLogger().Info("move stage: %s", stage) }, }) if err != nil {