diff --git a/ipam/allocator.go b/ipam/allocator.go index 79229c03..ae990444 100644 --- a/ipam/allocator.go +++ b/ipam/allocator.go @@ -376,29 +376,40 @@ func (alloc *Allocator) Shutdown() { <-doneChan } -// AdminTakeoverRanges (Sync) - take over the ranges owned by a given peer. +// AdminTakeoverRanges (Sync) - take over the ranges owned by a given +// peer, and return how much space was transferred in the process. // Only done on adminstrator command. -func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) error { - resultChan := make(chan error) +func (alloc *Allocator) AdminTakeoverRanges(peerNameOrNickname string) address.Count { + resultChan := make(chan address.Count) alloc.actionChan <- func() { peername, err := alloc.lookupPeername(peerNameOrNickname) if err != nil { - resultChan <- fmt.Errorf("Cannot find peer '%s'", peerNameOrNickname) + alloc.warnf("attempt to take over range from unknown peer '%s'", peerNameOrNickname) + resultChan <- address.Count(0) return } alloc.debugln("AdminTakeoverRanges:", peername) if peername == alloc.ourName { - resultChan <- fmt.Errorf("Cannot take over ranges from yourself!") + alloc.warnf("attempt to take over range from ourself") + resultChan <- address.Count(0) return } - newRanges, err := alloc.ring.Transfer(peername, alloc.ourName) - if err == nil && len(newRanges) > 0 { - alloc.space.AddRanges(newRanges) - alloc.gossip.GossipBroadcast(alloc.Gossip()) + newRanges := alloc.ring.Transfer(peername, alloc.ourName) + + if len(newRanges) == 0 { + resultChan <- address.Count(0) + return } - resultChan <- err + + before := alloc.space.NumFreeAddressesInRange(alloc.universe) + alloc.space.AddRanges(newRanges) + after := alloc.space.NumFreeAddressesInRange(alloc.universe) + + alloc.gossip.GossipBroadcast(alloc.Gossip()) + + resultChan <- after - before } return <-resultChan } diff --git a/ipam/allocator_test.go b/ipam/allocator_test.go index de5f6c62..dd775a6a 100644 --- a/ipam/allocator_test.go +++ b/ipam/allocator_test.go @@ -336,13 +336,18 @@ func TestTransfer(t *testing.T) { router.Flush() alloc2.gossip.GossipBroadcast(alloc3.Gossip()) router.Flush() + + free2 := alloc2.NumFreeAddresses(subnet.Range()) + free3 := alloc3.NumFreeAddresses(subnet.Range()) + router.RemovePeer(alloc2.ourName) router.RemovePeer(alloc3.ourName) alloc2.Stop() alloc3.Stop() router.Flush() - require.NoError(t, alloc1.AdminTakeoverRanges(alloc2.ourName.String())) - require.NoError(t, alloc1.AdminTakeoverRanges(alloc3.ourName.String())) + + require.Equal(t, free2+1, alloc1.AdminTakeoverRanges(alloc2.ourName.String())) + require.Equal(t, free3+1, alloc1.AdminTakeoverRanges(alloc3.ourName.String())) router.Flush() require.Equal(t, address.Count(1024), alloc1.NumFreeAddresses(subnet.Range())) diff --git a/ipam/http.go b/ipam/http.go index 1128f653..7330b96c 100644 --- a/ipam/http.go +++ b/ipam/http.go @@ -151,11 +151,7 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ident := mux.Vars(r)["id"] - if err := alloc.AdminTakeoverRanges(ident); err != nil { - badRequest(w, err) - return - } - - w.WriteHeader(204) + transferred := alloc.AdminTakeoverRanges(ident) + fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident) }) } diff --git a/ipam/ring/ring.go b/ipam/ring/ring.go index 58e320ba..d7f07906 100644 --- a/ipam/ring/ring.go +++ b/ipam/ring/ring.go @@ -498,28 +498,22 @@ func (r *Ring) PickPeerForTransfer(isValid func(mesh.PeerName) bool) mesh.PeerNa // Transfer will mark all entries associated with 'from' peer as owned by 'to' peer // and return ranges indicating the new space we picked up -func (r *Ring) Transfer(from, to mesh.PeerName) ([]address.Range, error) { +func (r *Ring) Transfer(from, to mesh.PeerName) []address.Range { r.assertInvariants() defer r.assertInvariants() defer r.updateExportedVariables() var newRanges []address.Range - found := false for i, entry := range r.Entries { if entry.Peer == from { - found = true entry.Peer = to entry.Version++ newRanges = append(newRanges, address.Range{Start: entry.Token, End: r.Entries.entry(i + 1).Token}) } } - if !found { - return nil, ErrNotFound - } - - return r.splitRangesOverZero(newRanges), nil + return r.splitRangesOverZero(newRanges) } // Contains returns true if addr is in this ring diff --git a/site/ipam/stop-remove-peers-ipam.md b/site/ipam/stop-remove-peers-ipam.md index 7c07645f..433b8148 100644 --- a/site/ipam/stop-remove-peers-ipam.md +++ b/site/ipam/stop-remove-peers-ipam.md @@ -24,6 +24,7 @@ Assume you had started the three peers in the example earlier, and then host3 caught fire, you can go to one of the other hosts and run: host1$ weave rmpeer host3 + 524288 IPs taken over from host3 Weave Net takes all the IP address ranges owned by host3 and transfers them to be owned by host1. The name "host3" is resolved via the @@ -36,4 +37,4 @@ status`. * [Address Allocation with IP Address Management (IPAM)](/site/ipam/overview-init-ipam.md) * [Automatic Allocation Across Multiple Subnets](/site/ipam/allocation-multi-ipam.md) * [Isolating Applications on a Weave Network](/site/using-weave/isolating-applications.md) - \ No newline at end of file +