feat: add configurable RQLite host advertising modes (auto/localhost/ip)

This commit is contained in:
anonpenguin 2025-08-09 08:19:18 +03:00
parent 05798471dd
commit a59d0f1fd6
5 changed files with 38 additions and 20 deletions

View File

@ -26,26 +26,26 @@ test:
# Run bootstrap node explicitly
run-node:
@echo "Starting BOOTSTRAP node (role=bootstrap)..."
go run cmd/node/main.go -role bootstrap -data ./data/bootstrap
go run cmd/node/main.go -role bootstrap -data ./data/bootstrap -advertise localhost
# Run second node (regular) - requires BOOTSTRAP multiaddr
# Usage: make run-node2 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<ID> HTTP=5002 RAFT=7002
run-node2:
@echo "Starting REGULAR node2 (role=node)..."
@if [ -z "$(BOOTSTRAP)" ]; then echo "ERROR: Provide BOOTSTRAP multiaddr: make run-node2 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<ID> [HTTP=5002 RAFT=7002]"; exit 1; fi
go run cmd/node/main.go -role node -id node2 -data ./data/node2 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5002} -rqlite-raft-port $${RAFT:-7002}
go run cmd/node/main.go -role node -id node2 -data ./data/node2 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5002} -rqlite-raft-port $${RAFT:-7002} -advertise $${ADVERTISE:-localhost}
# Run third node (regular) - requires BOOTSTRAP multiaddr
# Usage: make run-node3 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<ID> HTTP=5003 RAFT=7003
run-node3:
@echo "Starting REGULAR node3 (role=node)..."
@if [ -z "$(BOOTSTRAP)" ]; then echo "ERROR: Provide BOOTSTRAP multiaddr: make run-node3 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<ID> [HTTP=5003 RAFT=7003]"; exit 1; fi
go run cmd/node/main.go -role node -id node3 -data ./data/node3 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5003} -rqlite-raft-port $${RAFT:-7003}
go run cmd/node/main.go -role node -id node3 -data ./data/node3 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5003} -rqlite-raft-port $${RAFT:-7003} -advertise $${ADVERTISE:-localhost}
# Show how to run with flags
show-bootstrap:
@echo "Provide bootstrap via flags, e.g.:"
@echo " make run-node2 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<PEER_ID> HTTP=5002 RAFT=7002"
@echo "Provide bootstrap via flags, e.g.:"
@echo " make run-node2 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<PEER_ID> HTTP=5002 RAFT=7002"
# Run network CLI
run-cli:

View File

@ -10,7 +10,7 @@ set -euo pipefail
# Collect ports from args or use defaults
PORTS=("$@")
if [ ${#PORTS[@]} -eq 0 ]; then
PORTS=(4001 5001 7001)
PORTS=(4001 5001 7001 7002)
fi
echo "Gracefully terminating listeners on: ${PORTS[*]}"

View File

@ -27,6 +27,7 @@ func main() {
role = flag.String("role", "auto", "Node role: auto|bootstrap|node (auto detects based on config)")
rqlHTTP = flag.Int("rqlite-http-port", 5001, "RQLite HTTP API port")
rqlRaft = flag.Int("rqlite-raft-port", 7001, "RQLite Raft port")
advertise = flag.String("advertise", "auto", "Advertise mode: auto|localhost|ip")
help = flag.Bool("help", false, "Show help")
)
flag.Parse()
@ -96,6 +97,8 @@ func main() {
// RQLite ports (overridable for local multi-node on one host)
cfg.Database.RQLitePort = *rqlHTTP
cfg.Database.RQLiteRaftPort = *rqlRaft
cfg.Database.AdvertiseMode = strings.ToLower(*advertise)
logger.Printf("RQLite advertise mode: %s", cfg.Database.AdvertiseMode)
if isBootstrap {
// Check if this is the primary bootstrap node (first in list) or secondary
@ -131,8 +134,8 @@ func main() {
// Extract IP from bootstrap peer for RQLite
bootstrapHost := parseHostFromMultiaddr(*bootstrap)
if bootstrapHost != "" {
// If user provided localhost for libp2p, translate to this host's primary IP so rqlite can join the correct process.
if bootstrapHost == "127.0.0.1" || strings.EqualFold(bootstrapHost, "localhost") {
// Only translate localhost to external IP when not explicitly in localhost advertise mode
if (bootstrapHost == "127.0.0.1" || strings.EqualFold(bootstrapHost, "localhost")) && cfg.Database.AdvertiseMode != "localhost" {
if extIP, err := getPreferredLocalIP(); err == nil && extIP != "" {
logger.Printf("Translating localhost bootstrap to external IP %s for RQLite join", extIP)
bootstrapHost = extIP

View File

@ -39,6 +39,7 @@ type DatabaseConfig struct {
RQLitePort int `yaml:"rqlite_port"` // RQLite HTTP API port
RQLiteRaftPort int `yaml:"rqlite_raft_port"` // RQLite Raft consensus port
RQLiteJoinAddress string `yaml:"rqlite_join_address"` // Address to join RQLite cluster
AdvertiseMode string `yaml:"advertise_mode"` // Advertise mode: "auto" (default), "localhost", or "ip"
}
// DiscoveryConfig contains peer discovery configuration
@ -124,6 +125,7 @@ func DefaultConfig() *Config {
RQLitePort: 5001,
RQLiteRaftPort: 7001,
RQLiteJoinAddress: "", // Empty for bootstrap node
AdvertiseMode: "auto",
},
Discovery: DiscoveryConfig{
BootstrapPeers: []string{},

View File

@ -43,13 +43,28 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
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"
// Determine advertise host based on configuration
advertiseHost := "127.0.0.1" // default
mode := strings.ToLower(r.config.AdvertiseMode)
switch mode {
case "localhost":
advertiseHost = "127.0.0.1"
r.logger.Info("Using localhost for RQLite advertising (dev mode)")
case "ip":
if ip, err := r.getExternalIP(); err == nil && ip != "" {
advertiseHost = ip
r.logger.Info("Using external IP for RQLite advertising (forced)", zap.String("ip", ip))
} else {
r.logger.Warn("Failed to get external IP, falling back to localhost", zap.Error(err))
}
default: // auto
if ip, err := r.getExternalIP(); err == nil && ip != "" {
advertiseHost = ip
r.logger.Info("Using external IP for RQLite advertising (auto)", zap.String("ip", ip))
} else {
r.logger.Info("No external IP found, using localhost for RQLite advertising (auto)")
}
}
r.logger.Info("Using external IP for RQLite advertising", zap.String("ip", externalIP))
// Build RQLite command
args := []string{
@ -58,11 +73,9 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
// Auth disabled for testing
}
// 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))
}
// Always set advertised addresses explicitly to avoid 0.0.0.0 announcements
args = append(args, "-http-adv-addr", fmt.Sprintf("%s:%d", advertiseHost, r.config.RQLitePort))
args = append(args, "-raft-adv-addr", fmt.Sprintf("%s:%d", advertiseHost, r.config.RQLiteRaftPort))
// Add join address if specified (for non-bootstrap or secondary bootstrap nodes)
if r.config.RQLiteJoinAddress != "" {
@ -99,7 +112,7 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
zap.Int("http_port", r.config.RQLitePort),
zap.Int("raft_port", r.config.RQLiteRaftPort),
zap.String("join_address", r.config.RQLiteJoinAddress),
zap.String("external_ip", externalIP),
zap.String("advertise_host", advertiseHost),
zap.Strings("full_args", args),
)