• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

noironetworks / aci-containers / 10441

28 Feb 2025 04:14AM UTC coverage: 69.039% (-0.08%) from 69.123%
10441

push

travis-pro

web-flow
Merge pull request #1492 from noironetworks/delete-stale-snatlocalinfo

Delete stale snatlocalinfo CRs

16 of 62 new or added lines in 2 files covered. (25.81%)

4 existing lines in 1 file now uncovered.

13279 of 19234 relevant lines covered (69.04%)

0.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.8
/pkg/controller/nodes.go
1
// Copyright 2017 Cisco Systems, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
// Handlers for node updates.
16

17
package controller
18

19
import (
20
        "crypto/rand"
21
        "encoding/json"
22
        "errors"
23
        "fmt"
24
        "net"
25
        "net/http"
26

27
        v1 "k8s.io/api/core/v1"
28
        kubeerr "k8s.io/apimachinery/pkg/api/errors"
29
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
        "k8s.io/apimachinery/pkg/fields"
31
        "k8s.io/apimachinery/pkg/labels"
32
        "k8s.io/client-go/kubernetes"
33
        "k8s.io/client-go/tools/cache"
34

35
        "github.com/sirupsen/logrus"
36

37
        "github.com/noironetworks/aci-containers/pkg/apicapi"
38
        "github.com/noironetworks/aci-containers/pkg/ipam"
39
        "github.com/noironetworks/aci-containers/pkg/metadata"
40
        nodePodIf "github.com/noironetworks/aci-containers/pkg/nodepodif/apis/acipolicy/v1"
41
        "github.com/noironetworks/aci-containers/pkg/util"
42
)
43

44
// Name of the taint to add to nodes that are not ready
45
const (
46
        ACIContainersTaintName string = "aci-containers-host/unavailable"
47
)
48

49
func (cont *AciController) initNodeInformerFromClient(
50
        kubeClient *kubernetes.Clientset) {
×
51
        cont.initNodeInformerBase(
×
52
                cache.NewListWatchFromClient(
×
53
                        kubeClient.CoreV1().RESTClient(), "nodes",
×
54
                        metav1.NamespaceAll, fields.Everything()))
×
55
}
×
56

57
func (cont *AciController) initNodeInformerBase(listWatch *cache.ListWatch) {
1✔
58
        cont.nodeIndexer, cont.nodeInformer = cache.NewIndexerInformer(
1✔
59
                listWatch, &v1.Node{}, 0,
1✔
60
                cache.ResourceEventHandlerFuncs{
1✔
61
                        AddFunc: func(obj interface{}) {
2✔
62
                                cont.syncPodNet(obj) // update cache
1✔
63
                                cont.nodeChanged(obj)
1✔
64
                        },
1✔
65
                        UpdateFunc: func(_ interface{}, obj interface{}) {
1✔
66
                                cont.nodeChanged(obj)
1✔
67
                        },
1✔
68
                        DeleteFunc: func(obj interface{}) {
1✔
69
                                cont.nodeDeleted(obj)
1✔
70
                        },
1✔
71
                },
72
                cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
73
        )
74
}
75

76
func apicNodeNetPol(name string, tenantName string,
77
        nodeIps map[string]bool) apicapi.ApicObject {
1✔
78
        hpp := apicapi.NewHostprotPol(tenantName, name)
1✔
79
        hppDn := hpp.GetDn()
1✔
80
        nodeSubj := apicapi.NewHostprotSubj(hppDn, "local-node")
1✔
81
        if len(nodeIps) > 0 {
2✔
82
                nodeSubjDn := nodeSubj.GetDn()
1✔
83
                outbound := apicapi.NewHostprotRule(nodeSubjDn, "allow-all-egress")
1✔
84
                outbound.SetAttr("direction", "egress")
1✔
85
                outbound.SetAttr("ethertype", "ipv4")
1✔
86
                outbound.SetAttr("connTrack", "normal")
1✔
87

1✔
88
                inbound := apicapi.NewHostprotRule(nodeSubjDn, "allow-all-ingress")
1✔
89
                inbound.SetAttr("direction", "ingress")
1✔
90
                inbound.SetAttr("ethertype", "ipv4")
1✔
91
                inbound.SetAttr("connTrack", "normal")
1✔
92

1✔
93
                for ip := range nodeIps {
2✔
94
                        outbound.AddChild(apicapi.NewHostprotRemoteIp(outbound.GetDn(), ip))
1✔
95
                        inbound.AddChild(apicapi.NewHostprotRemoteIp(inbound.GetDn(), ip))
1✔
96
                }
1✔
97

98
                nodeSubj.AddChild(inbound)
1✔
99
                nodeSubj.AddChild(outbound)
1✔
100
        }
101
        hpp.AddChild(nodeSubj)
1✔
102
        return hpp
1✔
103
}
104

