|
@@ -1,16 +1,12 @@
|
|
|
package server
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
- "bytes"
|
|
|
|
|
- "encoding/binary"
|
|
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
- "io/ioutil"
|
|
|
|
|
"math/rand"
|
|
"math/rand"
|
|
|
"net/http"
|
|
"net/http"
|
|
|
"net/url"
|
|
"net/url"
|
|
|
"sort"
|
|
"sort"
|
|
|
- "strconv"
|
|
|
|
|
"strings"
|
|
"strings"
|
|
|
"sync"
|
|
"sync"
|
|
|
"time"
|
|
"time"
|
|
@@ -52,6 +48,7 @@ type PeerServerConfig struct {
|
|
|
|
|
|
|
|
type PeerServer struct {
|
|
type PeerServer struct {
|
|
|
Config PeerServerConfig
|
|
Config PeerServerConfig
|
|
|
|
|
+ client *Client
|
|
|
clusterConfig *ClusterConfig
|
|
clusterConfig *ClusterConfig
|
|
|
raftServer raft.Server
|
|
raftServer raft.Server
|
|
|
server *Server
|
|
server *Server
|
|
@@ -86,9 +83,10 @@ type snapshotConf struct {
|
|
|
snapshotThr uint64
|
|
snapshotThr uint64
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func NewPeerServer(psConfig PeerServerConfig, registry *Registry, store store.Store, mb *metrics.Bucket, followersStats *raftFollowersStats, serverStats *raftServerStats) *PeerServer {
|
|
|
|
|
|
|
+func NewPeerServer(psConfig PeerServerConfig, client *Client, registry *Registry, store store.Store, mb *metrics.Bucket, followersStats *raftFollowersStats, serverStats *raftServerStats) *PeerServer {
|
|
|
s := &PeerServer{
|
|
s := &PeerServer{
|
|
|
Config: psConfig,
|
|
Config: psConfig,
|
|
|
|
|
+ client: client,
|
|
|
clusterConfig: NewClusterConfig(),
|
|
clusterConfig: NewClusterConfig(),
|
|
|
registry: registry,
|
|
registry: registry,
|
|
|
store: store,
|
|
store: store,
|
|
@@ -410,24 +408,6 @@ func (s *PeerServer) startAsFollower(cluster []string, retryTimes int) error {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// getVersion fetches the peer version of a cluster.
|
|
|
|
|
-func getVersion(t *transporter, versionURL url.URL) (int, error) {
|
|
|
|
|
- resp, _, err := t.Get(versionURL.String())
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return 0, err
|
|
|
|
|
- }
|
|
|
|
|
- defer resp.Body.Close()
|
|
|
|
|
-
|
|
|
|
|
- body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return 0, err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Parse version number.
|
|
|
|
|
- version, _ := strconv.Atoi(string(body))
|
|
|
|
|
- return version, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// Upgradable checks whether all peers in a cluster support an upgrade to the next store version.
|
|
// Upgradable checks whether all peers in a cluster support an upgrade to the next store version.
|
|
|
func (s *PeerServer) Upgradable() error {
|
|
func (s *PeerServer) Upgradable() error {
|
|
|
nextVersion := s.store.Version() + 1
|
|
nextVersion := s.store.Version() + 1
|
|
@@ -437,13 +417,12 @@ func (s *PeerServer) Upgradable() error {
|
|
|
return fmt.Errorf("PeerServer: Cannot parse URL: '%s' (%s)", peerURL, err)
|
|
return fmt.Errorf("PeerServer: Cannot parse URL: '%s' (%s)", peerURL, err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- t, _ := s.raftServer.Transporter().(*transporter)
|
|
|
|
|
- checkURL := (&url.URL{Host: u.Host, Scheme: s.Config.Scheme, Path: fmt.Sprintf("/version/%d/check", nextVersion)}).String()
|
|
|
|
|
- resp, _, err := t.Get(checkURL)
|
|
|
|
|
|
|
+ url := (&url.URL{Host: u.Host, Scheme: s.Config.Scheme}).String()
|
|
|
|
|
+ ok, err := s.client.CheckVersion(url, nextVersion)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return fmt.Errorf("PeerServer: Cannot check version compatibility: %s", u.Host)
|
|
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- if resp.StatusCode != 200 {
|
|
|
|
|
|
|
+ if !ok {
|
|
|
return fmt.Errorf("PeerServer: Version %d is not compatible with peer: %s", nextVersion, u.Host)
|
|
return fmt.Errorf("PeerServer: Version %d is not compatible with peer: %s", nextVersion, u.Host)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -552,73 +531,53 @@ func (s *PeerServer) joinCluster(cluster []string) bool {
|
|
|
|
|
|
|
|
// Send join requests to peer.
|
|
// Send join requests to peer.
|
|
|
func (s *PeerServer) joinByPeer(server raft.Server, peer string, scheme string) error {
|
|
func (s *PeerServer) joinByPeer(server raft.Server, peer string, scheme string) error {
|
|
|
- // t must be ok
|
|
|
|
|
- t, _ := server.Transporter().(*transporter)
|
|
|
|
|
|
|
+ u := (&url.URL{Host: peer, Scheme: scheme}).String()
|
|
|
|
|
|
|
|
// Our version must match the leaders version
|
|
// Our version must match the leaders version
|
|
|
- versionURL := url.URL{Host: peer, Scheme: scheme, Path: "/version"}
|
|
|
|
|
- version, err := getVersion(t, versionURL)
|
|
|
|
|
|
|
+ version, err := s.client.GetVersion(u)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return fmt.Errorf("Error during join version check: %v", err)
|
|
|
|
|
|
|
+ return fmt.Errorf("fail checking join version: %v", err)
|
|
|
}
|
|
}
|
|
|
if version < store.MinVersion() || version > store.MaxVersion() {
|
|
if version < store.MinVersion() || version > store.MaxVersion() {
|
|
|
- return fmt.Errorf("Unable to join: cluster version is %d; version compatibility is %d - %d", version, store.MinVersion(), store.MaxVersion())
|
|
|
|
|
|
|
+ return fmt.Errorf("fail passing version compatibility(%d-%d) using %d", store.MinVersion(), store.MaxVersion(), version)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var b bytes.Buffer
|
|
|
|
|
- c := &JoinCommand{
|
|
|
|
|
- MinVersion: store.MinVersion(),
|
|
|
|
|
- MaxVersion: store.MaxVersion(),
|
|
|
|
|
- Name: server.Name(),
|
|
|
|
|
- RaftURL: s.Config.URL,
|
|
|
|
|
- EtcdURL: s.server.URL(),
|
|
|
|
|
|
|
+ // Fetch current peer list
|
|
|
|
|
+ machines, err := s.client.GetMachines(u)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("fail getting machine messages: %v", err)
|
|
|
}
|
|
}
|
|
|
- json.NewEncoder(&b).Encode(c)
|
|
|
|
|
-
|
|
|
|
|
- joinURL := url.URL{Host: peer, Scheme: scheme, Path: "/join"}
|
|
|
|
|
- log.Infof("Send Join Request to %s", joinURL.String())
|
|
|
|
|
-
|
|
|
|
|
- req, _ := http.NewRequest("PUT", joinURL.String(), &b)
|
|
|
|
|
- resp, err := t.client.Do(req)
|
|
|
|
|
-
|
|
|
|
|
- for {
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return fmt.Errorf("Unable to join: %v", err)
|
|
|
|
|
|
|
+ exist := false
|
|
|
|
|
+ for _, machine := range machines {
|
|
|
|
|
+ if machine.Name == server.Name() {
|
|
|
|
|
+ exist = true
|
|
|
|
|
+ break
|
|
|
}
|
|
}
|
|
|
- if resp != nil {
|
|
|
|
|
- defer resp.Body.Close()
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- log.Infof("»»»» %d", resp.StatusCode)
|
|
|
|
|
- if resp.StatusCode == http.StatusOK {
|
|
|
|
|
- b, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
|
- s.joinIndex, _ = binary.Uvarint(b)
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
- if resp.StatusCode == http.StatusTemporaryRedirect {
|
|
|
|
|
- address := resp.Header.Get("Location")
|
|
|
|
|
- log.Debugf("Send Join Request to %s", address)
|
|
|
|
|
- c := &JoinCommand{
|
|
|
|
|
- MinVersion: store.MinVersion(),
|
|
|
|
|
- MaxVersion: store.MaxVersion(),
|
|
|
|
|
- Name: server.Name(),
|
|
|
|
|
- RaftURL: s.Config.URL,
|
|
|
|
|
- EtcdURL: s.server.URL(),
|
|
|
|
|
- }
|
|
|
|
|
- json.NewEncoder(&b).Encode(c)
|
|
|
|
|
- resp, _, err = t.Put(address, &b)
|
|
|
|
|
-
|
|
|
|
|
- } else if resp.StatusCode == http.StatusBadRequest {
|
|
|
|
|
- log.Debug("Reach max number peers in the cluster")
|
|
|
|
|
- decoder := json.NewDecoder(resp.Body)
|
|
|
|
|
- err := &etcdErr.Error{}
|
|
|
|
|
- decoder.Decode(err)
|
|
|
|
|
- return *err
|
|
|
|
|
- } else {
|
|
|
|
|
- return fmt.Errorf("Unable to join")
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Fetch cluster config to see whether exists some place.
|
|
|
|
|
+ clusterConfig, err := s.client.GetClusterConfig(u)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("fail getting cluster config: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if !exist && clusterConfig.ActiveSize <= len(machines) {
|
|
|
|
|
+ return fmt.Errorf("stop joining because the cluster is full with %d nodes", len(machines))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ joinIndex, err := s.client.AddMachine(u,
|
|
|
|
|
+ &JoinCommand{
|
|
|
|
|
+ MinVersion: store.MinVersion(),
|
|
|
|
|
+ MaxVersion: store.MaxVersion(),
|
|
|
|
|
+ Name: server.Name(),
|
|
|
|
|
+ RaftURL: s.Config.URL,
|
|
|
|
|
+ EtcdURL: s.server.URL(),
|
|
|
|
|
+ })
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("fail on join request: %v", err)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ s.joinIndex = joinIndex
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (s *PeerServer) Stats() []byte {
|
|
func (s *PeerServer) Stats() []byte {
|