-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
types.go
434 lines (362 loc) · 13 KB
/
types.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
// Copyright 2020 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"github.com/cilium/cilium/pkg/cidr"
"github.com/cilium/cilium/pkg/lock"
)
// Limits specifies the IPAM relevant instance limits
type Limits struct {
// Adapters specifies the maximum number of interfaces that can be
// attached to the instance
Adapters int
// IPv4 is the maximum number of IPv4 addresses per adapter/interface
IPv4 int
// IPv6 is the maximum number of IPv6 addresses per adapter/interface
IPv6 int
}
// AllocationIP is an IP which is available for allocation, or already
// has been allocated
type AllocationIP struct {
// Owner is the owner of the IP. This field is set if the IP has been
// allocated. It will be set to the pod name or another identifier
// representing the usage of the IP
//
// The owner field is left blank for an entry in Spec.IPAM.Pool and
// filled out as the IP is used and also added to Status.IPAM.Used.
//
// +optional
Owner string `json:"owner,omitempty"`
// Resource is set for both available and allocated IPs, it represents
// what resource the IP is associated with, e.g. in combination with
// AWS ENI, this will refer to the ID of the ENI
//
// +optional
Resource string `json:"resource,omitempty"`
}
// AllocationMap is a map of allocated IPs indexed by IP
type AllocationMap map[string]AllocationIP
// IPAMSpec is the IPAM specification of the node
//
// This structure is embedded into v2.CiliumNode
type IPAMSpec struct {
// Pool is the list of IPs available to the node for allocation. When
// an IP is used, the IP will remain on this list but will be added to
// Status.IPAM.Used
//
// +optional
Pool AllocationMap `json:"pool,omitempty"`
// PodCIDRs is the list of CIDRs available to the node for allocation.
// When an IP is used, the IP will be added to Status.IPAM.Used
//
// +optional
PodCIDRs []string `json:"podCIDRs,omitempty"`
// MinAllocate is the minimum number of IPs that must be allocated when
// the node is first bootstrapped. It defines the minimum base socket
// of addresses that must be available. After reaching this watermark,
// the PreAllocate and MaxAboveWatermark logic takes over to continue
// allocating IPs.
//
// +kubebuilder:validation:Minimum=0
MinAllocate int `json:"min-allocate,omitempty"`
// MaxAllocate is the maximum number of IPs that can be allocated to the
// node. When the current amount of allocated IPs will approach this value,
// the considered value for PreAllocate will decrease down to 0 in order to
// not attempt to allocate more addresses than defined.
//
// +kubebuilder:validation:Minimum=0
MaxAllocate int `json:"max-allocate,omitempty"`
// PreAllocate defines the number of IP addresses that must be
// available for allocation in the IPAMspec. It defines the buffer of
// addresses available immediately without requiring cilium-operator to
// get involved.
//
// +kubebuilder:validation:Minimum=0
PreAllocate int `json:"pre-allocate,omitempty"`
// MaxAboveWatermark is the maximum number of addresses to allocate
// beyond the addresses needed to reach the PreAllocate watermark.
// Going above the watermark can help reduce the number of API calls to
// allocate IPs, e.g. when a new ENI is allocated, as many secondary
// IPs as possible are allocated. Limiting the amount can help reduce
// waste of IPs.
//
// +kubebuilder:validation:Minimum=0
MaxAboveWatermark int `json:"max-above-watermark,omitempty"`
}
// IPAMStatus is the IPAM status of a node
//
// This structure is embedded into v2.CiliumNode
type IPAMStatus struct {
// Used lists all IPs out of Spec.IPAM.Pool which have been allocated
// and are in use.
//
// +optional
Used AllocationMap `json:"used,omitempty"`
// Operator is the Operator status of the node
//
// +optional
OperatorStatus OperatorStatus `json:"operator-status,omitempty"`
}
// OperatorStatus is the status used by cilium-operator to report
// errors in case the allocation CIDR failed.
type OperatorStatus struct {
// Error is the error message set by cilium-operator.
//
// +optional
Error string `json:"error,omitempty"`
}
// Tags implements generic key value tags
type Tags map[string]string
// Match returns true if the required tags are all found
func (t Tags) Match(required Tags) bool {
for k, neededvalue := range required {
haveValue, ok := t[k]
if !ok || (ok && neededvalue != haveValue) {
return false
}
}
return true
}
// Subnet is a representation of a subnet
type Subnet struct {
// ID is the subnet ID
ID string
// Name is the subnet name
Name string
// CIDR is the CIDR associated with the subnet
CIDR *cidr.CIDR
// AvailabilityZone is the availability zone of the subnet
AvailabilityZone string
// VirtualNetworkID is the virtual network the subnet is in
VirtualNetworkID string
// AvailableAddresses is the number of addresses available for
// allocation
AvailableAddresses int
// Tags is the tags of the subnet
Tags Tags
}
// SubnetMap indexes subnets by subnet ID
type SubnetMap map[string]*Subnet
// FirstSubnetWithAvailableAddresses returns the first pool ID in the list of
// subnets with available addresses. If any of the preferred pool IDs have
// available addresses, the first pool ID with available addresses is returned.
func (m SubnetMap) FirstSubnetWithAvailableAddresses(preferredPoolIDs []PoolID) (PoolID, int) {
for _, p := range preferredPoolIDs {
if s := m[string(p)]; s != nil {
if s.AvailableAddresses > 0 {
return p, s.AvailableAddresses
}
}
}
for poolID, s := range m {
if s.AvailableAddresses > 0 {
return PoolID(poolID), s.AvailableAddresses
}
}
return PoolNotExists, 0
}
// VirtualNetwork is the representation of a virtual network
type VirtualNetwork struct {
// ID is the ID of the virtual network
ID string
// PrimaryCIDR is the primary IPv4 CIDR
PrimaryCIDR string
// CIDRs is the list of secondary IPv4 CIDR ranges associated with the VPC
CIDRs []string
}
// VirtualNetworkMap indexes virtual networks by their ID
type VirtualNetworkMap map[string]*VirtualNetwork
// PoolNotExists indicate that no such pool ID exists
const PoolNotExists = PoolID("")
// PoolUnspec indicates that the pool ID is unspecified
const PoolUnspec = PoolNotExists
// PoolID is the type used to identify an IPAM pool
type PoolID string
// PoolQuota defines the limits of an IPAM pool
type PoolQuota struct {
// AvailabilityZone is the availability zone in which the IPAM pool resides in
AvailabilityZone string
// AvailableIPs is the number of available IPs in the pool
AvailableIPs int
}
// PoolQuotaMap is a map of pool quotas indexes by pool identifier
type PoolQuotaMap map[PoolID]PoolQuota
// Interface is the implementation of a IPAM relevant network interface
// +k8s:deepcopy-gen=false
// +deepequal-gen=false
type Interface interface {
// InterfaceID must return the identifier of the interface
InterfaceID() string
// ForeachAddress must iterate over all addresses of the interface and
// call fn for each address
ForeachAddress(instanceID string, fn AddressIterator) error
}
// InterfaceRevision is the configurationr revision of a network interface. It
// consists of a revision hash representing the current configuration version
// and the resource itself.
//
// +k8s:deepcopy-gen=false
// +deepequal-gen=false
type InterfaceRevision struct {
// Resource is the interface resource
Resource Interface
// Fingerprint is the fingerprint reprsenting the network interface
// configuration. It is typically implemented as the result of a hash
// function calculated off the resource. This field is optional, not
// all IPAM backends make use of fingerprints.
Fingerprint string
}
// Instance is the representation of an instance, typically a VM, subject to
// per-node IPAM logic
//
// +k8s:deepcopy-gen=false
// +deepequal-gen=false
type Instance struct {
// interfaces is a map of all interfaces attached to the instance
// indexed by the interface ID
Interfaces map[string]InterfaceRevision
}
// InstanceMap is the list of all instances indexed by instance ID
//
// +k8s:deepcopy-gen=false
// +deepequal-gen=false
type InstanceMap struct {
mutex lock.RWMutex
data map[string]*Instance
}
// NewInstanceMap returns a new InstanceMap
func NewInstanceMap() *InstanceMap {
return &InstanceMap{data: map[string]*Instance{}}
}
// Update updates the definition of an interface for a particular instance. If
// the interface is already known, the definition is updated, otherwise the
// interface is added to the instance.
func (m *InstanceMap) Update(instanceID string, iface InterfaceRevision) {
m.mutex.Lock()
m.updateLocked(instanceID, iface)
m.mutex.Unlock()
}
func (m *InstanceMap) updateLocked(instanceID string, iface InterfaceRevision) {
if iface.Resource == nil {
return
}
i, ok := m.data[instanceID]
if !ok {
i = &Instance{}
m.data[instanceID] = i
}
if i.Interfaces == nil {
i.Interfaces = map[string]InterfaceRevision{}
}
i.Interfaces[iface.Resource.InterfaceID()] = iface
}
type Address interface{}
// AddressIterator is the function called by the ForeachAddress iterator
type AddressIterator func(instanceID, interfaceID, ip, poolID string, address Address) error
func foreachAddress(instanceID string, instance *Instance, fn AddressIterator) error {
for _, rev := range instance.Interfaces {
if err := rev.Resource.ForeachAddress(instanceID, fn); err != nil {
return err
}
}
return nil
}
// ForeachAddress calls fn for each address on each interface attached to each
// instance. If an instanceID is specified, the only the interfaces and
// addresses of the specified instance are considered.
//
// The InstanceMap is read-locked throughout the iteration process, i.e., no
// updates will occur. However, the address object given to the AddressIterator
// will point to live data and must be deep copied if used outside of the
// context of the iterator function.
func (m *InstanceMap) ForeachAddress(instanceID string, fn AddressIterator) error {
m.mutex.RLock()
defer m.mutex.RUnlock()
if instanceID != "" {
if instance := m.data[instanceID]; instance != nil {
return foreachAddress(instanceID, instance, fn)
}
return fmt.Errorf("instance does not exist")
}
for instanceID, instance := range m.data {
if err := foreachAddress(instanceID, instance, fn); err != nil {
return err
}
}
return nil
}
// InterfaceIterator is the function called by the ForeachInterface iterator
type InterfaceIterator func(instanceID, interfaceID string, iface InterfaceRevision) error
func foreachInterface(instanceID string, instance *Instance, fn InterfaceIterator) error {
for _, rev := range instance.Interfaces {
if err := fn(instanceID, rev.Resource.InterfaceID(), rev); err != nil {
return err
}
}
return nil
}
// ForeachInterface calls fn for each interface on each interface attached to
// each instance. If an instanceID is specified, the only the interfaces and
// addresses of the specified instance are considered.
//
// The InstanceMap is read-locked throughout the iteration process, i.e., no
// updates will occur. However, the address object given to the InterfaceIterator
// will point to live data and must be deep copied if used outside of the
// context of the iterator function.
func (m *InstanceMap) ForeachInterface(instanceID string, fn InterfaceIterator) error {
m.mutex.RLock()
defer m.mutex.RUnlock()
if instanceID != "" {
if instance := m.data[instanceID]; instance != nil {
return foreachInterface(instanceID, instance, fn)
}
return fmt.Errorf("instance does not exist")
}
for instanceID, instance := range m.data {
if err := foreachInterface(instanceID, instance, fn); err != nil {
return err
}
}
return nil
}
// GetInterface returns returns a particular interface of an instance. The
// boolean indicates whether the interface was found or not.
func (m *InstanceMap) GetInterface(instanceID, interfaceID string) (InterfaceRevision, bool) {
m.mutex.RLock()
defer m.mutex.RUnlock()
if instance := m.data[instanceID]; instance != nil {
if rev, ok := instance.Interfaces[interfaceID]; ok {
return rev, true
}
}
return InterfaceRevision{}, false
}
// DeepCopy returns a deep copy
func (m *InstanceMap) DeepCopy() *InstanceMap {
c := NewInstanceMap()
m.ForeachInterface("", func(instanceID, interfaceID string, rev InterfaceRevision) error {
// c is not exposed yet, we can access it without locking it
c.updateLocked(instanceID, rev)
return nil
})
return c
}
// NumInstances returns the number of instances in the instance map
func (m *InstanceMap) NumInstances() (size int) {
m.mutex.RLock()
size = len(m.data)
m.mutex.RUnlock()
return
}