github.com/kafkaliu/etcd@v0.1.2-0.20131007164923-44c16dd30d69/command.go (about)

     1  /*
     2  Copyright 2013 CoreOS Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/binary"
    21  	"encoding/json"
    22  	"fmt"
    23  	"os"
    24  	"path"
    25  	"time"
    26  
    27  	etcdErr "github.com/coreos/etcd/error"
    28  	"github.com/coreos/etcd/store"
    29  	"github.com/coreos/go-raft"
    30  )
    31  
    32  const commandPrefix = "etcd:"
    33  
    34  func commandName(name string) string {
    35  	return commandPrefix + name
    36  }
    37  
    38  // A command represents an action to be taken on the replicated state machine.
    39  type Command interface {
    40  	CommandName() string
    41  	Apply(server *raft.Server) (interface{}, error)
    42  }
    43  
    44  // Set command
    45  type SetCommand struct {
    46  	Key        string    `json:"key"`
    47  	Value      string    `json:"value"`
    48  	ExpireTime time.Time `json:"expireTime"`
    49  }
    50  
    51  // The name of the set command in the log
    52  func (c *SetCommand) CommandName() string {
    53  	return commandName("set")
    54  }
    55  
    56  // Set the key-value pair
    57  func (c *SetCommand) Apply(server *raft.Server) (interface{}, error) {
    58  	return etcdStore.Set(c.Key, c.Value, c.ExpireTime, server.CommitIndex())
    59  }
    60  
    61  // TestAndSet command
    62  type TestAndSetCommand struct {
    63  	Key        string    `json:"key"`
    64  	Value      string    `json:"value"`
    65  	PrevValue  string    `json: prevValue`
    66  	ExpireTime time.Time `json:"expireTime"`
    67  }
    68  
    69  // The name of the testAndSet command in the log
    70  func (c *TestAndSetCommand) CommandName() string {
    71  	return commandName("testAndSet")
    72  }
    73  
    74  // Set the key-value pair if the current value of the key equals to the given prevValue
    75  func (c *TestAndSetCommand) Apply(server *raft.Server) (interface{}, error) {
    76  	return etcdStore.TestAndSet(c.Key, c.PrevValue, c.Value, c.ExpireTime, server.CommitIndex())
    77  }
    78  
    79  // Get command
    80  type GetCommand struct {
    81  	Key string `json:"key"`
    82  }
    83  
    84  // The name of the get command in the log
    85  func (c *GetCommand) CommandName() string {
    86  	return commandName("get")
    87  }
    88  
    89  // Get the value of key
    90  func (c *GetCommand) Apply(server *raft.Server) (interface{}, error) {
    91  	return etcdStore.Get(c.Key)
    92  }
    93  
    94  // Delete command
    95  type DeleteCommand struct {
    96  	Key string `json:"key"`
    97  }
    98  
    99  // The name of the delete command in the log
   100  func (c *DeleteCommand) CommandName() string {
   101  	return commandName("delete")
   102  }
   103  
   104  // Delete the key
   105  func (c *DeleteCommand) Apply(server *raft.Server) (interface{}, error) {
   106  	return etcdStore.Delete(c.Key, server.CommitIndex())
   107  }
   108  
   109  // Watch command
   110  type WatchCommand struct {
   111  	Key        string `json:"key"`
   112  	SinceIndex uint64 `json:"sinceIndex"`
   113  }
   114  
   115  // The name of the watch command in the log
   116  func (c *WatchCommand) CommandName() string {
   117  	return commandName("watch")
   118  }
   119  
   120  func (c *WatchCommand) Apply(server *raft.Server) (interface{}, error) {
   121  	// create a new watcher
   122  	watcher := store.NewWatcher()
   123  
   124  	// add to the watchers list
   125  	etcdStore.AddWatcher(c.Key, watcher, c.SinceIndex)
   126  
   127  	// wait for the notification for any changing
   128  	res := <-watcher.C
   129  
   130  	if res == nil {
   131  		return nil, fmt.Errorf("Clearing watch")
   132  	}
   133  
   134  	return json.Marshal(res)
   135  }
   136  
   137  // JoinCommand
   138  type JoinCommand struct {
   139  	RaftVersion string `json:"raftVersion"`
   140  	Name        string `json:"name"`
   141  	RaftURL     string `json:"raftURL"`
   142  	EtcdURL     string `json:"etcdURL"`
   143  }
   144  
   145  func newJoinCommand() *JoinCommand {
   146  	return &JoinCommand{
   147  		RaftVersion: r.version,
   148  		Name:        r.name,
   149  		RaftURL:     r.url,
   150  		EtcdURL:     e.url,
   151  	}
   152  }
   153  
   154  // The name of the join command in the log
   155  func (c *JoinCommand) CommandName() string {
   156  	return commandName("join")
   157  }
   158  
   159  // Join a server to the cluster
   160  func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) {
   161  
   162  	// check if the join command is from a previous machine, who lost all its previous log.
   163  	response, _ := etcdStore.RawGet(path.Join("_etcd/machines", c.Name))
   164  
   165  	b := make([]byte, 8)
   166  	binary.PutUvarint(b, raftServer.CommitIndex())
   167  
   168  	if response != nil {
   169  		return b, nil
   170  	}
   171  
   172  	// check machine number in the cluster
   173  	num := machineNum()
   174  	if num == maxClusterSize {
   175  		debug("Reject join request from ", c.Name)
   176  		return []byte{0}, etcdErr.NewError(103, "")
   177  	}
   178  
   179  	addNameToURL(c.Name, c.RaftVersion, c.RaftURL, c.EtcdURL)
   180  
   181  	// add peer in raft
   182  	err := raftServer.AddPeer(c.Name, "")
   183  
   184  	// add machine in etcd storage
   185  	key := path.Join("_etcd/machines", c.Name)
   186  	value := fmt.Sprintf("raft=%s&etcd=%s&raftVersion=%s", c.RaftURL, c.EtcdURL, c.RaftVersion)
   187  	etcdStore.Set(key, value, time.Unix(0, 0), raftServer.CommitIndex())
   188  
   189  	// add peer stats
   190  	if c.Name != r.Name() {
   191  		r.followersStats.Followers[c.Name] = &raftFollowerStats{}
   192  		r.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63
   193  	}
   194  
   195  	return b, err
   196  }
   197  
   198  func (c *JoinCommand) NodeName() string {
   199  	return c.Name
   200  }
   201  
   202  // RemoveCommand
   203  type RemoveCommand struct {
   204  	Name string `json:"name"`
   205  }
   206  
   207  // The name of the remove command in the log
   208  func (c *RemoveCommand) CommandName() string {
   209  	return commandName("remove")
   210  }
   211  
   212  // Remove a server from the cluster
   213  func (c *RemoveCommand) Apply(raftServer *raft.Server) (interface{}, error) {
   214  
   215  	// remove machine in etcd storage
   216  	key := path.Join("_etcd/machines", c.Name)
   217  
   218  	_, err := etcdStore.Delete(key, raftServer.CommitIndex())
   219  
   220  	// delete from stats
   221  	delete(r.followersStats.Followers, c.Name)
   222  
   223  	if err != nil {
   224  		return []byte{0}, err
   225  	}
   226  
   227  	// remove peer in raft
   228  	err = raftServer.RemovePeer(c.Name)
   229  
   230  	if err != nil {
   231  		return []byte{0}, err
   232  	}
   233  
   234  	if c.Name == raftServer.Name() {
   235  		// the removed node is this node
   236  
   237  		// if the node is not replaying the previous logs
   238  		// and the node has sent out a join request in this
   239  		// start. It is sure that this node received a new remove
   240  		// command and need to be removed
   241  		if raftServer.CommitIndex() > r.joinIndex && r.joinIndex != 0 {
   242  			debugf("server [%s] is removed", raftServer.Name())
   243  			os.Exit(0)
   244  		} else {
   245  			// else ignore remove
   246  			debugf("ignore previous remove command.")
   247  		}
   248  	}
   249  
   250  	b := make([]byte, 8)
   251  	binary.PutUvarint(b, raftServer.CommitIndex())
   252  
   253  	return b, err
   254  }