105
func (cont *AciController) createNetPolForNode(node *v1.Node) {
1✔
106
        nodeIps := make(map[string]bool)
1✔
107
        for _, a := range node.Status.Addresses {
2✔
108
                if a.Address != "" &&
1✔
109
                        (a.Type == "InternalIP" || a.Type == "ExternalIP") {
2✔
110
                        nodeIps[a.Address] = true
1✔
111
                }
1✔
112
        }
113

114
        sgName := cont.aciNameForKey("node", node.Name)
1✔
115

1✔
116
        if !cont.config.EnableHppDirect {
2✔
117
                cont.apicConn.WriteApicObjects(sgName,
1✔
118
                        apicapi.ApicSlice{
1✔
119
                                apicNodeNetPol(sgName, cont.config.AciPolicyTenant, nodeIps),
1✔
120
                        })
1✔
121
        } else {
1✔
122
                cont.createNodeHostProtPol(sgName, node.Name, nodeIps)
×
123
        }
×
124
}
125

126
func (cont *AciController) createServiceEndpoint(existing, ep *metadata.ServiceEndpoint, deviceMac, nodeName string) error {
1✔
127
        _, err := net.ParseMAC(deviceMac)
1✔
128
        if err == nil && deviceMac != "00:00:00:00:00:00" {
2✔
129
                ep.Mac = deviceMac
1✔
130
        } else {
2✔
131
                _, err := net.ParseMAC(existing.Mac)
1✔
132
                if err == nil {
2✔
133
                        ep.Mac = existing.Mac
1✔
134
                } else {
2✔
135
                        var mac net.HardwareAddr = make([]byte, 6)
1✔
136
                        _, err := rand.Read(mac)
1✔
137
                        if err != nil {
1✔
138
                                return err
×
139
                        }
×
140

141
                        mac[0] = (mac[0] & 254) | 2
1✔
142
                        ep.Mac = mac.String()
1✔
143
                }
144
        }
145

146
        if ep.Ipv4 == nil && existing.Ipv4 != nil &&
1✔
147
                cont.nodeServiceIps.V4.RemoveIp(existing.Ipv4) {
2✔
148
                ep.Ipv4 = existing.Ipv4
1✔
149
        }
1✔
150

151
        if ep.Ipv4 == nil {
2✔
152
                ipv4, err := cont.nodeServiceIps.V4.GetIp()
1✔
153
                if err == nil {
2✔
154
                        ep.Ipv4 = ipv4
1✔
155
                } else {
2✔
156
                        ep.Ipv4 = nil
1✔
157
                }
1✔
158
        }
159

160
        if ep.Ipv6 == nil && existing.Ipv6 != nil &&
1✔
161
                cont.nodeServiceIps.V6.RemoveIp(existing.Ipv6) {
1✔
162
                ep.Ipv6 = existing.Ipv6
×
163
        }
×
164

165
        if ep.Ipv6 == nil {
2✔
166
                ipv6, err := cont.nodeServiceIps.V6.GetIp()
1✔
167
                if err == nil {
2✔
168
                        ep.Ipv6 = ipv6
1✔
169
                } else {
2✔
170
                        ep.Ipv6 = nil
1✔
171
                }
1✔
172
        }
173

174
        if ep.Ipv4 == nil && ep.Ipv6 == nil {
2✔
175
                return errors.New("No IP addresses available for service endpoint")
1✔
176
        }
1✔
177

178
        if (ep.HealthGroupDn == "") && (cont.config.AciServiceMonitorInterval > 0) {
2✔
179
                name := cont.aciNameForKey("svc", nodeName)
1✔
180
                healthGroupObj := apicapi.NewVnsRedirectHealthGroup(cont.config.AciVrfTenant, name)
1✔
181
                ep.HealthGroupDn = healthGroupObj.GetDn()
1✔
182
                cont.apicConn.WriteApicObjects(name, apicapi.ApicSlice{healthGroupObj})
1✔
183
        }
1✔
184

185
        return nil
1✔
186
}
187

