Repository: minio/minio (language: go)

Issue:
Regression related to starting server on port 0
If I understand correctly based on previous issues, `minio` does not allow starting even a SNSD instance with the S3-API on port 0 or letting the OS choose a port.

https://github.com/minio/minio/issues/3314
https://github.com/minio/minio/pull/3318
https://github.com/minio/minio/pull/6179

## Expected Behavior

The server should do one of two things:
1. The server should refuse to start (based on the PRs I can find).
2. If the server can start, it should report the assigned port number.

## Current Behavior

The server starts but does not report the port number.

## Possible Solution

It seems like the console endpoint is already starting on a random available port. I wonder if it is possible to do the same for SNSD setup (typically for integration test environments). 

## Your Environment

```
darwin-arm64 minio.RELEASE.2023-09-04T19-57-37Z
sha256: 1226866f00503fd8dbf2133412c3505e724b845ac71792c806535cd03110c89b
```


Discussion / hints:
We have never supported port :0, we will duly error out.

Fix patch:
diff --git a/cmd/common-main.go b/cmd/common-main.go
index 224e86c3e0915..3bd24d082a371 100644
--- a/cmd/common-main.go
+++ b/cmd/common-main.go
@@ -391,7 +391,7 @@ func handleCommonCmdArgs(ctx *cli.Context) {
 	if consoleAddr == "" {
 		p, err := xnet.GetFreePort()
 		if err != nil {
-			logger.FatalIf(err, "Unable to get free port for console on the host")
+			logger.FatalIf(err, "Unable to get free port for Console UI on the host")
 		}
 		consoleAddr = net.JoinHostPort("", p.String())
 	}
@@ -405,6 +405,15 @@ func handleCommonCmdArgs(ctx *cli.Context) {
 	}
 
 	globalMinioHost, globalMinioPort = mustSplitHostPort(addr)
+	if globalMinioPort == "0" {
+		p, err := xnet.GetFreePort()
+		if err != nil {
+			logger.FatalIf(err, "Unable to get free port for S3 API on the host")
+		}
+		globalMinioPort = p.String()
+		globalDynamicAPIPort = true
+	}
+
 	globalMinioConsoleHost, globalMinioConsolePort = mustSplitHostPort(consoleAddr)
 
 	if globalMinioPort == globalMinioConsolePort {
diff --git a/cmd/globals.go b/cmd/globals.go
index 2a6f746e20c59..3a151d2e37e1c 100644
--- a/cmd/globals.go
+++ b/cmd/globals.go
@@ -402,6 +402,9 @@ var (
 
 	// Captures all batch jobs metrics globally
 	globalBatchJobsMetrics batchJobMetrics
+
+	// Indicates if server was started as `--address ":0"`
+	globalDynamicAPIPort bool
 	// Add new variable global values here.
 )
 
diff --git a/cmd/server-main.go b/cmd/server-main.go
index d2a43f6fdd970..12a635935ed5f 100644
--- a/cmd/server-main.go
+++ b/cmd/server-main.go
@@ -255,6 +255,16 @@ func serverHandleCmdArgs(ctx *cli.Context) {
 	logger.FatalIf(err, "Invalid command line arguments")
 	globalNodes = globalEndpoints.GetNodes()
 
+	globalIsErasure = (setupType == ErasureSetupType)
+	globalIsDistErasure = (setupType == DistErasureSetupType)
+	if globalIsDistErasure {
+		globalIsErasure = true
+	}
+	globalIsErasureSD = (setupType == ErasureSDSetupType)
+	if globalDynamicAPIPort && globalIsDistErasure {
+		logger.FatalIf(errInvalidArgument, "Invalid --address=\"%s\", port '0' is not allowed in a distributed erasure coded setup", ctx.String("address"))
+	}
+
 	globalLocalNodeName = GetLocalPeer(globalEndpoints, globalMinioHost, globalMinioPort)
 	nodeNameSum := sha256.Sum256([]byte(globalLocalNodeName))
 	globalLocalNodeNameHex = hex.EncodeToString(nodeNameSum[:])
@@ -289,13 +299,6 @@ func serverHandleCmdArgs(ctx *cli.Context) {
 	// To avoid this error situation we check for port availability.
 	logger.FatalIf(xhttp.CheckPortAvailability(globalMinioHost, globalMinioPort, globalTCPOptions), "Unable to start the server")
 
-	globalIsErasure = (setupType == ErasureSetupType)
-	globalIsDistErasure = (setupType == DistErasureSetupType)
-	if globalIsDistErasure {
-		globalIsErasure = true
-	}
-	globalIsErasureSD = (setupType == ErasureSDSetupType)
-
 	globalConnReadDeadline = ctx.Duration("conn-read-deadline")
 	globalConnWriteDeadline = ctx.Duration("conn-write-deadline")
 }


Test patch: