github.com/DerekStrickland/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  }