package main import ( "encoding/hex" "flag" "log" "os" "os/signal" "syscall" "time" "github.com/hashicorp/memberlist" ) type eventDelegate struct{} func (e *eventDelegate) NotifyJoin(node *memberlist.Node) { log.Printf("[EVENT] Node joined: %s (%s:%d)", node.Name, node.Addr, node.Port) } func (e *eventDelegate) NotifyLeave(node *memberlist.Node) { log.Printf("[EVENT] Node left: %s", node.Name) } func (e *eventDelegate) NotifyUpdate(node *memberlist.Node) { log.Printf("[EVENT] Node updated: %s", node.Name) } func main() { name := flag.String("name", "go-node", "Node name") bind := flag.String("bind", "127.0.0.1", "Bind address") port := flag.Int("port", 7946, "Bind port") join := flag.String("join", "", "Address to join (host:port)") secretKey := flag.String("key", "", "Secret key (hex encoded, 16 bytes for AES-128)") flag.Parse() config := memberlist.DefaultLANConfig() config.Name = *name config.BindAddr = *bind config.BindPort = *port config.AdvertisePort = *port config.Events = &eventDelegate{} config.LogOutput = os.Stdout if *secretKey != "" { keyBytes, err := hex.DecodeString(*secretKey) if err != nil { log.Fatalf("Invalid hex key: %v", err) } if len(keyBytes) != 16 && len(keyBytes) != 24 && len(keyBytes) != 32 { log.Fatalf("Key must be 16, 24, or 32 bytes (got %d)", len(keyBytes)) } keyring, err := memberlist.NewKeyring(nil, keyBytes) if err != nil { log.Fatalf("Failed to create keyring: %v", err) } config.Keyring = keyring config.GossipVerifyIncoming = true config.GossipVerifyOutgoing = true log.Printf("Encryption enabled with %d-byte key", len(keyBytes)) } list, err := memberlist.Create(config) if err != nil { log.Fatalf("Failed to create memberlist: %v", err) } log.Printf("Memberlist started: %s on %s:%d", *name, *bind, *port) if *join != "" { log.Printf("Joining cluster via %s", *join) n, err := list.Join([]string{*join}) if err != nil { log.Printf("Failed to join: %v", err) } else { log.Printf("Joined %d nodes", n) } } go func() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { members := list.Members() log.Printf("--- Members (%d) ---", len(members)) for _, m := range members { log.Printf(" %s: %s:%d (state=%v)", m.Name, m.Addr, m.Port, m.State) } } }() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh log.Println("Shutting down...") if err := list.Leave(time.Second); err != nil { log.Printf("Leave error: %v", err) } if err := list.Shutdown(); err != nil { log.Printf("Shutdown error: %v", err) } }