Browse Source

enhance port check logic and avoid infinite loop when all ports are exhuasted

abhishek9686 2 weeks ago
parent
commit
0c49c982d0
1 changed files with 75 additions and 9 deletions
  1. 75 9
      logic/hosts.go

+ 75 - 9
logic/hosts.go

@@ -621,24 +621,29 @@ func CheckHostPorts(h *models.Host) (changed bool) {
 	hostPortMutex.Lock()
 	defer hostPortMutex.Unlock()
 	utils.TraceCaller()
-	fmt.Println("=======> CHECKING HOST PORTS for HOST", h.Name, h.ListenPort)
+	fmt.Println("=======> CHECKING HOST PORTS for HOST", h.Name, h.ListenPort, h.EndpointIP.String())
 	defer func() {
-		fmt.Println("=======> CHECKED HOST PORTS for HOST", h.Name, h.ListenPort)
+		fmt.Println("=======> CHECKED HOST PORTS for HOST", h.Name, h.ListenPort, h.EndpointIP.String(), "changed:", changed)
 	}()
-	portsInUse := make(map[int]bool, 0)
-	hosts, err := GetAllHosts()
-	if err != nil {
+
+	if h.EndpointIP == nil {
 		return
 	}
+
 	originalPort := h.ListenPort
 	defer func() {
 		if originalPort != h.ListenPort {
 			changed = true
 		}
 	}()
-	if h.EndpointIP == nil {
+
+	hosts, err := GetAllHosts()
+	if err != nil {
 		return
 	}
+
+	// Build map of ports in use by other hosts with the same endpoint
+	portsInUse := make(map[int]bool)
 	for _, host := range hosts {
 		if host.ID.String() == h.ID.String() {
 			// skip self
@@ -650,19 +655,80 @@ func CheckHostPorts(h *models.Host) (changed bool) {
 		if !host.EndpointIP.Equal(h.EndpointIP) {
 			continue
 		}
-		portsInUse[host.ListenPort] = true
+		if host.ListenPort > 0 {
+			portsInUse[host.ListenPort] = true
+			fmt.Println("=======> Found host with same endpoint using port:", host.Name, host.ListenPort)
+		}
 	}
-	// iterate until port is not found or max iteration is reached
-	for i := 0; portsInUse[h.ListenPort] && i < maxPort-minPort+1; i++ {
+	fmt.Println("=======> Ports in use for endpoint", h.EndpointIP.String(), ":", portsInUse)
+
+	// If current port is not in use, no change needed
+	if !portsInUse[h.ListenPort] {
+		return
+	}
+
+	// Find an available port
+	maxIterations := maxPort - minPort + 1
+	checkedPorts := make(map[int]bool)
+	initialPort := h.ListenPort
+
+	for i := 0; i < maxIterations; i++ {
+		// Special case: skip port 443 by jumping to 51821
 		if h.ListenPort == 443 {
 			h.ListenPort = 51821
 		} else {
 			h.ListenPort++
 		}
+
+		// Wrap around if we exceed maxPort
 		if h.ListenPort > maxPort {
 			h.ListenPort = minPort
 		}
+
+		// Avoid infinite loop - if we've checked this port before, we've cycled through all
+		if checkedPorts[h.ListenPort] {
+			// All ports are in use, keep original port
+			h.ListenPort = originalPort
+			fmt.Println("=======> All ports checked, keeping original port:", originalPort)
+			break
+		}
+		checkedPorts[h.ListenPort] = true
+
+		// Re-read hosts to get the latest state (in case another host just changed its port)
+		// This is important to avoid conflicts when multiple hosts are being processed
+		latestHosts, err := GetAllHosts()
+		if err == nil {
+			// Update portsInUse with latest state
+			for _, host := range latestHosts {
+				if host.ID.String() == h.ID.String() {
+					continue
+				}
+				if host.EndpointIP == nil {
+					continue
+				}
+				if !host.EndpointIP.Equal(h.EndpointIP) {
+					continue
+				}
+				if host.ListenPort > 0 {
+					portsInUse[host.ListenPort] = true
+				}
+			}
+		}
+
+		// If this port is not in use, we found an available port
+		if !portsInUse[h.ListenPort] {
+			fmt.Println("=======> Found available port:", h.ListenPort)
+			break
+		}
+
+		// If we've wrapped back to the initial port, all ports are in use
+		if h.ListenPort == initialPort && i > 0 {
+			h.ListenPort = originalPort
+			fmt.Println("=======> Wrapped back to initial port, keeping original:", originalPort)
+			break
+		}
 	}
+
 	return
 }