Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not bootstrap with non voters #4699

Merged
merged 1 commit into from
Sep 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions agent/consul/server_serf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down
56 changes: 56 additions & 0 deletions agent/consul/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions agent/metadata/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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{
Expand All @@ -158,6 +162,7 @@ func IsConsulServer(m serf.Member) (bool, *Server) {
RaftVersion: raftVsn,
Status: m.Status,
UseTLS: useTLS,
NonVoter: nonVoter,
}
return true, parts
}
10 changes: 10 additions & 0 deletions agent/metadata/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func TestIsConsulServer(t *testing.T) {
"expect": "3",
"raft_vsn": "3",
"use_tls": "1",
"nonvoter": "1",
},
Status: serf.StatusLeft,
}
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand Down