188
func (cont *AciController) nodeFullSync() {
1✔
189
        cache.ListAll(cont.nodeIndexer, labels.Everything(),
1✔
190
                func(nodeobj interface{}) {
2✔
191
                        cont.syncPodNet(nodeobj) // update the cache
1✔
192
                        cont.nodeChanged(nodeobj)
1✔
193
                })
1✔
194
}
195

196
// syncPodNet syncs in the podnets from the node object's annotation
197
func (cont *AciController) syncPodNet(obj interface{}) {
1✔
198
        cont.indexMutex.Lock()
1✔
199
        defer cont.indexMutex.Unlock()
1✔
200

1✔
201
        node := obj.(*v1.Node)
1✔
202
        logger := cont.log.WithFields(logrus.Fields{
1✔
203
                "Node": node.ObjectMeta.Name,
1✔
204
        })
1✔
205

1✔
206
        if node.ObjectMeta.Annotations == nil {
2✔
207
                return // nothing to sync
1✔
208
        }
1✔
209

210
        netval, ok := node.ObjectMeta.Annotations[metadata.PodNetworkRangeAnnotation]
1✔
211
        if !ok {
2✔
212
                return // nothing to sync
1✔
213
        }
1✔
214

215
        nodePodNet := newNodePodNetMeta()
1✔
216
        cont.mergePodNet(nodePodNet, netval, logger)
1✔
217
        cont.nodePodNetCache[node.ObjectMeta.Name] = nodePodNet
1✔
218
}
219

220
func (cont *AciController) writeApicNode(node *v1.Node) {
1✔
221
        if cont.config.ChainedMode {
1✔
222
                return
×
223
        }
×
224
        tunnelID := 0
1✔
225
        key := cont.aciNameForKey("node-vmm", node.Name)
1✔
226
        aobj := apicapi.NewVmmInjectedHost(cont.vmmDomainProvider(),
1✔
227
                cont.config.AciVmmDomain, cont.config.AciVmmController,
1✔
228
                node.Name)
1✔
229
        aobj.SetAttr("mgmtIp", getNodeIP(node, v1.NodeInternalIP))
1✔
230
        aobj.SetAttr("os", node.Status.NodeInfo.OSImage)
1✔
231
        aobj.SetAttr("kernelVer", node.Status.NodeInfo.KernelVersion)
1✔
232
        if apicapi.ApicVersion >= "5.0" {
2✔
233
                aobj.SetAttr("id", fmt.Sprintf("%v", tunnelID))
1✔
234
        }
1✔
235
        cont.apicConn.WriteApicObjects(key, apicapi.ApicSlice{aobj})
1✔
236
}
237

238
func getNodeIP(node *v1.Node, aType v1.NodeAddressType) string {
1✔
239
        for _, a := range node.Status.Addresses {
2✔
240
                if a.Type == aType {
2✔
241
                        return a.Address
1✔
242
                }
1✔
243
        }
244

245
        return ""
1✔
246
}
247

248
func (cont *AciController) nodeChangedByName(nodeName string) {
1✔
249
        node, exists, err := cont.nodeIndexer.GetByKey(nodeName)
1✔
250
        if err != nil {
1✔
251
                cont.log.Error("Could not lookup node: ", err)
×
252
                return
×
253
        }
×
254
        if exists && node != nil {
2✔
255
                cont.nodeChanged(node)
1✔
256
        }
1✔
257
}
258

