|
|
@ -10,6 +10,7 @@ import (
|
|
|
|
"github.com/armon/go-metrics"
|
|
|
|
"github.com/armon/go-metrics"
|
|
|
|
"github.com/hashicorp/consul/consul/agent"
|
|
|
|
"github.com/hashicorp/consul/consul/agent"
|
|
|
|
"github.com/hashicorp/consul/consul/structs"
|
|
|
|
"github.com/hashicorp/consul/consul/structs"
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-version"
|
|
|
|
"github.com/hashicorp/raft"
|
|
|
|
"github.com/hashicorp/raft"
|
|
|
|
"github.com/hashicorp/serf/serf"
|
|
|
|
"github.com/hashicorp/serf/serf"
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -33,6 +34,8 @@ func (s *Server) stopAutopilot() {
|
|
|
|
s.autopilotWaitGroup.Wait()
|
|
|
|
s.autopilotWaitGroup.Wait()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var minAutopilotVersion = version.Must(version.NewVersion("0.8.0"))
|
|
|
|
|
|
|
|
|
|
|
|
// autopilotLoop periodically looks for nonvoting servers to promote and dead servers to remove.
|
|
|
|
// autopilotLoop periodically looks for nonvoting servers to promote and dead servers to remove.
|
|
|
|
func (s *Server) autopilotLoop() {
|
|
|
|
func (s *Server) autopilotLoop() {
|
|
|
|
defer s.autopilotWaitGroup.Done()
|
|
|
|
defer s.autopilotWaitGroup.Done()
|
|
|
@ -46,22 +49,25 @@ func (s *Server) autopilotLoop() {
|
|
|
|
case <-s.autopilotShutdownCh:
|
|
|
|
case <-s.autopilotShutdownCh:
|
|
|
|
return
|
|
|
|
return
|
|
|
|
case <-ticker.C:
|
|
|
|
case <-ticker.C:
|
|
|
|
state := s.fsm.State()
|
|
|
|
autopilotConfig, ok := s.getOrCreateAutopilotConfig()
|
|
|
|
_, autopilotConf, err := state.AutopilotConfig()
|
|
|
|
if !ok {
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
s.logger.Printf("[ERR] autopilot: error retrieving config from state store: %s", err)
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := s.autopilotPolicy.PromoteNonVoters(autopilotConf); err != nil {
|
|
|
|
if err := s.autopilotPolicy.PromoteNonVoters(autopilotConfig); err != nil {
|
|
|
|
s.logger.Printf("[ERR] autopilot: error checking for non-voters to promote: %s", err)
|
|
|
|
s.logger.Printf("[ERR] autopilot: error checking for non-voters to promote: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := s.pruneDeadServers(); err != nil {
|
|
|
|
if err := s.pruneDeadServers(autopilotConfig); err != nil {
|
|
|
|
s.logger.Printf("[ERR] autopilot: error checking for dead servers to remove: %s", err)
|
|
|
|
s.logger.Printf("[ERR] autopilot: error checking for dead servers to remove: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case <-s.autopilotRemoveDeadCh:
|
|
|
|
case <-s.autopilotRemoveDeadCh:
|
|
|
|
if err := s.pruneDeadServers(); err != nil {
|
|
|
|
autopilotConfig, ok := s.getOrCreateAutopilotConfig()
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err := s.pruneDeadServers(autopilotConfig); err != nil {
|
|
|
|
s.logger.Printf("[ERR] autopilot: error checking for dead servers to remove: %s", err)
|
|
|
|
s.logger.Printf("[ERR] autopilot: error checking for dead servers to remove: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -69,19 +75,13 @@ func (s *Server) autopilotLoop() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// pruneDeadServers removes up to numPeers/2 failed servers
|
|
|
|
// pruneDeadServers removes up to numPeers/2 failed servers
|
|
|
|
func (s *Server) pruneDeadServers() error {
|
|
|
|
func (s *Server) pruneDeadServers(autopilotConfig *structs.AutopilotConfig) error {
|
|
|
|
state := s.fsm.State()
|
|
|
|
|
|
|
|
_, autopilotConf, err := state.AutopilotConfig()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find any failed servers
|
|
|
|
// Find any failed servers
|
|
|
|
var failed []string
|
|
|
|
var failed []string
|
|
|
|
staleRaftServers := make(map[string]raft.Server)
|
|
|
|
staleRaftServers := make(map[string]raft.Server)
|
|
|
|
if autopilotConf.CleanupDeadServers {
|
|
|
|
if autopilotConfig.CleanupDeadServers {
|
|
|
|
future := s.raft.GetConfiguration()
|
|
|
|
future := s.raft.GetConfiguration()
|
|
|
|
if future.Error() != nil {
|
|
|
|
if err := future.Error(); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -155,7 +155,7 @@ type BasicAutopilot struct {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PromoteNonVoters promotes eligible non-voting servers to voters.
|
|
|
|
// PromoteNonVoters promotes eligible non-voting servers to voters.
|
|
|
|
func (b *BasicAutopilot) PromoteNonVoters(autopilotConf *structs.AutopilotConfig) error {
|
|
|
|
func (b *BasicAutopilot) PromoteNonVoters(autopilotConfig *structs.AutopilotConfig) error {
|
|
|
|
minRaftProtocol, err := ServerMinRaftProtocol(b.server.LANMembers())
|
|
|
|
minRaftProtocol, err := ServerMinRaftProtocol(b.server.LANMembers())
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error getting server raft protocol versions: %s", err)
|
|
|
|
return fmt.Errorf("error getting server raft protocol versions: %s", err)
|
|
|
@ -178,7 +178,7 @@ func (b *BasicAutopilot) PromoteNonVoters(autopilotConf *structs.AutopilotConfig
|
|
|
|
// If this server has been stable and passing for long enough, promote it to a voter
|
|
|
|
// If this server has been stable and passing for long enough, promote it to a voter
|
|
|
|
if !isVoter(server.Suffrage) {
|
|
|
|
if !isVoter(server.Suffrage) {
|
|
|
|
health := b.server.getServerHealth(string(server.ID))
|
|
|
|
health := b.server.getServerHealth(string(server.ID))
|
|
|
|
if health.IsStable(time.Now(), autopilotConf) {
|
|
|
|
if health.IsStable(time.Now(), autopilotConfig) {
|
|
|
|
promotions = append(promotions, server)
|
|
|
|
promotions = append(promotions, server)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|