221 lines
4.9 KiB
Go
221 lines
4.9 KiB
Go
/*
|
|
Copyright 2014 CoreOS Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package etcdserver
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/coreos/etcd/conf"
|
|
)
|
|
|
|
const (
|
|
participantMode int64 = iota
|
|
standbyMode
|
|
stopMode
|
|
)
|
|
|
|
type Server struct {
|
|
cfg *conf.Config
|
|
name string
|
|
pubAddr string
|
|
raftPubAddr string
|
|
tickDuration time.Duration
|
|
|
|
mode atomicInt
|
|
p *participant
|
|
s *standby
|
|
|
|
client *v2client
|
|
peerHub *peerHub
|
|
|
|
exited chan error
|
|
stopNotifyc chan struct{}
|
|
log *log.Logger
|
|
http.Handler
|
|
}
|
|
|
|
func New(c *conf.Config) (*Server, error) {
|
|
if err := c.Sanitize(); err != nil {
|
|
log.Fatalf("server.new sanitizeErr=\"%v\"\n", err)
|
|
}
|
|
|
|
tc := &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
}
|
|
var err error
|
|
if c.PeerTLSInfo().Scheme() == "https" {
|
|
tc, err = c.PeerTLSInfo().ClientConfig()
|
|
if err != nil {
|
|
log.Fatalf("server.new ClientConfigErr=\"%v\"\n", err)
|
|
}
|
|
}
|
|
|
|
tr := new(http.Transport)
|
|
tr.TLSClientConfig = tc
|
|
tr.Dial = (&net.Dialer{Timeout: 200 * time.Millisecond}).Dial
|
|
tr.TLSHandshakeTimeout = 10 * time.Second
|
|
tr.ResponseHeaderTimeout = defaultTickDuration * defaultHeartbeat
|
|
client := &http.Client{Transport: tr}
|
|
|
|
s := &Server{
|
|
cfg: c,
|
|
name: c.Name,
|
|
pubAddr: c.Addr,
|
|
raftPubAddr: c.Peer.Addr,
|
|
tickDuration: defaultTickDuration,
|
|
|
|
mode: atomicInt(stopMode),
|
|
|
|
client: newClient(tc),
|
|
|
|
exited: make(chan error, 1),
|
|
stopNotifyc: make(chan struct{}),
|
|
}
|
|
followersStats := NewRaftFollowersStats(s.name)
|
|
s.peerHub = newPeerHub(client, followersStats)
|
|
m := http.NewServeMux()
|
|
m.HandleFunc("/", s.requestHandler)
|
|
m.HandleFunc("/version", versionHandler)
|
|
s.Handler = m
|
|
|
|
log.Printf("name=%s server.new raftPubAddr=%s\n", s.name, s.raftPubAddr)
|
|
if err = os.MkdirAll(s.cfg.DataDir, 0700); err != nil {
|
|
if !os.IsExist(err) {
|
|
return nil, err
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func (s *Server) SetTick(tick time.Duration) {
|
|
s.tickDuration = tick
|
|
log.Printf("name=%s server.setTick tick=%q\n", s.name, s.tickDuration)
|
|
}
|
|
|
|
// Stop stops the server elegently.
|
|
func (s *Server) Stop() error {
|
|
s.mode.Set(stopMode)
|
|
close(s.stopNotifyc)
|
|
err := <-s.exited
|
|
s.client.CloseConnections()
|
|
log.Printf("name=%s server.stop\n", s.name)
|
|
return err
|
|
}
|
|
|
|
func (s *Server) requestHandler(w http.ResponseWriter, r *http.Request) {
|
|
switch s.mode.Get() {
|
|
case participantMode:
|
|
s.p.ServeHTTP(w, r)
|
|
case standbyMode:
|
|
s.s.ServeHTTP(w, r)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}
|
|
|
|
func (s *Server) RaftHandler() http.Handler {
|
|
return http.HandlerFunc(s.ServeRaftHTTP)
|
|
}
|
|
|
|
func (s *Server) ServeRaftHTTP(w http.ResponseWriter, r *http.Request) {
|
|
switch s.mode.Get() {
|
|
case participantMode:
|
|
s.p.raftHandler().ServeHTTP(w, r)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}
|
|
|
|
func (s *Server) Run() error {
|
|
var d *discoverer
|
|
var seeds []string
|
|
var exit error
|
|
defer func() { s.exited <- exit }()
|
|
|
|
durl := s.cfg.Discovery
|
|
if durl != "" {
|
|
u, err := url.Parse(durl)
|
|
if err != nil {
|
|
exit = err
|
|
return fmt.Errorf("bad discovery URL error: %v", err)
|
|
}
|
|
d = newDiscoverer(u, s.name, s.raftPubAddr)
|
|
if seeds, err = d.discover(); err != nil {
|
|
exit = err
|
|
return err
|
|
}
|
|
log.Printf("name=%s server.run source=-discovery seeds=%q", s.name, seeds)
|
|
} else {
|
|
for _, p := range s.cfg.Peers {
|
|
u, err := url.Parse(p)
|
|
if err != nil {
|
|
log.Printf("name=%s server.run err=%q", s.name, err)
|
|
continue
|
|
}
|
|
if u.Scheme == "" {
|
|
u.Scheme = s.cfg.PeerTLSInfo().Scheme()
|
|
}
|
|
seeds = append(seeds, u.String())
|
|
}
|
|
log.Printf("name=%s server.run source=-peers seeds=%q", s.name, seeds)
|
|
}
|
|
s.peerHub.setSeeds(seeds)
|
|
|
|
next := participantMode
|
|
for {
|
|
switch next {
|
|
case participantMode:
|
|
p, err := newParticipant(s.cfg, s.client, s.peerHub, s.tickDuration)
|
|
if err != nil {
|
|
log.Printf("name=%s server.run newParicipanteErr=\"%v\"\n", s.name, err)
|
|
exit = err
|
|
return err
|
|
}
|
|
s.p = p
|
|
s.mode.Set(participantMode)
|
|
log.Printf("name=%s server.run mode=participantMode\n", s.name)
|
|
dStopc := make(chan struct{})
|
|
if d != nil {
|
|
go d.heartbeat(dStopc)
|
|
}
|
|
s.p.run(s.stopNotifyc)
|
|
if d != nil {
|
|
close(dStopc)
|
|
}
|
|
next = standbyMode
|
|
case standbyMode:
|
|
s.s = newStandby(s.client, s.peerHub)
|
|
s.mode.Set(standbyMode)
|
|
log.Printf("name=%s server.run mode=standbyMode\n", s.name)
|
|
s.s.run(s.stopNotifyc)
|
|
next = participantMode
|
|
default:
|
|
panic("unsupport mode")
|
|
}
|
|
if s.mode.Get() == stopMode {
|
|
return nil
|
|
}
|
|
}
|
|
}
|