259
func (cont *AciController) nodeChanged(obj interface{}) {
1✔
260
        if cont.config.ChainedMode {
1✔
261
                return
×
262
        }
×
263
        cont.indexMutex.Lock()
1✔
264

1✔
265
        node := obj.(*v1.Node)
1✔
266
        logger := cont.log.WithFields(logrus.Fields{
1✔
267
                "Node": node.ObjectMeta.Name,
1✔
268
        })
1✔
269

1✔
270
        nodeUpdated := false
1✔
271

1✔
272
        if cont.config.TaintNotReadyNode {
1✔
273
                if !isNodeReady(node) {
×
274
                        logger.Debug("Node is NotReady, adding taint")
×
275
                        nodeUpdated = addTaintIfNotPresent(node)
×
276
                }
×
277
        }
278

279
        if node.ObjectMeta.Annotations == nil {
2✔
280
                node.ObjectMeta.Annotations = make(map[string]string)
1✔
281
        }
1✔
282

283
        nodeMeta, metaok := cont.nodeServiceMetaCache[node.ObjectMeta.Name]
1✔
284
        epval, epok := node.ObjectMeta.Annotations[metadata.ServiceEpAnnotation]
1✔
285
        deviceMac, hasDevice := cont.deviceMacForNode(node.ObjectMeta.Name)
1✔
286

1✔
287
        if cont.nodeSyncEnabled && hasDevice {
2✔
288
                if !metaok {
2✔
289
                        nodeMeta = &nodeServiceMeta{}
1✔
290
                        cont.nodeServiceMetaCache[node.ObjectMeta.Name] = nodeMeta
1✔
291
                }
1✔
292

293
                existing := &metadata.ServiceEndpoint{}
1✔
294
                if epok {
2✔
295
                        err := json.Unmarshal([]byte(epval), existing)
1✔
296
                        if err != nil {
1✔
297
                                logger.WithFields(logrus.Fields{
×
298
                                        "epval": epval,
×
299
                                }).Warn("Could not parse existing node ",
×
300
                                        "service endpoint annotation: ", err)
×
301
                        }
×
302
                }
303

304
                cont.createServiceEndpoint(existing, &nodeMeta.serviceEp, deviceMac, node.ObjectMeta.Name)
1✔
305
                raw, err := json.Marshal(&nodeMeta.serviceEp)
1✔
306
                if err != nil {
1✔
307
                        logger.Error("Could not create node service endpoint annotation", err)
×
308
                } else {
1✔
309
                        serviceEpAnnotation := string(raw)
1✔
310
                        if !epok || serviceEpAnnotation != epval {
2✔
311
                                node.ObjectMeta.Annotations[metadata.ServiceEpAnnotation] =
1✔
312
                                        serviceEpAnnotation
1✔
313
                                nodeUpdated = true
1✔
314
                                cont.updateServicesForNode(node.ObjectMeta.Name)
1✔
315
                                cont.snatFullSync()
1✔
316
                        }
1✔
317
                }
318
        }
319

320
        nodePodNet, ok := cont.nodePodNetCache[node.ObjectMeta.Name]
1✔
321
        if !ok {
2✔
322
                nodePodNet = newNodePodNetMeta()
1✔
323
                cont.nodePodNetCache[node.ObjectMeta.Name] = nodePodNet
1✔
324
        }
1✔
325

326
        netval := node.ObjectMeta.Annotations[metadata.PodNetworkRangeAnnotation]
1✔
327
        if cont.nodeSyncEnabled {
2✔
328
                cont.checkNodePodNet(node.ObjectMeta.Name)
1✔
329
                if netval != nodePodNet.podNetIpsAnnotation {
2✔
330
                        logger.Debug("Overwriting existing pod network: ", netval)
1✔
331
                        node.ObjectMeta.Annotations[metadata.PodNetworkRangeAnnotation] =
1✔
332
                                nodePodNet.podNetIpsAnnotation
1✔
333
                        nodeUpdated = true
1✔
334
                }
1✔
335
        }
336

337
        if cont.config.EnableOpflexAgentReconnect {
2✔
338
                nodeAciPodAnnotation, ok := cont.nodeACIPodAnnot[node.ObjectMeta.Name]
1✔
339
                if ok {
2✔
340
                        nodeAciPod := nodeAciPodAnnotation.aciPod
1✔
341
                        aciPodAnn := node.ObjectMeta.Annotations[metadata.NodeAciPodAnnotation]
1✔
342
                        if cont.nodeSyncEnabled {
2✔
343
                                if aciPodAnn != nodeAciPod && nodeAciPod != "" {
2✔
344
                                        node.ObjectMeta.Annotations[metadata.NodeAciPodAnnotation] = nodeAciPod
1✔
345
                                        logger.Info("ACI pod annotation on node ", node.ObjectMeta.Name, "changed from ", aciPodAnn, " to ", nodeAciPod)
1✔
346
                                        nodeUpdated = true
1✔
347
                                }
1✔
348
                        }
349
                } else {
1✔
350
                        var annot aciPodAnnot
1✔
351
                        cont.nodeACIPodAnnot[node.ObjectMeta.Name] = annot
1✔
352
                }
1✔
353
        }
354

355
        if cont.config.AciMultipod {
2✔
356
                nodeAciPodAnnot, ok := cont.nodeACIPod[node.ObjectMeta.Name]
1✔
357
                if ok {
2✔
358
                        nodeAciPod := nodeAciPodAnnot.aciPod
1✔
359
                        aciPodAnn := node.ObjectMeta.Annotations[metadata.AciPodAnnotation]
1✔
360
                        if cont.nodeSyncEnabled {
2✔
361
                                if aciPodAnn != nodeAciPod && nodeAciPod != "" {
1✔
362
                                        node.ObjectMeta.Annotations[metadata.AciPodAnnotation] = nodeAciPod
×
363
                                        logger.Info("ACI pod annotation for multipod on node ", node.ObjectMeta.Name, "changed from ", aciPodAnn, " to ", nodeAciPod)
×
364
                                        nodeUpdated = true
×
365
                                }
×
366
                        }
367
                } else {
1✔
368
                        var annot aciPodAnnot
1✔
369
                        cont.nodeACIPod[node.ObjectMeta.Name] = annot
1✔
370
                }
1✔
371
        }
372
        cont.indexMutex.Unlock()
1✔
373

1✔
374
        cont.createNetPolForNode(node)
1✔
375
        cont.writeApicNode(node)
1✔
376

1✔
377
        if nodeUpdated {
2✔
378
                _, err := cont.updateNode(node)
1✔
379
                if err != nil {
1✔
380
                        var serr *kubeerr.StatusError
×
381
                        if errors.As(err, &serr) {
×
382
                                if serr.ErrStatus.Code == http.StatusConflict {
×
383
                                        logger.Debug("Conflict updating node; ",
×
384
                                                "will retry on next update")
×
385
                                        return
×
386
                                }
×
387
                        }
388
                        logger.Error("Failed to update node: ", err)
×
389
                } else {
1✔
390
                        logger.WithFields(logrus.Fields{
1✔
391
                                "ServiceEpAnnotation": node.
1✔
392
                                        ObjectMeta.Annotations[metadata.ServiceEpAnnotation],
1✔
393
                                "PodNetworkRangeAnnotation": node.
1✔
394
                                        ObjectMeta.Annotations[metadata.PodNetworkRangeAnnotation],
1✔
395
                        }).Info("Updated node annotations")
1✔
396
                }
1✔
397
        }
398
}
399

