this repo has no description
1package main
2
3import (
4 "encoding/hex"
5 "flag"
6 "log"
7 "os"
8 "os/signal"
9 "syscall"
10 "time"
11
12 "github.com/hashicorp/memberlist"
13)
14
15type eventDelegate struct{}
16
17func (e *eventDelegate) NotifyJoin(node *memberlist.Node) {
18 log.Printf("[EVENT] Node joined: %s (%s:%d)", node.Name, node.Addr, node.Port)
19}
20
21func (e *eventDelegate) NotifyLeave(node *memberlist.Node) {
22 log.Printf("[EVENT] Node left: %s", node.Name)
23}
24
25func (e *eventDelegate) NotifyUpdate(node *memberlist.Node) {
26 log.Printf("[EVENT] Node updated: %s", node.Name)
27}
28
29func main() {
30 name := flag.String("name", "go-node", "Node name")
31 bind := flag.String("bind", "127.0.0.1", "Bind address")
32 port := flag.Int("port", 7946, "Bind port")
33 join := flag.String("join", "", "Address to join (host:port)")
34 secretKey := flag.String("key", "", "Secret key (hex encoded, 16 bytes for AES-128)")
35 flag.Parse()
36
37 config := memberlist.DefaultLANConfig()
38 config.Name = *name
39 config.BindAddr = *bind
40 config.BindPort = *port
41 config.AdvertisePort = *port
42 config.Events = &eventDelegate{}
43
44 config.LogOutput = os.Stdout
45
46 if *secretKey != "" {
47 keyBytes, err := hex.DecodeString(*secretKey)
48 if err != nil {
49 log.Fatalf("Invalid hex key: %v", err)
50 }
51 if len(keyBytes) != 16 && len(keyBytes) != 24 && len(keyBytes) != 32 {
52 log.Fatalf("Key must be 16, 24, or 32 bytes (got %d)", len(keyBytes))
53 }
54 keyring, err := memberlist.NewKeyring(nil, keyBytes)
55 if err != nil {
56 log.Fatalf("Failed to create keyring: %v", err)
57 }
58 config.Keyring = keyring
59 config.GossipVerifyIncoming = true
60 config.GossipVerifyOutgoing = true
61 log.Printf("Encryption enabled with %d-byte key", len(keyBytes))
62 }
63
64 list, err := memberlist.Create(config)
65 if err != nil {
66 log.Fatalf("Failed to create memberlist: %v", err)
67 }
68
69 log.Printf("Memberlist started: %s on %s:%d", *name, *bind, *port)
70
71 if *join != "" {
72 log.Printf("Joining cluster via %s", *join)
73 n, err := list.Join([]string{*join})
74 if err != nil {
75 log.Printf("Failed to join: %v", err)
76 } else {
77 log.Printf("Joined %d nodes", n)
78 }
79 }
80
81 go func() {
82 ticker := time.NewTicker(5 * time.Second)
83 for range ticker.C {
84 members := list.Members()
85 log.Printf("--- Members (%d) ---", len(members))
86 for _, m := range members {
87 log.Printf(" %s: %s:%d (state=%v)", m.Name, m.Addr, m.Port, m.State)
88 }
89 }
90 }()
91
92 sigCh := make(chan os.Signal, 1)
93 signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
94 <-sigCh
95
96 log.Println("Shutting down...")
97 if err := list.Leave(time.Second); err != nil {
98 log.Printf("Leave error: %v", err)
99 }
100 if err := list.Shutdown(); err != nil {
101 log.Printf("Shutdown error: %v", err)
102 }
103}