diff --git a/ipam/allocator.go b/ipam/allocator.go index 04ad0cd9..05fdcda1 100644 --- a/ipam/allocator.go +++ b/ipam/allocator.go @@ -55,19 +55,21 @@ type Allocator struct { paxos *paxos.Node paxosTicker *time.Ticker shuttingDown bool // to avoid doing any requests while trying to shut down + isKnownPeer func(router.PeerName) bool now func() time.Time } // NewAllocator creates and initialises a new Allocator -func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname string, universe address.Range, quorum uint) *Allocator { +func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname string, universe address.Range, quorum uint, isKnownPeer func(name router.PeerName) bool) *Allocator { return &Allocator{ - ourName: ourName, - universe: universe, - ring: ring.New(universe.Start, universe.End, ourName), - owned: make(map[string][]address.Address), - paxos: paxos.NewNode(ourName, ourUID, quorum), - nicknames: map[router.PeerName]string{ourName: ourNickname}, - now: time.Now, + ourName: ourName, + universe: universe, + ring: ring.New(universe.Start, universe.End, ourName), + owned: make(map[string][]address.Address), + paxos: paxos.NewNode(ourName, ourUID, quorum), + nicknames: map[router.PeerName]string{ourName: ourNickname}, + isKnownPeer: isKnownPeer, + now: time.Now, } } @@ -274,6 +276,33 @@ func (alloc *Allocator) Free(ident string, addrToFree address.Address) error { return <-errChan } +func (alloc *Allocator) pickPeerFromNicknames(isValid func(router.PeerName) bool) router.PeerName { + for name := range alloc.nicknames { + if name != alloc.ourName && isValid(name) { + return name + } + } + return router.UnknownPeerName +} + +func (alloc *Allocator) pickPeerForTransfer() router.PeerName { + // first try alive peers that actively participate in IPAM (i.e. have entries) + if heir := alloc.ring.PickPeerForTransfer(alloc.isKnownPeer); heir != router.UnknownPeerName { + return heir + } + // next try alive peers that have IPAM enabled but have no entries + if heir := alloc.pickPeerFromNicknames(alloc.isKnownPeer); heir != router.UnknownPeerName { + return heir + } + // next try disappeared peers that still have entries + t := func(router.PeerName) bool { return true } + if heir := alloc.ring.PickPeerForTransfer(t); heir != router.UnknownPeerName { + return heir + } + // finally, disappeared peers that that passively participated in IPAM + return alloc.pickPeerFromNicknames(t) +} + // Shutdown (Sync) func (alloc *Allocator) Shutdown() { alloc.infof("Shutdown") @@ -282,7 +311,7 @@ func (alloc *Allocator) Shutdown() { alloc.shuttingDown = true alloc.cancelOps(&alloc.pendingClaims) alloc.cancelOps(&alloc.pendingAllocates) - if heir := alloc.ring.PickPeerForTransfer(); heir != router.UnknownPeerName { + if heir := alloc.pickPeerForTransfer(); heir != router.UnknownPeerName { alloc.ring.Transfer(alloc.ourName, heir) alloc.space.Clear() alloc.gossip.GossipBroadcast(alloc.Gossip()) @@ -310,7 +339,6 @@ func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error { return } - delete(alloc.nicknames, peername) newRanges, err := alloc.ring.Transfer(peername, alloc.ourName) alloc.space.AddRanges(newRanges) resultChan <- err @@ -332,11 +360,11 @@ func (alloc *Allocator) lookupPeername(name string) (router.PeerName, error) { return router.PeerNameFromString(name) } -// Restrict the peers in "nicknames" to those in the ring and our own +// Restrict the peers in "nicknames" to those in the ring plus peers known to the router func (alloc *Allocator) pruneNicknames() { ringPeers := alloc.ring.PeerNames() for name := range alloc.nicknames { - if _, ok := ringPeers[name]; !ok && name != alloc.ourName { + if _, ok := ringPeers[name]; !ok && !alloc.isKnownPeer(name) { delete(alloc.nicknames, name) } } diff --git a/ipam/ring/ring.go b/ipam/ring/ring.go index b82bad1d..fee7991e 100644 --- a/ipam/ring/ring.go +++ b/ipam/ring/ring.go @@ -470,9 +470,9 @@ func (r *Ring) ChoosePeersToAskForSpace(start, end address.Address) []router.Pee return result } -func (r *Ring) PickPeerForTransfer() router.PeerName { +func (r *Ring) PickPeerForTransfer(isValid func(router.PeerName) bool) router.PeerName { for _, entry := range r.Entries { - if entry.Peer != r.Peer { + if entry.Peer != r.Peer && isValid(entry.Peer) { return entry.Peer } } diff --git a/ipam/testutils_test.go b/ipam/testutils_test.go index 95f6407a..144e313f 100644 --- a/ipam/testutils_test.go +++ b/ipam/testutils_test.go @@ -135,7 +135,7 @@ func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, addres } alloc := NewAllocator(peername, router.PeerUID(rand.Int63()), - "nick-"+name, cidr.Range(), quorum) + "nick-"+name, cidr.Range(), quorum, func(router.PeerName) bool { return true }) return alloc, cidr.HostRange() } diff --git a/prog/weaver/main.go b/prog/weaver/main.go index db0f7144..453584b3 100644 --- a/prog/weaver/main.go +++ b/prog/weaver/main.go @@ -375,7 +375,8 @@ func createAllocator(router *weave.Router, ipRangeStr string, defaultSubnetStr s Log.Fatalf("IP address allocation default subnet %s does not overlap with allocation range %s", defaultSubnet, ipRange) } } - allocator := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange.Range(), quorum) + isKnownPeer := func(name weave.PeerName) bool { return router.Peers.Fetch(name) != nil } + allocator := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange.Range(), quorum, isKnownPeer) allocator.SetInterfaces(router.NewGossip("IPallocation", allocator)) allocator.Start() diff --git a/test/165_reset_2_test.sh b/test/165_reset_2_test.sh new file mode 100755 index 00000000..05d68677 --- /dev/null +++ b/test/165_reset_2_test.sh @@ -0,0 +1,22 @@ +#! /bin/bash + +. ./config.sh + +start_suite "Rolling restart with reset" + +weave_on $HOST1 launch-router +weave_on $HOST2 launch-router $HOST1 + +start_container $HOST1 --name=c1 +start_container $HOST2 --name=c2 + +# Now reset the routers so they should give their space away, and restart +weave_on $HOST1 reset +weave_on $HOST1 launch-router $HOST2 +weave_on $HOST2 reset +weave_on $HOST2 launch-router $HOST1 + +# And try to start a container +assert_raises "timeout 10 cat <( start_container $HOST1 )" + +end_suite