github.com/sym3tri/etcd@v0.2.1-0.20140422215517-a563d82f95d6/server/join_command.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/json"
     6  
     7  	etcdErr "github.com/coreos/etcd/error"
     8  	"github.com/coreos/etcd/log"
     9  	"github.com/coreos/etcd/third_party/github.com/goraft/raft"
    10  )
    11  
    12  func init() {
    13  	raft.RegisterCommand(&JoinCommandV1{})
    14  	raft.RegisterCommand(&JoinCommandV2{})
    15  }
    16  
    17  // JoinCommandV1 represents a request to join the cluster.
    18  // The command returns the join_index (Uvarint).
    19  type JoinCommandV1 struct {
    20  	MinVersion int    `json:"minVersion"`
    21  	MaxVersion int    `json:"maxVersion"`
    22  	Name       string `json:"name"`
    23  	RaftURL    string `json:"raftURL"`
    24  	EtcdURL    string `json:"etcdURL"`
    25  }
    26  
    27  // The name of the join command in the log
    28  func (c *JoinCommandV1) CommandName() string {
    29  	return "etcd:join"
    30  }
    31  
    32  func (c *JoinCommandV1) updatePeerURL(ps *PeerServer) error {
    33  	log.Debugf("Update peer URL of %v to %v", c.Name, c.RaftURL)
    34  	if err := ps.registry.UpdatePeerURL(c.Name, c.RaftURL); err != nil {
    35  		log.Debugf("Error while updating in registry: %s (%v)", c.Name, err)
    36  		return err
    37  	}
    38  	// Flush commit index, so raft will replay to here when restarted
    39  	ps.raftServer.FlushCommitIndex()
    40  	return nil
    41  }
    42  
    43  // Join a server to the cluster
    44  func (c *JoinCommandV1) Apply(context raft.Context) (interface{}, error) {
    45  	ps, _ := context.Server().Context().(*PeerServer)
    46  
    47  	b := make([]byte, 8)
    48  	binary.PutUvarint(b, context.CommitIndex())
    49  
    50  	// Make sure we're not getting a cached value from the registry.
    51  	ps.registry.Invalidate(c.Name)
    52  
    53  	// Check if the join command is from a previous peer, who lost all its previous log.
    54  	if peerURL, ok := ps.registry.PeerURL(c.Name); ok {
    55  		// If previous node restarts with different peer URL,
    56  		// update its information.
    57  		if peerURL != c.RaftURL {
    58  			log.Infof("Rejoin with %v instead of %v from %v", c.RaftURL, peerURL, c.Name)
    59  			if err := c.updatePeerURL(ps); err != nil {
    60  				return []byte{0}, err
    61  			}
    62  		}
    63  		return b, nil
    64  	}
    65  
    66  	// Check if the join command adds an instance that collides with existing one on peer URL.
    67  	peerURLs := ps.registry.PeerURLs(ps.raftServer.Leader(), c.Name)
    68  	for _, peerURL := range peerURLs {
    69  		if peerURL == c.RaftURL {
    70  			log.Warnf("%v tries to join the cluster with existing URL %v", c.Name, c.EtcdURL)
    71  			return []byte{0}, etcdErr.NewError(etcdErr.EcodeExistingPeerAddr, c.EtcdURL, context.CommitIndex())
    72  		}
    73  	}
    74  
    75  	// Check peer number in the cluster
    76  	if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize {
    77  		log.Debug("Reject join request from ", c.Name)
    78  		return []byte{0}, etcdErr.NewError(etcdErr.EcodeNoMorePeer, "", context.CommitIndex())
    79  	}
    80  
    81  	// Add to shared peer registry.
    82  	ps.registry.RegisterPeer(c.Name, c.RaftURL, c.EtcdURL)
    83  
    84  	// Add peer in raft
    85  	err := context.Server().AddPeer(c.Name, "")
    86  
    87  	// Add peer stats
    88  	if c.Name != ps.RaftServer().Name() {
    89  		ps.followersStats.Followers[c.Name] = &raftFollowerStats{}
    90  		ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63
    91  	}
    92  
    93  	return b, err
    94  }
    95  
    96  func (c *JoinCommandV1) NodeName() string {
    97  	return c.Name
    98  }
    99  
   100  // JoinCommandV2 represents a request to join the cluster.
   101  type JoinCommandV2 struct {
   102  	MinVersion int    `json:"minVersion"`
   103  	MaxVersion int    `json:"maxVersion"`
   104  	Name       string `json:"name"`
   105  	PeerURL    string `json:"peerURL"`
   106  	ClientURL  string `json:"clientURL"`
   107  }
   108  
   109  // CommandName returns the name of the command in the Raft log.
   110  func (c *JoinCommandV2) CommandName() string {
   111  	return "etcd:v2:join"
   112  }
   113  
   114  func (c *JoinCommandV2) updatePeerURL(ps *PeerServer) error {
   115  	log.Debugf("Update peer URL of %v to %v", c.Name, c.PeerURL)
   116  	if err := ps.registry.UpdatePeerURL(c.Name, c.PeerURL); err != nil {
   117  		log.Debugf("Error while updating in registry: %s (%v)", c.Name, err)
   118  		return err
   119  	}
   120  	// Flush commit index, so raft will replay to here when restart
   121  	ps.raftServer.FlushCommitIndex()
   122  	return nil
   123  }
   124  
   125  // Apply attempts to join a machine to the cluster.
   126  func (c *JoinCommandV2) Apply(context raft.Context) (interface{}, error) {
   127  	ps, _ := context.Server().Context().(*PeerServer)
   128  	var msg = joinMessageV2{
   129  		Mode:        PeerMode,
   130  		CommitIndex: context.CommitIndex(),
   131  	}
   132  
   133  	// Make sure we're not getting a cached value from the registry.
   134  	ps.registry.Invalidate(c.Name)
   135  
   136  	// Check if the join command is from a previous peer, who lost all its previous log.
   137  	if peerURL, ok := ps.registry.PeerURL(c.Name); ok {
   138  		// If previous node restarts with different peer URL,
   139  		// update its information.
   140  		if peerURL != c.PeerURL {
   141  			log.Infof("Rejoin with %v instead of %v from %v", c.PeerURL, peerURL, c.Name)
   142  			if err := c.updatePeerURL(ps); err != nil {
   143  				return []byte{0}, err
   144  			}
   145  		}
   146  		return json.Marshal(msg)
   147  	}
   148  
   149  	// Check if the join command adds an instance that collides with existing one on peer URL.
   150  	peerURLs := ps.registry.PeerURLs(ps.raftServer.Leader(), c.Name)
   151  	for _, peerURL := range peerURLs {
   152  		if peerURL == c.PeerURL {
   153  			log.Warnf("%v tries to join the cluster with existing URL %v", c.Name, c.PeerURL)
   154  			return []byte{0}, etcdErr.NewError(etcdErr.EcodeExistingPeerAddr, c.PeerURL, context.CommitIndex())
   155  		}
   156  	}
   157  
   158  	// Check peer number in the cluster.
   159  	if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize {
   160  		log.Debug("Join as standby ", c.Name)
   161  		ps.registry.RegisterStandby(c.Name, c.PeerURL, c.ClientURL)
   162  		msg.Mode = StandbyMode
   163  		return json.Marshal(msg)
   164  	}
   165  
   166  	// Remove it as a standby if it is one.
   167  	if ps.registry.StandbyExists(c.Name) {
   168  		ps.registry.UnregisterStandby(c.Name)
   169  	}
   170  
   171  	// Add to shared peer registry.
   172  	ps.registry.RegisterPeer(c.Name, c.PeerURL, c.ClientURL)
   173  
   174  	// Add peer in raft
   175  	if err := context.Server().AddPeer(c.Name, ""); err != nil {
   176  		b, _ := json.Marshal(msg)
   177  		return b, err
   178  	}
   179  
   180  	// Add peer stats
   181  	if c.Name != ps.RaftServer().Name() {
   182  		ps.followersStats.Followers[c.Name] = &raftFollowerStats{}
   183  		ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63
   184  	}
   185  
   186  	return json.Marshal(msg)
   187  }
   188  
   189  func (c *JoinCommandV2) NodeName() string {
   190  	return c.Name
   191  }
   192  
   193  type joinMessageV2 struct {
   194  	CommitIndex uint64 `json:"commitIndex"`
   195  	Mode        Mode   `json:"mode"`
   196  }