diff --git a/ipam/allocator.go b/ipam/allocator.go index 90177d46..0c8e22dd 100644 --- a/ipam/allocator.go +++ b/ipam/allocator.go @@ -215,9 +215,9 @@ func (alloc *Allocator) Lookup(ident string, r address.Range) (address.Address, } // Claim an address that we think we should own (Sync) -func (alloc *Allocator) Claim(ident string, addr address.Address) error { +func (alloc *Allocator) Claim(ident string, addr address.Address, reclaim bool) error { resultChan := make(chan error) - op := &claim{resultChan: resultChan, ident: ident, addr: addr} + op := &claim{resultChan: resultChan, ident: ident, addr: addr, reclaim: reclaim} alloc.doOperation(op, &alloc.pendingClaims) return <-resultChan } diff --git a/ipam/allocator_test.go b/ipam/allocator_test.go index 34222593..cbaef1f5 100644 --- a/ipam/allocator_test.go +++ b/ipam/allocator_test.go @@ -143,7 +143,7 @@ func TestAllocatorClaim(t *testing.T) { addr1, _ := address.ParseIP(testAddr1) // First claim should trigger "dunno, I'm going to wait" - err := alloc.Claim(container3, addr1) + err := alloc.Claim(container3, addr1, true) require.NoError(t, err) // Do one allocate to ensure paxos is all done @@ -152,23 +152,23 @@ func TestAllocatorClaim(t *testing.T) { addrx, err := allocs[0].Allocate(container1, subnet, nil) // Now try the claim again - err = alloc.Claim(container3, addr1) + err = alloc.Claim(container3, addr1, true) require.NoError(t, err) // Check we get this address back if we try an allocate addr3, _ := alloc.Allocate(container3, subnet, nil) require.Equal(t, testAddr1, addr3.String(), "address") // one more claim should still work - err = alloc.Claim(container3, addr1) + err = alloc.Claim(container3, addr1, true) require.NoError(t, err) // claim for a different container should fail - err = alloc.Claim(container1, addr1) + err = alloc.Claim(container1, addr1, true) require.Error(t, err) // claiming the address allocated on the other peer should fail - err = alloc.Claim(container1, addrx) + err = alloc.Claim(container1, addrx, true) require.Error(t, err, "claiming address allocated on other peer should fail") // Check an address outside of our universe addr2, _ := address.ParseIP(testAddr2) - err = alloc.Claim(container1, addr2) + err = alloc.Claim(container1, addr2, true) require.NoError(t, err) } @@ -465,7 +465,7 @@ func TestAllocatorFuzz(t *testing.T) { addressIndex := rand.Int31n(int32(subnet.Size())) alloc := allocs[allocIndex] addr := address.Add(subnet.Start, address.Offset(addressIndex)) - err := alloc.Claim(name, addr) + err := alloc.Claim(name, addr, true) if err == nil { noteAllocation(allocIndex, name, addr) } diff --git a/ipam/claim.go b/ipam/claim.go index fa35ef8f..2cb2c1f6 100644 --- a/ipam/claim.go +++ b/ipam/claim.go @@ -12,6 +12,7 @@ type claim struct { resultChan chan<- error ident string addr address.Address + reclaim bool } func (c *claim) sendResult(result error) { @@ -20,6 +21,7 @@ func (c *claim) sendResult(result error) { c.resultChan <- result close(c.resultChan) c.resultChan = nil + return } if result != nil { common.Log.Errorln("[allocator] " + result.Error()) @@ -30,7 +32,9 @@ func (c *claim) sendResult(result error) { func (c *claim) Try(alloc *Allocator) bool { if !alloc.ring.Contains(c.addr) { // Address not within our universe; assume user knows what they are doing - alloc.infof("Ignored address %s claimed by %s - not in our universe", c.addr, c.ident) + if c.reclaim { + alloc.infof("Ignored address %s claimed by %s - not in our universe", c.addr, c.ident) + } c.sendResult(nil) return true } @@ -40,15 +44,23 @@ func (c *claim) Try(alloc *Allocator) bool { // success case router.UnknownPeerName: // If our ring doesn't know, it must be empty. - alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.addr, c.ident) - c.sendResult(nil) // don't make the caller wait + if c.reclaim { + alloc.infof("Claim %s for %s: address allocator still initializing; will try later.", c.addr, c.ident) + c.sendResult(nil) // don't make the caller wait + } else { + c.sendResult(fmt.Errorf("%s is in the range %s, but the allocator is not initialized yet", c.addr, alloc.universe.AsCIDRString())) + } return false default: alloc.debugf("requesting address %s from other peer %s", c.addr, owner) err := alloc.sendSpaceRequest(owner, address.NewRange(c.addr, 1)) - if err != nil { // can't speak to owner right now; figure it out later - alloc.infof("Claim %s for %s: %s; will try later.", c.addr, c.ident, err) - c.sendResult(nil) + if err != nil { // can't speak to owner right now + if c.reclaim { // don't hold up launch; figure it out later + alloc.infof("Claim %s for %s: %s; will try later.", c.addr, c.ident, err) + c.sendResult(nil) + } else { // just tell the user they can't do this. + c.DeniedBy(alloc, owner) + } } return false } diff --git a/ipam/http.go b/ipam/http.go index 3fcbb332..e18d4caf 100644 --- a/ipam/http.go +++ b/ipam/http.go @@ -22,10 +22,11 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID vars := mux.Vars(r) ident := vars["id"] ipStr := vars["ip"] + reclaim := r.FormValue("reclaim") == "true" if ip, err := address.ParseIP(ipStr); err != nil { badRequest(w, err) return - } else if err := alloc.Claim(ident, ip); err != nil { + } else if err := alloc.Claim(ident, ip, reclaim); err != nil { badRequest(w, fmt.Errorf("Unable to claim: %s", err)) return } diff --git a/weave b/weave index e3ce04ef..f7ad69b1 100755 --- a/weave +++ b/weave @@ -812,10 +812,10 @@ delete_dns_fqdn() { # Claim addresses for a container in IPAM. Expects to be called from # with_container_addresses. -ipam_claim() { +ipam_reclaim() { for CIDR in $3 ; do # NB: CONTAINER_IP is the IP of the weave container; it is set by wait_for_status. - http_call_ip $CONTAINER_IP $HTTP_PORT PUT /ip/$1/${CIDR%/*} + http_call_ip $CONTAINER_IP $HTTP_PORT PUT /ip/$1/${CIDR%/*}?reclaim=true done } @@ -862,6 +862,9 @@ ipam_cidrs() { else # This is a plain IP address; warn if it clashes but carry on command_exists netcheck && netcheck --ignore-iface=$BRIDGE $1 || true + if container_ip $CONTAINER_NAME 2>/dev/null ; then + http_call_ip $CONTAINER_IP $HTTP_PORT PUT /ip/$CONTAINER_ID/${1%/*} >&2 + fi ALL_CIDRS="$ALL_CIDRS $1" fi shift 1 @@ -1091,7 +1094,7 @@ launch_router() { wait_for_status $CONTAINER_NAME $HTTP_PORT if [ -n "$IPRANGE" ] ; then # Tell the newly-started weave IP allocator about existing weave IPs - with_container_addresses ipam_claim weave:expose $(docker ps -q --no-trunc) + with_container_addresses ipam_reclaim weave:expose $(docker ps -q --no-trunc) fi if [ -n "$DNS_PORT_MAPPING" ] ; then # Tell the newly-started weaveDNS about existing weave IPs