400
func (cont *AciController) nodeDeleted(obj interface{}) {
1✔
401
        if cont.config.ChainedMode {
1✔
402
                return
×
403
        }
×
404
        node, isNode := obj.(*v1.Node)
1✔
405
        if !isNode {
2✔
406
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
1✔
407
                if !ok {
2✔
408
                        cont.log.Error("Received unexpected object: ", obj)
1✔
409
                        return
1✔
410
                }
1✔
411
                node, ok = deletedState.Obj.(*v1.Node)
1✔
412
                if !ok {
2✔
413
                        cont.log.Error("DeletedFinalStateUnknown contained non-Node object: ", deletedState.Obj)
1✔
414
                        return
1✔
415
                }
1✔
416
        }
417
        cont.apicConn.ClearApicObjects(cont.aciNameForKey("node", node.Name))
1✔
418
        cont.apicConn.ClearApicObjects(cont.aciNameForKey("node-vmm", node.Name))
1✔
419
        cont.log.Infof("Node deleted: %s", node.ObjectMeta.Name)
1✔
420

1✔
421
        cont.indexMutex.Lock()
1✔
422
        defer cont.indexMutex.Unlock()
1✔
423

1✔
424
        if existing, ok := cont.nodeServiceMetaCache[node.ObjectMeta.Name]; ok {
2✔
425
                if existing.serviceEp.Ipv4 != nil {
2✔
426
                        cont.nodeServiceIps.V4.AddIp(existing.serviceEp.Ipv4)
1✔
427
                }
1✔
428
                if existing.serviceEp.Ipv6 != nil {
1✔
429
                        cont.nodeServiceIps.V6.AddIp(existing.serviceEp.Ipv6)
×
430
                }
×
431
        }
432
        delete(cont.nodeServiceMetaCache, node.ObjectMeta.Name)
1✔
433
        cont.updateServicesForNode(node.ObjectMeta.Name)
1✔
434
        cont.snatFullSync()
1✔
435
        if _, ok := cont.snatNodeInfoCache[node.ObjectMeta.Name]; ok {
1✔
NEW
436
                cont.log.Info("Deleting stale snat resources of node: ", node.ObjectMeta.Name)
×
NEW
437
                err := cont.deleteStaleSnatResources(node.ObjectMeta.Name, true, true)
×
NEW
438
                if err != nil {
×
NEW
439
                        cont.log.Error("Failed to delete stale snat resources for node: ", node.ObjectMeta.Name)
×
NEW
440
                        return
×
441
                }
×
442
                nodeinfo := cont.snatNodeInfoCache[node.ObjectMeta.Name]
×
443
                delete(cont.snatNodeInfoCache, node.ObjectMeta.Name)
×
444
                cont.log.Debug("Node deleted from snatNodeInfoCache: ", node.ObjectMeta.Name)
×
445
                nodeinfokey, _ := cache.MetaNamespaceKeyFunc(nodeinfo)
×
446
                cont.queueNodeInfoUpdateByKey(nodeinfokey)
×
447
        }
448

449
        if podnet, ok := cont.nodePodNetCache[node.ObjectMeta.Name]; ok {
2✔
450
                if podnet != nil && cont.podNetworkIps != nil {
2✔
451
                        if cont.podNetworkIps.V4 != nil && podnet.podNetIps.V4 != nil {
1✔
452
                                cont.podNetworkIps.V4.AddRanges(podnet.podNetIps.V4)
×
453
                        }
×
454
                        if cont.podNetworkIps.V4 != nil && podnet.podNetIps.V6 != nil {
1✔
455
                                cont.podNetworkIps.V6.AddRanges(podnet.podNetIps.V6)
×
456
                        }
×
457
                }
458
                delete(cont.nodePodNetCache, node.ObjectMeta.Name)
1✔
459
                cont.log.Debug("Node deleted from nodePodNetCache: ", node.ObjectMeta.Name)
1✔
460
        }
461

462
        if cont.config.EnableOpflexAgentReconnect {
1✔
463
                delete(cont.nodeACIPodAnnot, node.ObjectMeta.Name)
×
464
        }
×
465
        if cont.config.AciMultipod {
1✔
466
                delete(cont.nodeACIPod, node.ObjectMeta.Name)
×
467
        }
×
468

469
        np, ok := obj.(*nodePodIf.NodePodIF)
1✔
470
        if !ok {
2✔
471
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
1✔
472
                if !ok {
2✔
473
                        cont.log.Error("Received unexpected object: ", obj)
1✔
474
                        return
1✔
475
                }
1✔
476
                np, ok = deletedState.Obj.(*nodePodIf.NodePodIF)
×
477
                if !ok {
×
478
                        cont.log.Error("DeletedFinalStateUnknown contained non-NodePodIF object: ", deletedState.Obj)
×
479
                        return
×
480
                }
×
481
        }
482
        env := cont.env.(*K8sEnvironment)
×
483
        nodepodifc1 := env.nodePodifClient
×
484
        if nodepodifc1 != nil {
×
485
                err := util.DeleteNodePodIfCR(*nodepodifc1, np.ObjectMeta.Name)
×
486
                if err != nil {
×
487
                        cont.log.Error("Could not delete the NodePodIF", np.ObjectMeta.Name)
×
488
                        return
×
489
                }
×
490
        }
491
}
492

