detect when IPAM has been seeded by different peers
This is an early detector for a multitude of other symptoms, some of which cause panics. When creating the ring we record the paxos-supplied normalised list of peers used for seeding. This subsequently gets gossipped around as part of the Ring data structure, and checked in ring.Merge that it is the same for the recipient. There are some subtleties due to the desire to maintain backward compatibility and deal with empty rings - The gossip gob decoder will ignore the Ring.Seeds field when an old weave receives a Ring from a new weave. So everything on the old weave will just continue to work as before. - The gossip gob decoder will set the Ring.Seeds field to nil when a new weave receives a Ring from an old weave. We therefore omit the seed equivalence check when the received Seeds slice is empty. - Our ring may not have any seeds - either because it is empty or was created from a ring received from an old weave. We therefore a) omit the seed equivalence check in that situation, and b) set our seeds to those received. Note that this allows us to learn about the seeds well after ring creation - a situation which may arise when receiving gossip from a mixture of old and new weaves. Fixes #1463.
This commit is contained in:
1 parent
9ad1ccf19f
commit
7e0ae6112c
2 files changed
+49
-3
No files matched your search
+21
-3
@@ -19,9 +19,10 @@ import (
|
||||
|
||||
// Ring represents the ring itself
|
||||
type Ring struct {
|
||||
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
|
||||
Peer router.PeerName // name of peer owning this ring instance
|
||||
Entries entries // list of entries sorted by token
|
||||
Start, End address.Address // [min, max) tokens in this ring. Due to wrapping, min == max (effectively)
|
||||
Peer router.PeerName // name of peer owning this ring instance
|
||||
Entries entries // list of entries sorted by token
|
||||
Seeds []router.PeerName // peers with which the ring was seeded
|
||||
}
|
||||
|
||||
func (r *Ring) assertInvariants() {
|
||||
@@ -36,6 +37,7 @@ var (
|
||||
ErrNotSorted = errors.New("Ring not sorted")
|
||||
ErrTokenRepeated = errors.New("Token appears twice in ring")
|
||||
ErrTokenOutOfRange = errors.New("Token is out of range")
|
||||
ErrDifferentSeeds = errors.New("Received ring was seeded differently from ours!")
|
||||
ErrDifferentRange = errors.New("Received range differs from ours!")
|
||||
ErrNewerVersion = errors.New("Received new version for entry I own!")
|
||||
ErrInvalidEntry = errors.New("Received invalid state update!")
|
||||
@@ -188,6 +190,17 @@ func (r *Ring) Merge(gossip Ring) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(gossip.Seeds) > 0 && len(r.Seeds) > 0 {
|
||||
if len(gossip.Seeds) != len(r.Seeds) {
|
||||
return ErrDifferentSeeds
|
||||
}
|
||||
for i, seed := range gossip.Seeds {
|
||||
if seed != r.Seeds[i] {
|
||||
return ErrDifferentSeeds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.Start != gossip.Start || r.End != gossip.End {
|
||||
return ErrDifferentRange
|
||||
}
|
||||
@@ -254,6 +267,9 @@ func (r *Ring) Merge(gossip Ring) error {
|
||||
previousOwner = nil
|
||||
}
|
||||
|
||||
if len(r.Seeds) == 0 {
|
||||
r.Seeds = gossip.Seeds
|
||||
}
|
||||
r.Entries = result
|
||||
return nil
|
||||
}
|
||||
@@ -330,6 +346,8 @@ func (r *Ring) ClaimForPeers(peers []router.PeerName) {
|
||||
}
|
||||
|
||||
common.Assert(pos == r.End)
|
||||
|
||||
r.Seeds = peers
|
||||
}
|
||||
|
||||
func (r *Ring) FprintWithNicknames(w io.Writer, m map[router.PeerName]string) {
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "IPAM ring seed mismatch"
|
||||
|
||||
weave_on $HOST1 launch-router --no-discovery
|
||||
weave_on $HOST2 launch-router --no-discovery
|
||||
weave_on $HOST3 launch-router --no-discovery
|
||||
|
||||
# Do some allocations to get the ring initialized on two hosts
|
||||
start_container $HOST1
|
||||
start_container $HOST2
|
||||
|
||||
# Connect 2 to 3 and allocate so they will each have half the ring
|
||||
weave_on $HOST2 connect $HOST3
|
||||
start_container $HOST3
|
||||
|
||||
# More allocations on 1 to increase the version number
|
||||
start_container $HOST1
|
||||
start_container $HOST1
|
||||
|
||||
weave_on $HOST1 connect $HOST3
|
||||
|
||||
# Check that weave on 3 is still running
|
||||
assert_raises "weave_on $HOST3 status"
|
||||
|
||||
end_suite
|
||||
Reference in new issue
Block a user