This repository was archived by the owner on Jun 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 680
/
Copy pathclaim.go
159 lines (142 loc) · 4.95 KB
/
claim.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
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)
case c.ident == "weave:expose":
alloc.debugln("Ignoring weave:expose")
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
}