github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/merge.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/consul/agent/metadata" 7 "github.com/hashicorp/consul/types" 8 "github.com/hashicorp/go-version" 9 "github.com/hashicorp/serf/serf" 10 ) 11 12 // lanMergeDelegate is used to handle a cluster merge on the LAN gossip 13 // ring. We check that the peers are in the same datacenter and abort the 14 // merge if there is a mis-match. 15 type lanMergeDelegate struct { 16 dc string 17 nodeID types.NodeID 18 nodeName string 19 segment string 20 } 21 22 // uniqueIDMinVersion is the lowest version where we insist that nodes 23 // have a unique ID. 24 var uniqueIDMinVersion = version.Must(version.NewVersion("0.8.5")) 25 26 func (md *lanMergeDelegate) NotifyMerge(members []*serf.Member) error { 27 nodeMap := make(map[types.NodeID]string) 28 for _, m := range members { 29 if rawID, ok := m.Tags["id"]; ok && rawID != "" { 30 nodeID := types.NodeID(rawID) 31 32 // See if there's another node that conflicts with us. 33 if (nodeID == md.nodeID) && (m.Name != md.nodeName) { 34 return fmt.Errorf("Member '%s' has conflicting node ID '%s' with this agent's ID", 35 m.Name, nodeID) 36 } 37 38 // See if there are any two nodes that conflict with each 39 // other. This lets us only do joins into a hygienic 40 // cluster now that node IDs are critical for operation. 41 if other, ok := nodeMap[nodeID]; ok { 42 return fmt.Errorf("Member '%s' has conflicting node ID '%s' with member '%s'", 43 m.Name, nodeID, other) 44 } 45 46 // Only map nodes with a version that's >= than when 47 // we made host-based IDs opt-in, which helps prevent 48 // chaos when upgrading older clusters. See #3070 for 49 // more details. 50 if ver, err := metadata.Build(m); err == nil { 51 if ver.Compare(uniqueIDMinVersion) >= 0 { 52 nodeMap[nodeID] = m.Name 53 } 54 } 55 } 56 57 if ok, dc := isConsulNode(*m); ok { 58 if dc != md.dc { 59 return fmt.Errorf("Member '%s' part of wrong datacenter '%s'", 60 m.Name, dc) 61 } 62 } 63 64 if ok, parts := metadata.IsConsulServer(*m); ok { 65 if parts.Datacenter != md.dc { 66 return fmt.Errorf("Member '%s' part of wrong datacenter '%s'", 67 m.Name, parts.Datacenter) 68 } 69 } 70 71 if segment := m.Tags["segment"]; segment != md.segment { 72 return fmt.Errorf("Member '%s' part of wrong segment '%s' (expected '%s')", 73 m.Name, segment, md.segment) 74 } 75 } 76 return nil 77 } 78 79 // wanMergeDelegate is used to handle a cluster merge on the WAN gossip 80 // ring. We check that the peers are server nodes and abort the merge 81 // otherwise. 82 type wanMergeDelegate struct { 83 } 84 85 func (md *wanMergeDelegate) NotifyMerge(members []*serf.Member) error { 86 for _, m := range members { 87 ok, _ := metadata.IsConsulServer(*m) 88 if !ok { 89 return fmt.Errorf("Member '%s' is not a server", m.Name) 90 } 91 } 92 return nil 93 }