mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 10:19:07 +00:00
Fix RQLite advertised addresses for proper cluster formation
- Add automatic external IP detection for RQLite advertised addresses - Use 0.0.0.0 for binding but actual IP for advertising to other nodes - Add -http-adv-addr and -raft-adv-addr parameters to RQLite startup - Resolves 'advertised HTTP address is not routable' error - Enables proper RQLite cluster formation between nodes
This commit is contained in:
parent
2181b5ced0
commit
79efd7b2c5
@ -3,10 +3,12 @@ package database
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rqlite/gorqlite"
|
"github.com/rqlite/gorqlite"
|
||||||
@ -41,12 +43,25 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
|
|||||||
return fmt.Errorf("failed to create RQLite data directory: %w", err)
|
return fmt.Errorf("failed to create RQLite data directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the external IP address for advertising
|
||||||
|
externalIP, err := r.getExternalIP()
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Warn("Failed to get external IP, using localhost", zap.Error(err))
|
||||||
|
externalIP = "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
// Build RQLite command
|
// Build RQLite command
|
||||||
args := []string{
|
args := []string{
|
||||||
"-http-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLitePort),
|
"-http-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLitePort),
|
||||||
"-raft-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLiteRaftPort),
|
"-raft-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLiteRaftPort),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add advertised addresses if we have an external IP
|
||||||
|
if externalIP != "localhost" {
|
||||||
|
args = append(args, "-http-adv-addr", fmt.Sprintf("%s:%d", externalIP, r.config.RQLitePort))
|
||||||
|
args = append(args, "-raft-adv-addr", fmt.Sprintf("%s:%d", externalIP, r.config.RQLiteRaftPort))
|
||||||
|
}
|
||||||
|
|
||||||
// Add join address if specified (for non-bootstrap or secondary bootstrap nodes)
|
// Add join address if specified (for non-bootstrap or secondary bootstrap nodes)
|
||||||
if r.config.RQLiteJoinAddress != "" {
|
if r.config.RQLiteJoinAddress != "" {
|
||||||
args = append(args, "-join", r.config.RQLiteJoinAddress)
|
args = append(args, "-join", r.config.RQLiteJoinAddress)
|
||||||
@ -168,3 +183,96 @@ func (r *RQLiteManager) Stop() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getExternalIP attempts to get the external IP address of this machine
|
||||||
|
func (r *RQLiteManager) getExternalIP() (string, error) {
|
||||||
|
// Method 1: Try using `ip route get` to find the IP used to reach the internet
|
||||||
|
if output, err := exec.Command("ip", "route", "get", "8.8.8.8").Output(); err == nil {
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, "src") {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
for i, part := range parts {
|
||||||
|
if part == "src" && i+1 < len(parts) {
|
||||||
|
ip := parts[i+1]
|
||||||
|
if net.ParseIP(ip) != nil {
|
||||||
|
r.logger.Debug("Found external IP via ip route", zap.String("ip", ip))
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method 2: Get all network interfaces and find non-localhost, non-private IPs
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer public IPs over private IPs
|
||||||
|
if ip.To4() != nil && !ip.IsPrivate() {
|
||||||
|
r.logger.Debug("Found public IP", zap.String("ip", ip.String()))
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method 3: Fall back to private IPs if no public IP found
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use any IPv4 address
|
||||||
|
if ip.To4() != nil {
|
||||||
|
r.logger.Debug("Found private IP", zap.String("ip", ip.String()))
|
||||||
|
return ip.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("no suitable IP address found")
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user