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:
Matthias Radestock committed 2015-10-02 16:33:28 +01:00
1 parent 9ad1ccf19f
commit 7e0ae6112c
2 files changed
+49 -3

No files matched your search

+21 -3
View File
@@ -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) {
+28
View File
@@ -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