493
// must have index lock
494
func (cont *AciController) addPodToNode(nodename, key string) {
1✔
495
        existing, ok := cont.nodePodNetCache[nodename]
1✔
496
        if !ok {
2✔
497
                existing = newNodePodNetMeta()
1✔
498
                cont.nodePodNetCache[nodename] = existing
1✔
499
        }
1✔
500
        if _, ok = existing.nodePods[key]; !ok {
2✔
501
                existing.nodePods[key] = true
1✔
502
                cont.checkNodePodNet(nodename)
1✔
503
        }
1✔
504
}
505

506
// must have index lock
507
func (cont *AciController) removePodFromNode(nodename, key string) {
×
508
        if existing, ok := cont.nodePodNetCache[nodename]; ok {
×
509
                delete(existing.nodePods, key)
×
510
                cont.checkNodePodNet(nodename)
×
511
        }
×
512
}
513

514
func (cont *AciController) recomputePodNetAnnotation(podnet *nodePodNetMeta) {
1✔
515
        raw, err := json.Marshal(&podnet.podNetIps)
1✔
516
        if err != nil {
1✔
517
                cont.log.Error("Could not create node pod network ",
×
518
                        "annotation", err)
×
519
        }
×
520
        podnet.podNetIpsAnnotation = string(raw)
1✔
521
}
522

