Files
weave/ipam/claim.go
T
Bryan Boreham 39ac1a0037 Collect existing weave addresses at start-up, to avoid a race when
doing it from the script

Note we need to run weaver in the host pid namespace, so it can search
for existing container IPs. Also now we are firing off sub-processes
it's better to not be PID 1 so Linux doesn't expect us to reap zombies.
2017-02-10 15:10:02 +00:00

131 lines
4.0 KiB
Go

package ipam
import (
"fmt"
"github.com/weaveworks/mesh"
"github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common"
"github.com/weaveworks/weave/net/address"
)
type claim struct {
resultChan chan<- error
ident string
cidr address.CIDR
isContainer bool
noErrorOnUnknown bool
hasBeenCancelled func() bool
}
// Send an error (or nil for success) back to caller listening on resultChan
func (c *claim) sendResult(result error) {
// Make sure we only send a result once, since listener stops listening after that
if c.resultChan != nil {
c.resultChan <- result
close(c.resultChan)
c.resultChan = nil
return
}
if result != nil {
common.Log.Errorln("[allocator] " + result.Error())
}
}
// Try returns true for success (or failure), false if we need to try again later
func (c *claim) Try(alloc *Allocator) bool {
if c.hasBeenCancelled != nil && c.hasBeenCancelled() {
c.Cancel()
return true
}
if !alloc.ring.Contains(c.cidr.Addr) {
// Address not within our universe; assume user knows what they are doing
alloc.infof("Address %s claimed by %s - not in our range", c.cidr, c.ident)
alloc.addOwned(c.ident, c.cidr, c.isContainer)
c.sendResult(nil)
return true
}
alloc.establishRing()
// If we had heard that this container died, resurrect it
delete(alloc.dead, c.ident) // (delete is no-op if key not in map)
switch owner := alloc.ring.Owner(c.cidr.Addr); owner {
case alloc.ourName:
// success
case mesh.UnknownPeerName:
// If our ring doesn't know, it must be empty.
alloc.infof("Claim %s for %s: is in the range %s, but the allocator is not initialized yet; will try later.",
c.cidr, c.ident, alloc.universe)
if c.noErrorOnUnknown {
c.sendResult(nil)
}
return false
default:
alloc.debugf("requesting address %s from other peer %s", c.cidr, owner)
err := alloc.sendSpaceRequest(owner, address.NewRange(c.cidr.Addr, 1))
if err != nil { // can't speak to owner right now
if c.noErrorOnUnknown {
alloc.infof("Claim %s for %s: %s; will try later.", c.cidr, c.ident, err)
c.sendResult(nil)
} else { // just tell the user they can't do this.
c.deniedBy(alloc, owner)
}
}
return false
}
// We are the owner, check we haven't given it to another container
existingIdent := alloc.findOwner(c.cidr.Addr)
switch {
case existingIdent == "":
// Unused address, we try to claim it:
if err := alloc.space.Claim(c.cidr.Addr); err == nil {
alloc.debugln("Claimed", c.cidr, "for", c.ident)
if c.ident == api.NoContainerID {
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
} else {
alloc.addOwned(c.ident, c.cidr, c.isContainer)
}
c.sendResult(nil)
} else {
c.sendResult(err)
}
case (existingIdent == c.ident) || (c.ident == api.NoContainerID && existingIdent == c.cidr.Addr.String()):
// same identifier is claiming same address; that's OK
alloc.debugln("Re-Claimed", c.cidr, "for", c.ident)
c.sendResult(nil)
case existingIdent == c.cidr.Addr.String():
// Address already allocated via api.NoContainerID name and current ID is a real container ID:
c.sendResult(fmt.Errorf("address %s already in use", c.cidr))
case c.ident == api.NoContainerID:
// We do not know whether this is the same container or another one,
// but we also cannot prove otherwise, so we let it reclaim the address:
alloc.debugln("Re-Claimed", c.cidr, "for ID", c.ident, "having existing ID as", existingIdent)
c.sendResult(nil)
default:
// Addr already owned by container on this machine
c.sendResult(fmt.Errorf("address %s is already owned by %s", c.cidr.String(), existingIdent))
}
return true
}
func (c *claim) deniedBy(alloc *Allocator, owner mesh.PeerName) {
name, found := alloc.nicknames[owner]
if found {
name = " (" + name + ")"
}
c.sendResult(fmt.Errorf("address %s is owned by other peer %s%s", c.cidr.String(), owner, name))
}
func (c *claim) Cancel() {
c.sendResult(&errorCancelled{"Claim", c.ident})
}
func (c *claim) ForContainer(ident string) bool {
return c.ident == ident
}