Merge pull request #4699 from hashicorp/b-non-voter-bootstrap

Do not bootstrap with non voters
This commit is contained in:
Kyle Havlovitz 2018-09-20 11:13:21 -07:00 committed by GitHub
commit 14d169eb16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 3 deletions

View File

@ -245,6 +245,7 @@ func (s *Server) maybeBootstrap() {
// Scan for all the known servers.
members := s.serfLAN.Members()
var servers []metadata.Server
voters := 0
for _, member := range members {
valid, p := metadata.IsConsulServer(member)
if !valid {
@ -262,11 +263,14 @@ func (s *Server) maybeBootstrap() {
s.logger.Printf("[ERR] consul: Member %v has bootstrap mode. Expect disabled.", member)
return
}
if !p.NonVoter {
voters++
}
servers = append(servers, *p)
}
// Skip if we haven't met the minimum expect count.
if len(servers) < s.config.BootstrapExpect {
if voters < s.config.BootstrapExpect {
return
}
@ -322,9 +326,14 @@ func (s *Server) maybeBootstrap() {
} else {
id = raft.ServerID(addr)
}
suffrage := raft.Voter
if server.NonVoter {
suffrage = raft.Nonvoter
}
peer := raft.Server{
ID: id,
Address: raft.ServerAddress(addr),
ID: id,
Address: raft.ServerAddress(addr),
Suffrage: suffrage,
}
configuration.Servers = append(configuration.Servers, peer)
}

View File

@ -137,6 +137,15 @@ func testServerDCExpect(t *testing.T, dc string, expect int) (string, *Server) {
})
}
func testServerDCExpectNonVoter(t *testing.T, dc string, expect int) (string, *Server) {
return testServerWithConfig(t, func(c *Config) {
c.Datacenter = dc
c.Bootstrap = false
c.BootstrapExpect = expect
c.NonVoter = true
})
}
func testServerWithConfig(t *testing.T, cb func(*Config)) (string, *Server) {
dir, config := testServerConfig(t)
if cb != nil {
@ -579,6 +588,53 @@ func TestServer_Expect(t *testing.T) {
}
}
func TestServer_Expect_NonVoters(t *testing.T) {
t.Parallel()
dir1, s1 := testServerDCExpectNonVoter(t, "dc1", 2)
defer os.RemoveAll(dir1)
defer s1.Shutdown()
dir2, s2 := testServerDCExpectNonVoter(t, "dc1", 2)
defer os.RemoveAll(dir2)
defer s2.Shutdown()
dir3, s3 := testServerDCExpect(t, "dc1", 2)
defer os.RemoveAll(dir3)
defer s3.Shutdown()
dir4, s4 := testServerDCExpect(t, "dc1", 2)
defer os.RemoveAll(dir4)
defer s4.Shutdown()
// Join the first three servers.
joinLAN(t, s2, s1)
joinLAN(t, s3, s1)
// Should have no peers yet since the bootstrap didn't occur.
retry.Run(t, func(r *retry.R) {
r.Check(wantPeers(s1, 0))
r.Check(wantPeers(s2, 0))
r.Check(wantPeers(s3, 0))
})
// Join the fourth node.
joinLAN(t, s4, s1)
// Now we have three servers so we should bootstrap.
retry.Run(t, func(r *retry.R) {
r.Check(wantPeers(s1, 4))
r.Check(wantPeers(s2, 4))
r.Check(wantPeers(s3, 4))
r.Check(wantPeers(s4, 4))
})
// Make sure a leader is elected
testrpc.WaitForLeader(t, s1.RPC, "dc1")
retry.Run(t, func(r *retry.R) {
r.Check(wantRaft([]*Server{s1, s2, s3, s4}))
})
}
func TestServer_BadExpect(t *testing.T) {
t.Parallel()
// this one is in expect=3 mode

View File

@ -38,6 +38,7 @@ type Server struct {
RaftVersion int
Addr net.Addr
Status serf.MemberStatus
NonVoter bool
// If true, use TLS when connecting to this server
UseTLS bool
@ -139,6 +140,9 @@ func IsConsulServer(m serf.Member) (bool, *Server) {
}
}
// Check if the server is a non voter
_, nonVoter := m.Tags["nonvoter"]
addr := &net.TCPAddr{IP: m.Addr, Port: port}
parts := &Server{
@ -158,6 +162,7 @@ func IsConsulServer(m serf.Member) (bool, *Server) {
RaftVersion: raftVsn,
Status: m.Status,
UseTLS: useTLS,
NonVoter: nonVoter,
}
return true, parts
}

View File

@ -65,6 +65,7 @@ func TestIsConsulServer(t *testing.T) {
"expect": "3",
"raft_vsn": "3",
"use_tls": "1",
"nonvoter": "1",
},
Status: serf.StatusLeft,
}
@ -99,6 +100,9 @@ func TestIsConsulServer(t *testing.T) {
if !parts.UseTLS {
t.Fatalf("bad: %v", parts.UseTLS)
}
if !parts.NonVoter {
t.Fatalf("unexpected voter")
}
m.Tags["bootstrap"] = "1"
m.Tags["disabled"] = "1"
ok, parts = metadata.IsConsulServer(m)
@ -125,6 +129,12 @@ func TestIsConsulServer(t *testing.T) {
t.Fatalf("unexpected bootstrap")
}
delete(m.Tags, "nonvoter")
ok, parts = metadata.IsConsulServer(m)
if !ok || parts.NonVoter {
t.Fatalf("unexpected nonvoter")
}
delete(m.Tags, "role")
ok, parts = metadata.IsConsulServer(m)
if ok {