523
// must have index lock
524
func (cont *AciController) mergePodNet(podnet *nodePodNetMeta, existingAnnotation string, logger *logrus.Entry) {
1✔
525
        existing := &metadata.NetIps{}
1✔
526
        err := json.Unmarshal([]byte(existingAnnotation), existing)
1✔
527
        if err != nil {
1✔
528
                cont.log.Error("Could not parse existing pod network ",
×
529
                        "annotation", err)
×
530
                return
×
531
        }
×
532

533
        logger.Debug("Merging existing pod network: ", existingAnnotation)
1✔
534

1✔
535
        {
2✔
536
                v4 := ipam.NewFromRanges(podnet.podNetIps.V4)
1✔
537
                v4.AddRanges(existing.V4)
1✔
538
                annSize := v4.GetSize()
1✔
539

1✔
540
                // validate existing against configured range
1✔
541
                v4 = v4.Intersect(cont.configuredPodNetworkIps.V4)
1✔
542
                if v4.GetSize() != annSize {
2✔
543
                        logger.Warnf("intersect: %+v, config: %+v", v4, cont.configuredPodNetworkIps.V4)
1✔
544
                        logger.Warn("Existing annotation outside configured",
1✔
545
                                "range", existingAnnotation)
1✔
546
                }
1✔
547

548
                // mark the existing as allocated
549
                prevSize := cont.podNetworkIps.V4.GetSize()
1✔
550
                cont.podNetworkIps.V4.RemoveRanges(existing.V4)
1✔
551

1✔
552
                // verify allocation was successful
1✔
553
                newSize := cont.podNetworkIps.V4.GetSize()
1✔
554
                if (newSize + annSize) != prevSize {
2✔
555
                        logger.Warn("Existing annotation failed allocation: ",
1✔
556
                                existingAnnotation)
1✔
557
                }
1✔
558

559
                if len(v4.FreeList) > 0 {
2✔
560
                        podnet.podNetIps.V4 = v4.FreeList
1✔
561
                } else {
1✔
562
                        podnet.podNetIps.V4 = nil
×
563
                }
×
564
        }
565

566
        {
1✔
567
                v6 := ipam.NewFromRanges(podnet.podNetIps.V6)
1✔
568
                v6.AddRanges(existing.V6)
1✔
569
                v6 = v6.Intersect(cont.configuredPodNetworkIps.V6)
1✔
570
                cont.podNetworkIps.V6.RemoveRanges(existing.V6)
1✔
571
                if len(v6.FreeList) > 0 {
1✔
572
                        podnet.podNetIps.V6 = v6.FreeList
×
573
                } else {
1✔
574
                        podnet.podNetIps.V6 = nil
1✔
575
                }
1✔
576
        }
577

578
        cont.recomputePodNetAnnotation(podnet)
1✔
579
}
580

