github.com/ghodss/etcd@v0.3.1-0.20140417172404-cc329bfa55cb/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 peer number in the cluster
    67  	if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize {
    68  		log.Debug("Reject join request from ", c.Name)
    69  		return []byte{0}, etcdErr.NewError(etcdErr.EcodeNoMorePeer, "", context.CommitIndex())
    70  	}
    71  
    72  	// Add to shared peer registry.
    73  	ps.registry.RegisterPeer(c.Name, c.RaftURL, c.EtcdURL)
    74  
    75  	// Add peer in raft
    76  	err := context.Server().AddPeer(c.Name, "")
    77  
    78  	// Add peer stats
    79  	if c.Name != ps.RaftServer().Name() {
    80  		ps.followersStats.Followers[c.Name] = &raftFollowerStats{}
    81  		ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63
    82  	}
    83  
    84  	return b, err
    85  }
    86  
    87  func (c *JoinCommandV1) NodeName() string {
    88  	return c.Name
    89  }
    90  
    91  // JoinCommandV2 represents a request to join the cluster.
    92  type JoinCommandV2 struct {
    93  	MinVersion int    `json:"minVersion"`
    94  	MaxVersion int    `json:"maxVersion"`
    95  	Name       string `json:"name"`
    96  	PeerURL    string `json:"peerURL"`
    97  	ClientURL  string `json:"clientURL"`
    98  }
    99  
   100  // CommandName returns the name of the command in the Raft log.
   101  func (c *JoinCommandV2) CommandName() string {
   102  	return "etcd:v2:join"
   103  }
   104  
   105  func (c *JoinCommandV2) updatePeerURL(ps *PeerServer) error {
   106  	log.Debugf("Update peer URL of %v to %v", c.Name, c.PeerURL)
   107  	if err := ps.registry.UpdatePeerURL(c.Name, c.PeerURL); err != nil {
   108  		log.Debugf("Error while updating in registry: %s (%v)", c.Name, err)
   109  		return err
   110  	}
   111  	// Flush commit index, so raft will replay to here when restart
   112  	ps.raftServer.FlushCommitIndex()
   113  	return nil
   114  }
   115  
   116  // Apply attempts to join a machine to the cluster.
   117  func (c *JoinCommandV2) Apply(context raft.Context) (interface{}, error) {
   118  	ps, _ := context.Server().Context().(*PeerServer)
   119  	var msg = joinMessageV2{
   120  		Mode:        PeerMode,
   121  		CommitIndex: context.CommitIndex(),
   122  	}
   123  
   124  	// Make sure we're not getting a cached value from the registry.
   125  	ps.registry.Invalidate(c.Name)
   126  
   127  	// Check if the join command is from a previous peer, who lost all its previous log.
   128  	if peerURL, ok := ps.registry.PeerURL(c.Name); ok {
   129  		// If previous node restarts with different peer URL,
   130  		// update its information.
   131  		if peerURL != c.PeerURL {
   132  			log.Infof("Rejoin with %v instead of %v from %v", c.PeerURL, peerURL, c.Name)
   133  			if err := c.updatePeerURL(ps); err != nil {
   134  				return []byte{0}, err
   135  			}
   136  		}
   137  		return json.Marshal(msg)
   138  	}
   139  
   140  	// Check peer number in the cluster.
   141  	if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize {
   142  		log.Debug("Join as standby ", c.Name)
   143  		ps.registry.RegisterStandby(c.Name, c.PeerURL, c.ClientURL)
   144  		msg.Mode = StandbyMode
   145  		return json.Marshal(msg)
   146  	}
   147  
   148  	// Remove it as a standby if it is one.
   149  	if ps.registry.StandbyExists(c.Name) {
   150  		ps.registry.UnregisterStandby(c.Name)
   151  	}
   152  
   153  	// Add to shared peer registry.
   154  	ps.registry.RegisterPeer(c.Name, c.PeerURL, c.ClientURL)
   155  
   156  	// Add peer in raft
   157  	if err := context.Server().AddPeer(c.Name, ""); err != nil {
   158  		b, _ := json.Marshal(msg)
   159  		return b, err
   160  	}
   161  
   162  	// Add peer stats
   163  	if c.Name != ps.RaftServer().Name() {
   164  		ps.followersStats.Followers[c.Name] = &raftFollowerStats{}
   165  		ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63
   166  	}
   167  
   168  	return json.Marshal(msg)
   169  }
   170  
   171  func (c *JoinCommandV2) NodeName() string {
   172  	return c.Name
   173  }
   174  
   175  type joinMessageV2 struct {
   176  	CommitIndex uint64 `json:"commitIndex"`
   177  	Mode        Mode   `json:"mode"`
   178  }