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 config.EnableCompression = false
44
45 config.LogOutput = os.Stdout
46
47 if *secretKey != "" {
48 keyBytes, err := hex.DecodeString(*secretKey)
49 if err != nil {
50 log.Fatalf("Invalid hex key: %v", err)
51 }
52 if len(keyBytes) != 16 && len(keyBytes) != 24 && len(keyBytes) != 32 {
53 log.Fatalf("Key must be 16, 24, or 32 bytes (got %d)", len(keyBytes))
54 }
55 keyring, err := memberlist.NewKeyring(nil, keyBytes)
56 if err != nil {
57 log.Fatalf("Failed to create keyring: %v", err)
58 }
59 config.Keyring = keyring
60 config.GossipVerifyIncoming = true
61 config.GossipVerifyOutgoing = true
62 log.Printf("Encryption enabled with %d-byte key", len(keyBytes))
63 }
64
65 list, err := memberlist.Create(config)
66 if err != nil {
67 log.Fatalf("Failed to create memberlist: %v", err)
68 }
69
70 log.Printf("Memberlist started: %s on %s:%d", *name, *bind, *port)
71
72 if *join != "" {
73 log.Printf("Joining cluster via %s", *join)
74 n, err := list.Join([]string{*join})
75 if err != nil {
76 log.Printf("Failed to join: %v", err)
77 } else {
78 log.Printf("Joined %d nodes", n)
79 }
80 }
81
82 go func() {
83 ticker := time.NewTicker(5 * time.Second)
84 for range ticker.C {
85 members := list.Members()
86 log.Printf("--- Members (%d) ---", len(members))
87 for _, m := range members {
88 log.Printf(" %s: %s:%d (state=%v)", m.Name, m.Addr, m.Port, m.State)
89 }
90 }
91 }()
92
93 sigCh := make(chan os.Signal, 1)
94 signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
95 <-sigCh
96
97 log.Println("Shutting down...")
98 if err := list.Leave(time.Second); err != nil {
99 log.Printf("Leave error: %v", err)
100 }
101 if err := list.Shutdown(); err != nil {
102 log.Printf("Shutdown error: %v", err)
103 }
104}