Passed
Pull Request — master (#464)
by Tolga
02:46
created

gossip.*Gossip.Shutdown   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
package gossip
2
3
import (
4
	"errors"
5
	"fmt"
6
	"io"
7
	"log"
8
	"net"
9
	"time"
10
11
	"github.com/hashicorp/memberlist"
12
)
13
14
type IGossip interface {
15
	SyncMemberList() (nodes []string)
16
	Shutdown() error
17
}
18
19
type Gossip struct {
20
	Enabled    bool
21
	memberList *memberlist.Memberlist
22
}
23
24
// InitMemberList initializes a memberlist instance with the provided seed nodes and config.
25
func InitMemberList(nodes []string, grpcPort int) (*Gossip, error) {
26
	conf := memberlist.DefaultLocalConfig()
27
28
	conf.Logger = log.New(io.Discard, "", 0)
29
30
	//conf.BindAddr = "0.0.0.0"
31
	//conf.BindPort = gossipPort
32
33
	ip, err := ExternalIP()
34
	if err != nil {
35
		return nil, fmt.Errorf("external ip error: %v", err)
36
	}
37
38
	conf.AdvertiseAddr = ip
39
	conf.AdvertisePort = grpcPort
40
41
	list, err := memberlist.Create(conf)
42
	if err != nil {
43
		return nil, fmt.Errorf("memberlist Create Error %v", err)
44
	}
45
46
	if len(nodes) > 0 {
47
		_, err := list.Join(nodes)
48
		if err != nil {
49
			return nil, fmt.Errorf("starter ring join error: %v", err)
50
		}
51
	}
52
53
	return &Gossip{
54
		Enabled:    true,
55
		memberList: list,
56
	}, nil
57
}
58
59
// SyncMemberList returns a list of all nodes in the cluster.
60
func (g *Gossip) SyncMemberList() (nodes []string) {
61
	members := g.memberList.Members()
62
	for _, member := range members {
63
		nodes = append(nodes, member.Address())
64
	}
65
66
	return
67
}
68
69
// Shutdown gracefully shuts down the memberlist instance.
70
func (g *Gossip) Shutdown() error {
71
	return errors.Join(g.memberList.Leave(time.Second), g.memberList.Shutdown())
72
}
73
74
// ExternalIP returns the first non-loopback IPv4 address
75
func ExternalIP() (string, error) {
76
	// Get a list of network interfaces.
77
	interfaces, err := net.Interfaces()
78
	if err != nil {
79
		return "", err
80
	}
81
82
	// Iterate over the network interfaces.
83
	for _, iface := range interfaces {
84
		// Skip the interface if it's down or a loopback interface.
85
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
86
			continue
87
		}
88
89
		// Get a list of addresses associated with the interface.
90
		addresses, err := iface.Addrs()
91
		if err != nil {
92
			return "", err
93
		}
94
95
		// Iterate over the addresses.
96
		for _, addr := range addresses {
97
			// Extract the IP address from the address.
98
			var ip net.IP
99
			switch v := addr.(type) {
100
			case *net.IPNet:
101
				ip = v.IP
102
			case *net.IPAddr:
103
				ip = v.IP
104
			}
105
106
			// Skip the address if it's a loopback address or not IPv4.
107
			if ip == nil || ip.IsLoopback() || ip.To4() == nil {
108
				continue
109
			}
110
111
			// Return the IPv4 address as a string.
112
			return ip.String(), nil
113
		}
114
	}
115
116
	// Return an empty string if no external IPv4 address is found.
117
	return "", errors.New("network error")
118
}
119