Merge pull request #1353 from hashicorp/b-upshift

Uses correct version for automatic upshift, sets coordinate Raft log entries to ignore.
This commit is contained in:
James Phillips 2015-10-27 16:17:14 -07:00
commit 03d173c647
5 changed files with 142 additions and 48 deletions

View File

@ -549,22 +549,6 @@ func (a *Agent) WANMembers() []serf.Member {
} }
} }
// CanServersUnderstandProtocol checks to see if all the servers understand the
// given protocol version.
func (a *Agent) CanServersUnderstandProtocol(version uint8) bool {
numServers, numWhoGrok := 0, 0
members := a.LANMembers()
for _, member := range members {
if member.Tags["role"] == "consul" {
numServers++
if member.ProtocolMax >= version {
numWhoGrok++
}
}
}
return (numServers > 0) && (numWhoGrok == numServers)
}
// StartSync is called once Services and Checks are registered. // StartSync is called once Services and Checks are registered.
// This is called to prevent a race between clients and the anti-entropy routines // This is called to prevent a race between clients and the anti-entropy routines
func (a *Agent) StartSync() { func (a *Agent) StartSync() {
@ -603,13 +587,19 @@ func (a *Agent) sendCoordinate() {
select { select {
case <-time.After(intv): case <-time.After(intv):
if !a.CanServersUnderstandProtocol(3) { members := a.LANMembers()
grok, err := consul.CanServersUnderstandProtocol(members, 3)
if err != nil {
a.logger.Printf("[ERR] agent: failed to check servers: %s", err)
continue
}
if !grok {
a.logger.Printf("[DEBUG] agent: skipping coordinate updates until servers are upgraded")
continue continue
} }
var c *coordinate.Coordinate c, err := a.GetCoordinate()
var err error if err != nil {
if c, err = a.GetCoordinate(); err != nil {
a.logger.Printf("[ERR] agent: failed to get coordinate: %s", err) a.logger.Printf("[ERR] agent: failed to get coordinate: %s", err)
continue continue
} }

View File

@ -1602,30 +1602,3 @@ func TestAgent_GetCoordinate(t *testing.T) {
check(true) check(true)
check(false) check(false)
} }
func TestAgent_CanServersUnderstandProtocol(t *testing.T) {
config := nextConfig()
dir, agent := makeAgent(t, config)
defer os.RemoveAll(dir)
defer agent.Shutdown()
min := uint8(consul.ProtocolVersionMin)
if !agent.CanServersUnderstandProtocol(min) {
t.Fatalf("should grok %d", min)
}
max := uint8(consul.ProtocolVersionMax)
if !agent.CanServersUnderstandProtocol(max) {
t.Fatalf("should grok %d", max)
}
current := uint8(config.Protocol)
if !agent.CanServersUnderstandProtocol(current) {
t.Fatalf("should grok %d", current)
}
future := max + 1
if agent.CanServersUnderstandProtocol(future) {
t.Fatalf("should not grok %d", future)
}
}

View File

@ -87,8 +87,12 @@ func (c *Coordinate) batchApplyUpdates() error {
end = size end = size
} }
// We set the "safe to ignore" flag on this update type so old
// servers don't crash if they see one of these.
t := structs.CoordinateBatchUpdateType | structs.IgnoreUnknownTypeFlag
slice := updates[start:end] slice := updates[start:end]
if _, err := c.srv.raftApply(structs.CoordinateBatchUpdateType, slice); err != nil { if _, err := c.srv.raftApply(t, slice); err != nil {
return err return err
} }
} }

View File

@ -95,6 +95,35 @@ func ensurePath(path string, dir bool) error {
return os.MkdirAll(path, 0755) return os.MkdirAll(path, 0755)
} }
// CanServersUnderstandProtocol checks to see if all the servers in the given
// list understand the given protocol version. If there are no servers in the
// list then this will return false.
func CanServersUnderstandProtocol(members []serf.Member, version uint8) (bool, error) {
numServers, numWhoGrok := 0, 0
for _, m := range members {
if m.Tags["role"] != "consul" {
continue
}
numServers++
vsn_min, err := strconv.Atoi(m.Tags["vsn_min"])
if err != nil {
return false, err
}
vsn_max, err := strconv.Atoi(m.Tags["vsn_max"])
if err != nil {
return false, err
}
v := int(version)
if (v >= vsn_min) && (v <= vsn_max) {
numWhoGrok++
}
}
return (numServers > 0) && (numWhoGrok == numServers), nil
}
// Returns if a member is a consul server. Returns a bool, // Returns if a member is a consul server. Returns a bool,
// the datacenter, and the rpc port // the datacenter, and the rpc port
func isConsulServer(m serf.Member) (bool, *serverParts) { func isConsulServer(m serf.Member) (bool, *serverParts) {

View File

@ -2,6 +2,7 @@ package consul
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"regexp" "regexp"
"testing" "testing"
@ -118,6 +119,103 @@ func TestIsPrivateIP(t *testing.T) {
} }
} }
func TestUtil_CanServersUnderstandProtocol(t *testing.T) {
var members []serf.Member
// All empty list cases should return false.
for v := ProtocolVersionMin; v <= ProtocolVersionMax; v++ {
grok, err := CanServersUnderstandProtocol(members, v)
if err != nil {
t.Fatalf("err: %v", err)
}
if grok {
t.Fatalf("empty list should always return false")
}
}
// Add a non-server member.
members = append(members, serf.Member{
Tags: map[string]string{
"vsn_min": fmt.Sprintf("%d", ProtocolVersionMin),
"vsn_max": fmt.Sprintf("%d", ProtocolVersionMax),
},
})
// Make sure it doesn't get counted.
for v := ProtocolVersionMin; v <= ProtocolVersionMax; v++ {
grok, err := CanServersUnderstandProtocol(members, v)
if err != nil {
t.Fatalf("err: %v", err)
}
if grok {
t.Fatalf("non-server members should not be counted")
}
}
// Add a server member.
members = append(members, serf.Member{
Tags: map[string]string{
"role": "consul",
"vsn_min": fmt.Sprintf("%d", ProtocolVersionMin),
"vsn_max": fmt.Sprintf("%d", ProtocolVersionMax),
},
})
// Now it should report that it understands.
for v := ProtocolVersionMin; v <= ProtocolVersionMax; v++ {
grok, err := CanServersUnderstandProtocol(members, v)
if err != nil {
t.Fatalf("err: %v", err)
}
if !grok {
t.Fatalf("server should grok")
}
}
// Nobody should understand anything from the future.
for v := uint8(ProtocolVersionMax + 1); v <= uint8(ProtocolVersionMax+10); v++ {
grok, err := CanServersUnderstandProtocol(members, v)
if err != nil {
t.Fatalf("err: %v", err)
}
if grok {
t.Fatalf("server should not grok")
}
}
// Add an older server.
members = append(members, serf.Member{
Tags: map[string]string{
"role": "consul",
"vsn_min": fmt.Sprintf("%d", ProtocolVersionMin),
"vsn_max": fmt.Sprintf("%d", ProtocolVersionMax-1),
},
})
// The servers should no longer understand the max version.
for v := ProtocolVersionMin; v <= ProtocolVersionMax; v++ {
grok, err := CanServersUnderstandProtocol(members, v)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := v < ProtocolVersionMax
if grok != expected {
t.Fatalf("bad: %v != %v", grok, expected)
}
}
// Try a version that's too low for the minimum.
{
grok, err := CanServersUnderstandProtocol(members, 0)
if err != nil {
t.Fatalf("err: %v", err)
}
if grok {
t.Fatalf("server should not grok")
}
}
}
func TestIsConsulServer(t *testing.T) { func TestIsConsulServer(t *testing.T) {
m := serf.Member{ m := serf.Member{
Name: "foo", Name: "foo",