* Module name changed to `github.com/rajch/weave` * Added environment variable `CHECKPOINT_DISABLE=1` to default manifests, to bypass weaveworks telemetry
157 lines
4.8 KiB
Go
157 lines
4.8 KiB
Go
package ipam
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/weaveworks/mesh"
|
|
|
|
"github.com/rajch/weave/api"
|
|
"github.com/rajch/weave/common"
|
|
"github.com/rajch/weave/net/address"
|
|
)
|
|
|
|
type claim struct {
|
|
resultChan chan<- error
|
|
tryCount int
|
|
ident string // a container ID, something like "weave:expose", or api.NoContainerID
|
|
cidr address.CIDR // single address being claimed
|
|
isContainer bool // true if ident is a container ID
|
|
noErrorOnUnknown bool // if false, error or block if we don't know; if true return ok but keep trying
|
|
hasBeenCancelled func() bool
|
|
}
|
|
|
|
const maxTryCount = 5
|
|
|
|
// 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
|
|
}
|
|
|
|
c.tryCount++
|
|
|
|
addOwned := func() {
|
|
if c.ident == api.NoContainerID {
|
|
alloc.addOwned(c.cidr.Addr.String(), c.cidr, c.isContainer)
|
|
} else {
|
|
alloc.addOwned(c.ident, c.cidr, c.isContainer)
|
|
}
|
|
}
|
|
|
|
if !alloc.ring.Contains(c.cidr.Addr) {
|
|
alloc.infof("Address %s claimed by %s - not in our range", c.cidr, c.ident)
|
|
previousOwner := alloc.findOwner(c.cidr.Addr)
|
|
switch {
|
|
case previousOwner == "":
|
|
addOwned()
|
|
case previousOwner == c.ident: // already owned by this ID
|
|
case c.ident == api.NoContainerID: // already owned by anonymous container
|
|
// do nothing (no automatic fall-through in Go)
|
|
case !alloc.dead[previousOwner].IsZero(): // already owned by dead container
|
|
alloc.removeOwned(previousOwner, c.cidr.Addr)
|
|
addOwned()
|
|
default:
|
|
c.sendResult(fmt.Errorf("address %s already in use by %s", c.cidr, previousOwner))
|
|
}
|
|
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 if c.tryCount > maxTryCount {
|
|
// give up, tell the user they can't do this
|
|
c.deniedBy(alloc, owner, err)
|
|
return true
|
|
}
|
|
}
|
|
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)
|
|
addOwned()
|
|
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, err error) {
|
|
name, found := alloc.nicknames[owner]
|
|
if found {
|
|
name = " (" + name + ")"
|
|
}
|
|
reason := ""
|
|
if err != nil {
|
|
reason = fmt.Sprintf(" - %s", err)
|
|
}
|
|
c.sendResult(fmt.Errorf("address %s is owned by other peer %s%s%s", c.cidr.String(), owner, name, reason))
|
|
}
|
|
|
|
func (c *claim) Cancel() {
|
|
c.sendResult(&errorCancelled{"Claim", c.ident})
|
|
}
|
|
|
|
func (c *claim) ForContainer(ident string) bool {
|
|
return c.ident == ident
|
|
}
|