581
func (cont *AciController) allocateIpChunk(podnet *nodePodNetMeta, v4 bool) bool {
1✔
582
        var podnetipam, ipa *ipam.IpAlloc
1✔
583
        changed := false
1✔
584
        if v4 {
2✔
585
                podnetipam = ipam.NewFromRanges(podnet.podNetIps.V4)
1✔
586
                ipa = cont.podNetworkIps.V4
1✔
587
        } else {
2✔
588
                podnetipam = ipam.NewFromRanges(podnet.podNetIps.V6)
1✔
589
                ipa = cont.podNetworkIps.V6
1✔
590
        }
1✔
591
        size := podnetipam.GetSize()
1✔
592
        if int64(len(podnet.nodePods)) >
1✔
593
                size-int64(cont.config.PodIpPoolChunkSize)/2 {
2✔
594
                // we have half a chunk left or less; allocate a new chunk
1✔
595
                r, err := ipa.GetIpChunk(int64(cont.config.PodIpPoolChunkSize))
1✔
596
                if err != nil {
2✔
597
                        cont.log.Error("Could not allocate address chunk: ", err)
1✔
598
                } else {
2✔
599
                        podnetipam.AddRanges(r)
1✔
600
                        if v4 {
2✔
601
                                podnet.podNetIps.V4 = podnetipam.FreeList
1✔
602
                        } else {
2✔
603
                                podnet.podNetIps.V6 = podnetipam.FreeList
1✔
604
                        }
1✔
605
                        cont.recomputePodNetAnnotation(podnet)
1✔
606
                        changed = true
1✔
607
                }
608
        }
609
        return changed
1✔
610
}
611

612
// must have index lock
613
func (cont *AciController) checkNodePodNet(nodename string) {
1✔
614
        v4changed, v6changed := false, false
1✔
615
        if podnet, ok := cont.nodePodNetCache[nodename]; ok {
2✔
616
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
617
                        v4changed = cont.allocateIpChunk(podnet, true)
1✔
618
                }
1✔
619
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
620
                        v6changed = cont.allocateIpChunk(podnet, false)
1✔
621
                }
1✔
622
        }
623
        if v4changed || v6changed {
2✔
624
                go cont.env.NodePodNetworkChanged(nodename)
1✔
625
        }
1✔
626
}
627

628
func isNodeReady(node *v1.Node) bool {
×
629
        for _, condition := range node.Status.Conditions {
×
630
                if condition.Type == v1.NodeReady {
×
631
                        return condition.Status == v1.ConditionTrue
×
632
                }
×
633
        }
634
        return false
×
635
}
636

637
func addTaintIfNotPresent(node *v1.Node) bool {
×
638
        for _, taint := range node.Spec.Taints {
×
639
                if taint.Key == ACIContainersTaintName && taint.Effect == v1.TaintEffectNoSchedule {
×
640
                        return false
×
641
                }
×
642
        }
643

644
        node.Spec.Taints = append(node.Spec.Taints, v1.Taint{
×
645
                Key:    ACIContainersTaintName,
×
646
                Effect: v1.TaintEffectNoSchedule,
×
647
        })
×
648

×
649
        return true
×
650
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc