diff options
| author | Paul Buetow <paul@buetow.org> | 2023-10-22 23:55:49 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-10-22 23:55:49 +0300 |
| commit | 8650dd57474e991515c93169151e6187205c9ff3 (patch) | |
| tree | ef205edf763a91e737a47eb8cf04a817f7e78d4b | |
| parent | 61919768cfe4df8fbd709e8e6034f4c4c83def27 (diff) | |
refactor Nodes
| -rw-r--r-- | gorum.json | 17 | ||||
| -rw-r--r-- | internal/client/client.go | 6 | ||||
| -rw-r--r-- | internal/config/config.go | 105 | ||||
| -rw-r--r-- | internal/config/config_test.go | 28 | ||||
| -rw-r--r-- | internal/quorum/quorum.go | 14 | ||||
| -rw-r--r-- | internal/quorum/quorum_test.go | 42 | ||||
| -rw-r--r-- | test/gorum-earth.json | 6 |
7 files changed, 139 insertions, 79 deletions
@@ -1,9 +1,16 @@ { "StateDir": "./", "Address": "earth:1234", - "Nodes": [ - "earth:1234", - "earth:2341", - "earth:3412" - ] + "Nodes": { + "FirstNode": { + "Hostname": "earth", + "Port": 1234, + "Priority": 100 + }, + "AnotherNode": { + "Hostname": "localhsot", + "Port": 2345, + "Priority": 50 + } + } } diff --git a/internal/client/client.go b/internal/client/client.go index 3cb2775..f05fcba 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -13,8 +13,10 @@ func Start(ctx context.Context, conf config.Config, myVoteCh <-chan vote.Vote) { log.Println("client: starting") fanOut := make([]chan vote.Vote, len(conf.Nodes)) - for i, node := range conf.Nodes { - fanOut[i] = startConnection(ctx, node) + nodeNum := 0 + for _, node := range conf.Nodes { + fanOut[nodeNum] = startConnection(ctx, node.Hostname) + nodeNum++ } go func() { diff --git a/internal/config/config.go b/internal/config/config.go index a882268..b5030ee 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,19 +11,26 @@ import ( "codeberg.org/snonux/gorum/internal/utils" ) +type Node struct { + Port int + Priority int + Alias string `json:",omitempty"` + Hostname string + originalId string +} + type Config struct { - StateDir string - ScoreFile string - WinnerFile string - Address string - Nodes []string - LoopIntervalS int64 `json:"LoopIntervalS,omitempty"` - MyID string `json:"MyID,omitempty"` - RelaxedMode bool `json:"RelaxedMode,omitempty"` - nodeNumberCache map[string]int - EmailTo string `json:"EmailTo,omitempty"` - EmailFrom string `json:"EmailFrom,omitempty"` - SMTPServer string `json:"SMTPServer,omitempty"` + StateDir string + ScoreFile string + WinnerFile string + Address string + Nodes map[string]Node + LoopIntervalS int64 `json:"LoopIntervalS,omitempty"` + MyID string `json:"MyID,omitempty"` + RelaxedMode bool `json:"RelaxedMode,omitempty"` + EmailTo string `json:"EmailTo,omitempty"` + EmailFrom string `json:"EmailFrom,omitempty"` + SMTPServer string `json:"SMTPServer,omitempty"` } func NewFromConfigFile(configFile string) (Config, error) { @@ -45,6 +52,15 @@ func NewFromConfigFile(configFile string) (Config, error) { return conf, err } + // Make it so that the key is the Hostname for internal lookup. + newNodes := make(map[string]Node, len(conf.Nodes)) + for id, node := range conf.Nodes { + log.Printf("adding node %s: %v", id, node) + node.originalId = id + newNodes[node.Hostname] = node + } + conf.Nodes = newNodes + return conf.setDefaults() } @@ -70,9 +86,14 @@ func WithAddress(address string) func(*Config) { } } -func WithNodes(nodes ...string) func(*Config) { +func WithNodes(nodes ...Node) func(*Config) { return func(conf *Config) { - conf.Nodes = append(conf.Nodes, nodes...) + if conf.Nodes == nil { + conf.Nodes = make(map[string]Node) + } + for _, node := range nodes { + conf.Nodes[node.Hostname] = node + } } } @@ -106,35 +127,20 @@ func (conf Config) setDefaults() (Config, error) { conf.MyID = hostname } - conf.nodeNumberCache = make(map[string]int, len(conf.Nodes)) - for i, node := range conf.Nodes { - conf.nodeNumberCache[utils.StripPort(node)] = i - } return conf, nil } -func (conf Config) NodeNumber(node string) int { - node = utils.StripPort(node) - nodeNumber, ok := conf.nodeNumberCache[node] - if ok { - return nodeNumber +func (conf Config) NodePriority(id string) (int, error) { + node, err := conf.findNode(id) + if err != nil { + return 0, err } - - log.Println("config:", - fmt.Errorf("node %s not found - it will affect it's score!", node)) - return 0 + return node.Priority, nil } func (conf Config) IsNode(remoteAddr string) bool { - remoteAddr = utils.StripPort(remoteAddr) - - for _, node := range conf.Nodes { - if remoteAddr == utils.StripPort(node) { - return true - } - } - - return false + _, err := conf.findNode(utils.StripPort(remoteAddr)) + return err == nil } func (conf Config) IsNodeWithLookup(remoteAddr string, @@ -142,19 +148,38 @@ func (conf Config) IsNodeWithLookup(remoteAddr string, remoteAddr = utils.StripPort(remoteAddr) - for _, node := range conf.Nodes { - ips, err := lookupIP(utils.StripPort(node)) + compare := func(hostname string) bool { + ips, err := lookupIP(hostname) if err != nil { log.Println("config:", err) - continue + return false } - for _, ip := range ips { if remoteAddr == ip.String() { return true } } + return false } + for _, node := range conf.Nodes { + if compare(node.Hostname) || compare(node.Alias) { + return true + } + } return false } + +func (conf Config) findNode(hostname string) (Node, error) { + if node, ok := conf.Nodes[hostname]; ok { + return node, nil + } + + for _, node := range conf.Nodes { + if hostname == node.Alias { + return node, nil + } + } + + return Node{}, fmt.Errorf("node %s not found", hostname) +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go index e24c048..900f045 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -6,30 +6,32 @@ import ( "testing" ) -func TestNodeNumber(t *testing.T) { +func TestNodePriority(t *testing.T) { t.Parallel() - conf, _ := New(WithNodes("localhost:1234", "hamburger:4321")) + conf, _ := New(WithNodes( + Node{Hostname: "localhost", Port: 1234, Priority: 200}, + Node{Hostname: "hamburger", Port: 4321, Priority: 100})) - num := conf.NodeNumber("localhost") - if num != 0 { - t.Errorf("localhost should be node number 0 but is %d", num) + num, _ := conf.NodePriority("localhost") + if num != 200 { + t.Errorf("localhost should have priority 200 but has %d", num) } - - num = conf.NodeNumber("hamburger") - if num != 1 { - t.Errorf("hamburger should be node number 1 but is %d", num) + num, _ = conf.NodePriority("hamburger") + if num != 100 { + t.Errorf("hamburger should have priority 100 but has %d", num) } } func TestIsNode(t *testing.T) { t.Parallel() - conf, _ := New(WithNodes("localhost:1234", "hamburger:4321")) + conf, _ := New(WithNodes( + Node{Hostname: "localhost", Port: 1234, Priority: 200}, + Node{Hostname: "hamburger", Port: 4321, Priority: 100})) remoteAddr := "localhost:323232" if !conf.IsNode(remoteAddr) { t.Errorf("%s should be node of %v", remoteAddr, conf.Nodes) } - remoteAddr = "foo.zone:2345" if conf.IsNode(remoteAddr) { t.Errorf("%s should not be node of %v", remoteAddr, conf.Nodes) @@ -38,7 +40,9 @@ func TestIsNode(t *testing.T) { func TestIsNodeWithLookup(t *testing.T) { t.Parallel() - conf, _ := New(WithNodes("localhost:1234", "hamburger:4321")) + conf, _ := New(WithNodes( + Node{Hostname: "localhost", Port: 1234, Priority: 200}, + Node{Hostname: "hamburger", Port: 4321, Priority: 100})) lookupIP := func(addr string) ([]net.IP, error) { switch addr { diff --git a/internal/quorum/quorum.go b/internal/quorum/quorum.go index 9bce682..ab24063 100644 --- a/internal/quorum/quorum.go +++ b/internal/quorum/quorum.go @@ -113,8 +113,13 @@ func (quo Quorum) scores() (scores Scores) { } for _, id := range vote.IDs { score := scoreMap[id] - nodeNumber := quo.conf.NodeNumber(id) - scoreMap[id] = score + 100 + (len(quo.conf.Nodes) - nodeNumber) + priority, err := quo.conf.NodePriority(id) + if err != nil { + log.Println(err) + scoreMap[id] = score + continue + } + scoreMap[id] = 10*score + priority } } @@ -154,8 +159,9 @@ func (quo *Quorum) strs() (string, string) { } sb.WriteString(score.ID) - sb.WriteString(" (node #") - sb.WriteString(strconv.Itoa(quo.conf.NodeNumber(score.ID))) + sb.WriteString(" (priority ") + priority, _ := quo.conf.NodePriority(score.ID) + sb.WriteString(strconv.Itoa(priority)) sb.WriteString(") with total score of ") sb.WriteString(strconv.Itoa(score.Value)) diff --git a/internal/quorum/quorum_test.go b/internal/quorum/quorum_test.go index 0a8a35c..c1ad813 100644 --- a/internal/quorum/quorum_test.go +++ b/internal/quorum/quorum_test.go @@ -13,7 +13,11 @@ var inOneHour = time.Now().Add(1 * time.Hour) func TestScore(t *testing.T) { t.Parallel() - conf, _ := config.New(config.WithNodes("foo:1234", "bar:4321", "baz:3444")) + conf, _ := config.New(config.WithNodes( + config.Node{Hostname: "foo", Port: 1234, Priority: 3}, + config.Node{Hostname: "bar", Port: 4321, Priority: 2}, + config.Node{Hostname: "baz", Port: 3444, Priority: 1})) + quo := New(conf) vote1, _ := vote.New(conf, "foo", "bar") @@ -42,8 +46,8 @@ func TestScore(t *testing.T) { } t.Log(scores) - if scores[0].ID != "bar" || scores[0].Value != 306 { - t.Errorf("Expected score[0] to be {bar,306}: %v", scores[0]) + if scores[0].ID != "bar" || scores[0].Value != 222 { + t.Errorf("Expected score[0] to be {bar,222}: %v", scores[0]) } winner, _ := scores.Winner() @@ -71,18 +75,22 @@ func TestTieScore(t *testing.T) { } t.Run("First tie score test", func(t *testing.T) { - // If it is a tie, the first particpant (here: "foo") will win. - conf, _ := config.New(config.WithNodes("foo:1234", "bar:4321", "baz:3444")) + // If it is a tie, the particpant with the highest prio (here: "foo") will win. + conf, _ := config.New(config.WithNodes( + config.Node{Hostname: "foo", Port: 1234, Priority: 3}, + config.Node{Hostname: "bar", Port: 4321, Priority: 2}, + config.Node{Hostname: "baz", Port: 3444, Priority: 1})) quo := New(conf) addVotes(conf, quo) scores := quo.scores() + t.Log("scores", scores) if len(scores) != 3 { t.Errorf("Expected scores to be of length 3: %v", scores) } - if scores[0].ID != "foo" || scores[0].Value != 309 { - t.Errorf("Expected score[0] to be {foo,309}: %v", scores[0]) + if scores[0].ID != "foo" || scores[0].Value != 333 { + t.Errorf("Expected score[0] to be {foo,333}: %v", scores[0]) } winner := scores[0].ID @@ -92,8 +100,11 @@ func TestTieScore(t *testing.T) { }) t.Run("Second tie score test", func(t *testing.T) { - // If it is a tie, the first particpant (here: "bar") will win. - conf, _ := config.New(config.WithNodes("bar:1234", "foo:4321", "baz:3444")) + // If it is a tie, the particpant with the highest prio (here: "bar") will win. + conf, _ := config.New(config.WithNodes( + config.Node{Hostname: "bar", Port: 1234, Priority: 3}, + config.Node{Hostname: "foo", Port: 4321, Priority: 2}, + config.Node{Hostname: "baz", Port: 3444, Priority: 1})) quo := New(conf) addVotes(conf, quo) @@ -102,8 +113,8 @@ func TestTieScore(t *testing.T) { if len(scores) != 3 { t.Errorf("Expected scores to be of length 3: %v", scores) } - if scores[0].ID != "bar" || scores[0].Value != 309 { - t.Errorf("Expected score[0] to be {bar,309}: %v", scores[0]) + if scores[0].ID != "bar" || scores[0].Value != 333 { + t.Errorf("Expected score[0] to be {bar,333}: %v", scores[0]) } winner := scores[0].ID @@ -114,7 +125,10 @@ func TestTieScore(t *testing.T) { } func TestExpire(t *testing.T) { - conf, _ := config.New(config.WithNodes("foo:1234", "bar:4321", "bay:2212")) + conf, _ := config.New(config.WithNodes( + config.Node{Hostname: "foo", Port: 1234, Priority: 3}, + config.Node{Hostname: "bar", Port: 4321, Priority: 2}, + config.Node{Hostname: "bay", Port: 2212, Priority: 1})) quo := New(conf) vote1, _ := vote.New(conf, "bar", "baz", "bay") @@ -146,7 +160,9 @@ func TestExpire(t *testing.T) { } func TestLiveNodes(t *testing.T) { - conf, _ := config.New(config.WithNodes("foo:1234", "bay:4321")) + conf, _ := config.New(config.WithNodes( + config.Node{Hostname: "foo", Port: 1234, Priority: 3}, + config.Node{Hostname: "bay", Port: 4321, Priority: 1})) quo := New(conf) vote1, _ := vote.New(conf, "bar", "baz", "bay") diff --git a/test/gorum-earth.json b/test/gorum-earth.json index 5183e29..08a749d 100644 --- a/test/gorum-earth.json +++ b/test/gorum-earth.json @@ -6,8 +6,8 @@ "RelaxedMode": true, "Address": ":1234", "Nodes": [ - "localhost:1234", - "localhost:2341", - "localhost:3412" + "localhost:1234:earth", + "localhost:2341:mars", + "localhost:3412:uranus" ] } |
