mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 08:19:07 +00:00
feat: add dev-local mode for localhost testing with auto-discovery of bootstrap peers
This commit is contained in:
parent
e76ad5cf16
commit
cf36d301d5
6
Makefile
6
Makefile
@ -26,21 +26,21 @@ test:
|
|||||||
# Run bootstrap node explicitly
|
# Run bootstrap node explicitly
|
||||||
run-node:
|
run-node:
|
||||||
@echo "Starting BOOTSTRAP node (role=bootstrap)..."
|
@echo "Starting BOOTSTRAP node (role=bootstrap)..."
|
||||||
go run ./cmd/node -role bootstrap -data ./data/bootstrap -advertise localhost -p2p-port $${P2P:-4001}
|
go run ./cmd/node -role bootstrap -data ./data/bootstrap -advertise localhost -p2p-port $${P2P:-4001} -dev-local
|
||||||
|
|
||||||
# Run second node (regular) - requires BOOTSTRAP multiaddr
|
# 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 P2P=4002
|
# Usage: make run-node2 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<ID> HTTP=5002 RAFT=7002 P2P=4002
|
||||||
run-node2:
|
run-node2:
|
||||||
@echo "Starting REGULAR node2 (role=node)..."
|
@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 P2P=4002]"; exit 1; fi
|
@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 P2P=4002]"; exit 1; fi
|
||||||
go run ./cmd/node -role node -id node2 -data ./data/node2 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5002} -rqlite-raft-port $${RAFT:-7002} -p2p-port $${P2P:-4002} -advertise $${ADVERTISE:-localhost}
|
go run ./cmd/node -role node -id node2 -data ./data/node2 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5002} -rqlite-raft-port $${RAFT:-7002} -p2p-port $${P2P:-4002} -advertise $${ADVERTISE:-localhost} -dev-local
|
||||||
|
|
||||||
# Run third node (regular) - requires BOOTSTRAP multiaddr
|
# 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 P2P=4003
|
# Usage: make run-node3 BOOTSTRAP=/ip4/127.0.0.1/tcp/4001/p2p/<ID> HTTP=5003 RAFT=7003 P2P=4003
|
||||||
run-node3:
|
run-node3:
|
||||||
@echo "Starting REGULAR node3 (role=node)..."
|
@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 P2P=4003]"; exit 1; fi
|
@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 P2P=4003]"; exit 1; fi
|
||||||
go run ./cmd/node -role node -id node3 -data ./data/node3 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5003} -rqlite-raft-port $${RAFT:-7003} -p2p-port $${P2P:-4003} -advertise $${ADVERTISE:-localhost}
|
go run ./cmd/node -role node -id node3 -data ./data/node3 -bootstrap $(BOOTSTRAP) -rqlite-http-port $${HTTP:-5003} -rqlite-raft-port $${RAFT:-7003} -p2p-port $${P2P:-4003} -advertise $${ADVERTISE:-localhost} -dev-local
|
||||||
|
|
||||||
# Run basic usage example
|
# Run basic usage example
|
||||||
run-example:
|
run-example:
|
||||||
|
@ -187,7 +187,7 @@ make build
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start an explicit bootstrap node (LibP2P 4001, RQLite 5001/7001)
|
# Start an explicit bootstrap node (LibP2P 4001, RQLite 5001/7001)
|
||||||
go run ./cmd/node -role bootstrap -data ./data/bootstrap
|
go run ./cmd/node -role bootstrap -data ./data/bootstrap -dev-local
|
||||||
```
|
```
|
||||||
|
|
||||||
**Terminal 2 - Regular Node:**
|
**Terminal 2 - Regular Node:**
|
||||||
@ -200,7 +200,8 @@ go run ./cmd/node \
|
|||||||
-data ./data/node2 \
|
-data ./data/node2 \
|
||||||
-bootstrap /ip4/127.0.0.1/tcp/4001/p2p/<BOOTSTRAP_PEER_ID> \
|
-bootstrap /ip4/127.0.0.1/tcp/4001/p2p/<BOOTSTRAP_PEER_ID> \
|
||||||
-rqlite-http-port 5002 \
|
-rqlite-http-port 5002 \
|
||||||
-rqlite-raft-port 7002
|
-rqlite-raft-port 7002 \
|
||||||
|
-dev-local
|
||||||
```
|
```
|
||||||
|
|
||||||
**Terminal 3 - Another Node (optional):**
|
**Terminal 3 - Another Node (optional):**
|
||||||
@ -421,6 +422,7 @@ For more advanced configuration options and development setup, see the sections
|
|||||||
|
|
||||||
- **Bootstrap node**: `-role bootstrap`
|
- **Bootstrap node**: `-role bootstrap`
|
||||||
- **Regular node**: `-role node -bootstrap <multiaddr>`
|
- **Regular node**: `-role node -bootstrap <multiaddr>`
|
||||||
|
- **Development localhost defaults**: `-dev-local` (sets `NETWORK_DEV_LOCAL=1` in-process); use this for local-only testing so the library returns localhost DB endpoints and bootstrap peers.
|
||||||
- **RQLite ports**: `-rqlite-http-port` (default 5001), `-rqlite-raft-port` (default 7001)
|
- **RQLite ports**: `-rqlite-http-port` (default 5001), `-rqlite-raft-port` (default 7001)
|
||||||
|
|
||||||
Examples are shown in Quick Start above for local multi-node on a single machine.
|
Examples are shown in Quick Start above for local multi-node on a single machine.
|
||||||
|
@ -2,9 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.debros.io/DeBros/network/pkg/config"
|
"git.debros.io/DeBros/network/pkg/config"
|
||||||
|
"git.debros.io/DeBros/network/pkg/client"
|
||||||
"git.debros.io/DeBros/network/pkg/constants"
|
"git.debros.io/DeBros/network/pkg/constants"
|
||||||
"git.debros.io/DeBros/network/pkg/logging"
|
"git.debros.io/DeBros/network/pkg/logging"
|
||||||
)
|
)
|
||||||
@ -21,6 +23,17 @@ type NodeFlagValues struct {
|
|||||||
Advertise string
|
Advertise string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isTruthyEnv returns true if the environment variable is set to a common truthy value
|
||||||
|
func isTruthyEnv(key string) bool {
|
||||||
|
v := strings.ToLower(strings.TrimSpace(os.Getenv(key)))
|
||||||
|
switch v {
|
||||||
|
case "1", "true", "yes", "on":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MapFlagsAndEnvToConfig applies environment overrides and CLI flags to cfg.
|
// MapFlagsAndEnvToConfig applies environment overrides and CLI flags to cfg.
|
||||||
// Precedence: flags > env > defaults. Behavior mirrors previous inline logic in main.go.
|
// Precedence: flags > env > defaults. Behavior mirrors previous inline logic in main.go.
|
||||||
// Returns the derived RQLite Raft join address for non-bootstrap nodes (empty for bootstrap nodes).
|
// Returns the derived RQLite Raft join address for non-bootstrap nodes (empty for bootstrap nodes).
|
||||||
@ -28,6 +41,9 @@ func MapFlagsAndEnvToConfig(cfg *config.Config, fv NodeFlagValues, isBootstrap b
|
|||||||
// Apply environment variable overrides first so that flags can override them after
|
// Apply environment variable overrides first so that flags can override them after
|
||||||
config.ApplyEnvOverrides(cfg)
|
config.ApplyEnvOverrides(cfg)
|
||||||
|
|
||||||
|
// Detect dev-local mode (set via -dev-local -> NETWORK_DEV_LOCAL=1)
|
||||||
|
devLocal := isTruthyEnv("NETWORK_DEV_LOCAL")
|
||||||
|
|
||||||
// Determine data directory if not provided
|
// Determine data directory if not provided
|
||||||
if fv.DataDir == "" {
|
if fv.DataDir == "" {
|
||||||
if isBootstrap {
|
if isBootstrap {
|
||||||
@ -56,6 +72,14 @@ func MapFlagsAndEnvToConfig(cfg *config.Config, fv NodeFlagValues, isBootstrap b
|
|||||||
|
|
||||||
// Bootstrap-specific vs regular-node logic
|
// Bootstrap-specific vs regular-node logic
|
||||||
if isBootstrap {
|
if isBootstrap {
|
||||||
|
if devLocal {
|
||||||
|
// In dev-local, run a primary bootstrap locally
|
||||||
|
cfg.Database.RQLiteJoinAddress = ""
|
||||||
|
// Also prefer localhost bootstrap peers for any consumers reading cfg
|
||||||
|
cfg.Discovery.BootstrapPeers = client.DefaultBootstrapPeers()
|
||||||
|
logger.Printf("Dev-local: Primary bootstrap node - localhost defaults enabled")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
bootstrapPeers := constants.GetBootstrapPeers()
|
bootstrapPeers := constants.GetBootstrapPeers()
|
||||||
isSecondaryBootstrap := false
|
isSecondaryBootstrap := false
|
||||||
if len(bootstrapPeers) > 1 {
|
if len(bootstrapPeers) > 1 {
|
||||||
@ -103,6 +127,11 @@ func MapFlagsAndEnvToConfig(cfg *config.Config, fv NodeFlagValues, isBootstrap b
|
|||||||
logger.Printf("Using command line bootstrap peer: %s", fv.Bootstrap)
|
logger.Printf("Using command line bootstrap peer: %s", fv.Bootstrap)
|
||||||
} else {
|
} else {
|
||||||
bootstrapPeers := cfg.Discovery.BootstrapPeers
|
bootstrapPeers := cfg.Discovery.BootstrapPeers
|
||||||
|
if devLocal {
|
||||||
|
// Force localhost bootstrap for development
|
||||||
|
bootstrapPeers = client.DefaultBootstrapPeers()
|
||||||
|
logger.Printf("Dev-local: overriding bootstrap peers to %v", bootstrapPeers)
|
||||||
|
}
|
||||||
if len(bootstrapPeers) == 0 {
|
if len(bootstrapPeers) == 0 {
|
||||||
bootstrapPeers = constants.GetBootstrapPeers()
|
bootstrapPeers = constants.GetBootstrapPeers()
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ func main() {
|
|||||||
rqlHTTP = flag.Int("rqlite-http-port", 5001, "RQLite HTTP API port")
|
rqlHTTP = flag.Int("rqlite-http-port", 5001, "RQLite HTTP API port")
|
||||||
rqlRaft = flag.Int("rqlite-raft-port", 7001, "RQLite Raft port")
|
rqlRaft = flag.Int("rqlite-raft-port", 7001, "RQLite Raft port")
|
||||||
advertise = flag.String("advertise", "auto", "Advertise mode: auto|localhost|ip")
|
advertise = flag.String("advertise", "auto", "Advertise mode: auto|localhost|ip")
|
||||||
|
devLocal = flag.Bool("dev-local", false, "Enable development localhost defaults for the client library (sets NETWORK_DEV_LOCAL=1)")
|
||||||
help = flag.Bool("help", false, "Show help")
|
help = flag.Bool("help", false, "Show help")
|
||||||
)
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -38,6 +39,12 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable development localhost defaults for the client library if requested
|
||||||
|
if *devLocal {
|
||||||
|
os.Setenv("NETWORK_DEV_LOCAL", "1")
|
||||||
|
log.Printf("Development local mode enabled (NETWORK_DEV_LOCAL=1)")
|
||||||
|
}
|
||||||
|
|
||||||
// Determine node role
|
// Determine node role
|
||||||
var isBootstrap bool
|
var isBootstrap bool
|
||||||
switch strings.ToLower(*role) {
|
switch strings.ToLower(*role) {
|
||||||
|
@ -2,6 +2,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -16,6 +17,10 @@ func DefaultBootstrapPeers() []string {
|
|||||||
if ma := os.Getenv("LOCAL_BOOTSTRAP_MULTIADDR"); ma != "" {
|
if ma := os.Getenv("LOCAL_BOOTSTRAP_MULTIADDR"); ma != "" {
|
||||||
return []string{ma}
|
return []string{ma}
|
||||||
}
|
}
|
||||||
|
// Try to auto-resolve local bootstrap peer multiaddr from peer.info
|
||||||
|
if ma, ok := readLocalPeerInfoMultiaddr(); ok {
|
||||||
|
return []string{ma}
|
||||||
|
}
|
||||||
// Fallback to localhost transport without peer ID (connect will warn and skip)
|
// Fallback to localhost transport without peer ID (connect will warn and skip)
|
||||||
return []string{"/ip4/127.0.0.1/tcp/4001"}
|
return []string{"/ip4/127.0.0.1/tcp/4001"}
|
||||||
}
|
}
|
||||||
@ -84,27 +89,27 @@ func MapAddrsToDBEndpoints(addrs []multiaddr.Multiaddr, dbPort int) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func endpointFromMultiaddr(ma multiaddr.Multiaddr, port int) string {
|
func endpointFromMultiaddr(ma multiaddr.Multiaddr, port int) string {
|
||||||
var host string
|
var host string
|
||||||
// Prefer DNS if present, then IP
|
// Prefer DNS if present, then IP
|
||||||
if v, err := ma.ValueForProtocol(multiaddr.P_DNS); err == nil && v != "" {
|
if v, err := ma.ValueForProtocol(multiaddr.P_DNS); err == nil && v != "" {
|
||||||
host = v
|
host = v
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
if v, err := ma.ValueForProtocol(multiaddr.P_DNS4); err == nil && v != "" { host = v }
|
if v, err := ma.ValueForProtocol(multiaddr.P_DNS4); err == nil && v != "" { host = v }
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
if v, err := ma.ValueForProtocol(multiaddr.P_DNS6); err == nil && v != "" { host = v }
|
if v, err := ma.ValueForProtocol(multiaddr.P_DNS6); err == nil && v != "" { host = v }
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
if v, err := ma.ValueForProtocol(multiaddr.P_IP4); err == nil && v != "" { host = v }
|
if v, err := ma.ValueForProtocol(multiaddr.P_IP4); err == nil && v != "" { host = v }
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
if v, err := ma.ValueForProtocol(multiaddr.P_IP6); err == nil && v != "" { host = v }
|
if v, err := ma.ValueForProtocol(multiaddr.P_IP6); err == nil && v != "" { host = v }
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
}
|
}
|
||||||
return "http://" + host + ":" + strconv.Itoa(port)
|
return "http://" + host + ":" + strconv.Itoa(port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dedupeStrings(in []string) []string {
|
func dedupeStrings(in []string) []string {
|
||||||
@ -123,3 +128,32 @@ func dedupeStrings(in []string) []string {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readLocalPeerInfoMultiaddr attempts to read the local bootstrap peer multiaddr from common dev paths.
|
||||||
|
// It checks LOCAL_BOOTSTRAP_INFO (path), then ./data/bootstrap/peer.info, then ./data/peer.info.
|
||||||
|
func readLocalPeerInfoMultiaddr() (string, bool) {
|
||||||
|
candidates := make([]string, 0, 3)
|
||||||
|
if p := strings.TrimSpace(os.Getenv("LOCAL_BOOTSTRAP_INFO")); p != "" {
|
||||||
|
candidates = append(candidates, p)
|
||||||
|
}
|
||||||
|
candidates = append(candidates,
|
||||||
|
filepath.Join(".", "data", "bootstrap", "peer.info"),
|
||||||
|
filepath.Join(".", "data", "peer.info"),
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, p := range candidates {
|
||||||
|
b, err := os.ReadFile(p)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(string(b))
|
||||||
|
if s == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// expect a full multiaddr with /p2p/<peerID>
|
||||||
|
if strings.Contains(s, "/p2p/") {
|
||||||
|
return s, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user