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

kubeovn / kube-ovn / 16336706178

17 Jul 2025 05:07AM UTC coverage: 21.439% (-0.02%) from 21.454%
16336706178

push

github

web-flow
feat(endpoint_slice): refactor and support arbitrary providers (#5414)

Signed-off-by: SkalaNetworks <contact@skala.network>

17 of 152 new or added lines in 2 files covered. (11.18%)

9 existing lines in 3 files now uncovered.

10542 of 49172 relevant lines covered (21.44%)

0.25 hits per line

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

8.2
/pkg/controller/endpoint_slice.go
1
package controller
2

3
import (
4
        "context"
5
        "fmt"
6
        "slices"
7
        "strings"
8
        "time"
9

10
        v1 "k8s.io/api/core/v1"
11
        discoveryv1 "k8s.io/api/discovery/v1"
12
        "k8s.io/apimachinery/pkg/api/errors"
13
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14
        "k8s.io/apimachinery/pkg/labels"
15
        utilruntime "k8s.io/apimachinery/pkg/util/runtime"
16
        "k8s.io/client-go/tools/cache"
17
        "k8s.io/klog/v2"
18

19
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
20
        "github.com/kubeovn/kube-ovn/pkg/ovs"
21
        "github.com/kubeovn/kube-ovn/pkg/util"
22
)
23

24
func findServiceKey(endpointSlice *discoveryv1.EndpointSlice) string {
1✔
25
        if endpointSlice != nil && endpointSlice.Labels != nil && endpointSlice.Labels[discoveryv1.LabelServiceName] != "" {
2✔
26
                return endpointSlice.Namespace + "/" + endpointSlice.Labels[discoveryv1.LabelServiceName]
1✔
27
        }
1✔
28
        return ""
1✔
29
}
30

31
func (c *Controller) enqueueAddEndpointSlice(obj any) {
×
32
        key := findServiceKey(obj.(*discoveryv1.EndpointSlice))
×
33
        if key != "" {
×
34
                klog.V(3).Infof("enqueue add endpointSlice %s", key)
×
35
                c.addOrUpdateEndpointSliceQueue.Add(key)
×
36
        }
×
37
}
38

39
func (c *Controller) enqueueUpdateEndpointSlice(oldObj, newObj any) {
×
40
        oldEndpointSlice := oldObj.(*discoveryv1.EndpointSlice)
×
41
        newEndpointSlice := newObj.(*discoveryv1.EndpointSlice)
×
42
        if oldEndpointSlice.ResourceVersion == newEndpointSlice.ResourceVersion {
×
43
                return
×
44
        }
×
45

46
        if len(oldEndpointSlice.Endpoints) == 0 && len(newEndpointSlice.Endpoints) == 0 {
×
47
                return
×
48
        }
×
49

50
        key := findServiceKey(newEndpointSlice)
×
51
        if key != "" {
×
52
                klog.V(3).Infof("enqueue update endpointSlice for service %s", key)
×
53
                c.addOrUpdateEndpointSliceQueue.Add(key)
×
54
        }
×
55
}
56

57
func (c *Controller) handleUpdateEndpointSlice(key string) error {
×
58
        namespace, name, err := cache.SplitMetaNamespaceKey(key)
×
59
        if err != nil {
×
60
                utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key))
×
61
                return nil
×
62
        }
×
63

64
        c.epKeyMutex.LockKey(key)
×
65
        defer func() { _ = c.epKeyMutex.UnlockKey(key) }()
×
66
        klog.Infof("handle update endpointSlice for service %s", key)
×
67

×
68
        endpointSlices, err := c.endpointSlicesLister.EndpointSlices(namespace).List(labels.Set{discoveryv1.LabelServiceName: name}.AsSelector())
×
69
        if err != nil {
×
70
                if errors.IsNotFound(err) {
×
71
                        return nil
×
72
                }
×
73
                klog.Error(err)
×
74
                return err
×
75
        }
76

77
        cachedService, err := c.servicesLister.Services(namespace).Get(name)
×
78
        if err != nil {
×
79
                if errors.IsNotFound(err) {
×
80
                        return nil
×
81
                }
×
82
                klog.Error(err)
×
83
                return err
×
84
        }
85
        svc := cachedService.DeepCopy()
×
86

×
87
        var (
×
88
                lbVips                   []string
×
89
                vip, vpcName, subnetName string
×
90
                ok                       bool
×
91
                ignoreHealthCheck        = true
×
92
                isPreferLocalBackend     = false
×
93
        )
×
94

×
95
        if vip, ok = svc.Annotations[util.SwitchLBRuleVipsAnnotation]; ok {
×
96
                lbVips = []string{vip}
×
97

×
98
                for _, endpointSlice := range endpointSlices {
×
99
                        for _, endpoint := range endpointSlice.Endpoints {
×
100
                                if util.CheckProtocol(vip) == kubeovnv1.ProtocolIPv4 &&
×
101
                                        endpoint.TargetRef.Name != "" {
×
102
                                        ignoreHealthCheck = false
×
103
                                }
×
104
                        }
105
                }
106
        } else if lbVips = util.ServiceClusterIPs(*svc); len(lbVips) == 0 {
×
107
                return nil
×
108
        }
×
109

110
        if c.config.EnableLb && c.config.EnableOVNLBPreferLocal {
×
111
                if svc.Spec.Type == v1.ServiceTypeLoadBalancer && svc.Spec.ExternalTrafficPolicy == v1.ServiceExternalTrafficPolicyTypeLocal {
×
112
                        if len(svc.Status.LoadBalancer.Ingress) > 0 {
×
113
                                for _, ingress := range svc.Status.LoadBalancer.Ingress {
×
114
                                        if ingress.IP != "" {
×
115
                                                lbVips = append(lbVips, ingress.IP)
×
116
                                        }
×
117
                                }
118
                        }
119
                        isPreferLocalBackend = true
×
120
                } else if svc.Spec.Type == v1.ServiceTypeClusterIP && svc.Spec.InternalTrafficPolicy != nil && *svc.Spec.InternalTrafficPolicy == v1.ServiceInternalTrafficPolicyLocal {
×
121
                        isPreferLocalBackend = true
×
122
                }
×
123
        }
124

NEW
125
        vpcName, subnetName, err = c.getVpcAndSubnetForEndpoints(endpointSlices, svc)
×
NEW
126
        if err != nil {
×
127
                return err
×
128
        }
×
129

130
        var (
×
131
                vpc    *kubeovnv1.Vpc
×
132
                svcVpc string
×
133
        )
×
134

×
135
        if vpc, err = c.vpcsLister.Get(vpcName); err != nil {
×
136
                klog.Errorf("failed to get vpc %s, %v", vpcName, err)
×
137
                return err
×
138
        }
×
139

140
        tcpLb, udpLb, sctpLb := vpc.Status.TCPLoadBalancer, vpc.Status.UDPLoadBalancer, vpc.Status.SctpLoadBalancer
×
141
        oldTCPLb, oldUDPLb, oldSctpLb := vpc.Status.TCPSessionLoadBalancer, vpc.Status.UDPSessionLoadBalancer, vpc.Status.SctpSessionLoadBalancer
×
142
        if svc.Spec.SessionAffinity == v1.ServiceAffinityClientIP {
×
143
                tcpLb, udpLb, sctpLb, oldTCPLb, oldUDPLb, oldSctpLb = oldTCPLb, oldUDPLb, oldSctpLb, tcpLb, udpLb, sctpLb
×
144
        }
×
145

146
        for _, lbVip := range lbVips {
×
147
                for _, port := range svc.Spec.Ports {
×
148
                        var lb, oldLb string
×
149
                        switch port.Protocol {
×
150
                        case v1.ProtocolTCP:
×
151
                                lb, oldLb = tcpLb, oldTCPLb
×
152
                        case v1.ProtocolUDP:
×
153
                                lb, oldLb = udpLb, oldUDPLb
×
154
                        case v1.ProtocolSCTP:
×
155
                                lb, oldLb = sctpLb, oldSctpLb
×
156
                        }
157

158
                        var (
×
159
                                vip, checkIP             string
×
160
                                backends                 []string
×
161
                                ipPortMapping, externals map[string]string
×
162
                        )
×
163

×
164
                        if !ignoreHealthCheck {
×
165
                                if checkIP, err = c.getHealthCheckVip(subnetName, lbVip); err != nil {
×
166
                                        klog.Error(err)
×
167
                                        return err
×
168
                                }
×
169
                                externals = map[string]string{
×
170
                                        util.SwitchLBRuleSubnet: subnetName,
×
171
                                }
×
172
                        }
173

174
                        if isPreferLocalBackend {
×
175
                                // only use the ipportmapping's lsp to ip map when the backend is local
×
176
                                checkIP = util.MasqueradeCheckIP
×
177
                        }
×
178
                        isGenIPPortMapping := !ignoreHealthCheck || isPreferLocalBackend
×
179
                        ipPortMapping, backends = c.getIPPortMappingBackend(endpointSlices, port, lbVip, checkIP, isGenIPPortMapping)
×
180
                        // for performance reason delete lb with no backends
×
181
                        if len(backends) != 0 {
×
182
                                vip = util.JoinHostPort(lbVip, port.Port)
×
183
                                klog.Infof("add vip endpoint %s, backends %v to LB %s", vip, backends, lb)
×
184
                                if err = c.OVNNbClient.LoadBalancerAddVip(lb, vip, backends...); err != nil {
×
185
                                        klog.Errorf("failed to add vip %s with backends %s to LB %s: %v", lbVip, backends, lb, err)
×
186
                                        return err
×
187
                                }
×
188

189
                                if isPreferLocalBackend && len(ipPortMapping) != 0 {
×
190
                                        if err = c.OVNNbClient.LoadBalancerUpdateIPPortMapping(lb, vip, ipPortMapping); err != nil {
×
191
                                                klog.Errorf("failed to update ip port mapping %s for vip %s to LB %s: %v", ipPortMapping, vip, lb, err)
×
192
                                                return err
×
193
                                        }
×
194
                                }
195

196
                                if !ignoreHealthCheck && len(ipPortMapping) != 0 {
×
197
                                        klog.Infof("add health check ip port mapping %v to LB %s", ipPortMapping, lb)
×
198
                                        if err = c.OVNNbClient.LoadBalancerAddHealthCheck(lb, vip, ignoreHealthCheck, ipPortMapping, externals); err != nil {
×
199
                                                klog.Errorf("failed to add health check for vip %s with ip port mapping %s to LB %s: %v", lbVip, ipPortMapping, lb, err)
×
200
                                                return err
×
201
                                        }
×
202
                                }
203
                        } else {
×
204
                                vip = util.JoinHostPort(lbVip, port.Port)
×
205
                                klog.V(3).Infof("delete vip endpoint %s from LB %s", vip, lb)
×
206
                                if err = c.OVNNbClient.LoadBalancerDeleteVip(lb, vip, true); err != nil {
×
207
                                        klog.Errorf("failed to delete vip endpoint %s from LB %s: %v", vip, lb, err)
×
208
                                        return err
×
209
                                }
×
210

211
                                klog.V(3).Infof("delete vip endpoint %s from old LB %s", vip, oldLb)
×
212
                                if err = c.OVNNbClient.LoadBalancerDeleteVip(oldLb, vip, true); err != nil {
×
213
                                        klog.Errorf("failed to delete vip %s from LB %s: %v", vip, oldLb, err)
×
214
                                        return err
×
215
                                }
×
216

217
                                if c.config.EnableOVNLBPreferLocal {
×
218
                                        if err := c.OVNNbClient.LoadBalancerDeleteIPPortMapping(lb, vip); err != nil {
×
219
                                                klog.Errorf("failed to delete ip port mapping for vip %s from LB %s: %v", vip, lb, err)
×
220
                                                return err
×
221
                                        }
×
222
                                        if err := c.OVNNbClient.LoadBalancerDeleteIPPortMapping(oldLb, vip); err != nil {
×
223
                                                klog.Errorf("failed to delete ip port mapping for vip %s from LB %s: %v", vip, lb, err)
×
224
                                                return err
×
225
                                        }
×
226
                                }
227
                        }
228
                }
229
        }
230

231
        if svcVpc = svc.Annotations[util.VpcAnnotation]; svcVpc != vpcName {
×
232
                patch := util.KVPatch{util.VpcAnnotation: vpcName}
×
233
                if err = util.PatchAnnotations(c.config.KubeClient.CoreV1().Services(namespace), svc.Name, patch); err != nil {
×
234
                        klog.Errorf("failed to patch service %s: %v", key, err)
×
235
                        return err
×
236
                }
×
237
        }
238

239
        return nil
×
240
}
241

242
// serviceHasSelector returns if a service has selectors
243
func serviceHasSelector(service *v1.Service) bool {
1✔
244
        return len(service.Spec.Selector) > 0
1✔
245
}
1✔
246

247
// getVpcAndSubnetForEndpoints returns the name of the VPC/Subnet for EndpointSlices
NEW
248
func (c *Controller) getVpcAndSubnetForEndpoints(endpointSlices []*discoveryv1.EndpointSlice, service *v1.Service) (vpcName, subnetName string, err error) {
×
NEW
249
        // Let the user self-determine what VPC and subnet to use if they provided annotations
×
NEW
250
        if service.Annotations != nil {
×
NEW
251
                if vpc := service.Annotations[util.LogicalRouterAnnotation]; vpc != "" {
×
NEW
252
                        vpcName = vpc
×
253
                }
×
NEW
254
                if subnet := service.Annotations[util.LogicalSwitchAnnotation]; subnet != "" {
×
NEW
255
                        subnetName = subnet
×
UNCOV
256
                }
×
257

NEW
258
                if vpcName != "" && subnetName != "" {
×
NEW
259
                        return vpcName, subnetName, nil
×
UNCOV
260
                }
×
261
        }
262

263
        // Choose the most optimized and straightforward way to retrieve the name of the VPC and subnet
NEW
264
        if serviceHasSelector(service) {
×
NEW
265
                // The service has a selector, which means that the EndpointSlices should have targets.
×
NEW
266
                // We can use those targets instead of looking at every pod in the namespace.
×
NEW
267
                vpcName, subnetName = c.findVpcAndSubnetWithTargets(endpointSlices)
×
NEW
268
        } else {
×
NEW
269
                // The service has no selectors, we must find which pods in the namespace of the service
×
NEW
270
                // are targeted by the endpoint by only looking at the IPs.
×
NEW
271
                pods, err := c.podsLister.Pods(service.Namespace).List(labels.Everything())
×
NEW
272
                if err != nil {
×
NEW
273
                        err := fmt.Errorf("failed to get pods for service %s in namespace %s: %w", service.Name, service.Namespace, err)
×
NEW
274
                        klog.Error(err)
×
NEW
275
                        return "", "", err
×
UNCOV
276
                }
×
277

NEW
278
                vpcName, subnetName = c.findVpcAndSubnetWithNoTargets(endpointSlices, pods)
×
279
        }
280

NEW
281
        if vpcName == "" { // Default to what's on the service or to the default VPC
×
282
                if vpcName = service.Annotations[util.VpcAnnotation]; vpcName == "" {
×
283
                        vpcName = c.config.ClusterRouter
×
284
                }
×
285
        }
286

NEW
287
        if subnetName == "" { // Use the default subnet
×
288
                subnetName = util.DefaultSubnet
×
289
        }
×
290

NEW
291
        return vpcName, subnetName, nil
×
292
}
293

294
// findVpcAndSubnetWithTargets returns the name of the VPC and Subnet for endpoints with targets
NEW
295
func (c *Controller) findVpcAndSubnetWithTargets(endpointSlices []*discoveryv1.EndpointSlice) (vpcName, subnetName string) {
×
NEW
296
        for _, slice := range endpointSlices {
×
NEW
297
                for _, endpoint := range slice.Endpoints {
×
NEW
298
                        if endpoint.TargetRef == nil {
×
NEW
299
                                continue
×
300
                        }
301

NEW
302
                        namespace, name := endpoint.TargetRef.Namespace, endpoint.TargetRef.Name
×
NEW
303
                        if name == "" || namespace == "" {
×
NEW
304
                                continue
×
305
                        }
306

NEW
307
                        pod, err := c.podsLister.Pods(namespace).Get(name)
×
NEW
308
                        if err != nil {
×
NEW
309
                                err := fmt.Errorf("couldn't retrieve pod %s/%s: %w", namespace, name, err)
×
NEW
310
                                klog.Error(err)
×
NEW
311
                                continue
×
312
                        }
313

NEW
314
                        vpc, subnet, err := c.getEndpointVpcAndSubnet(pod, endpoint.Addresses)
×
NEW
315
                        if err != nil {
×
NEW
316
                                err := fmt.Errorf("couldn't retrieve get subnet/vpc for pod %s/%s: %w", namespace, name, err)
×
NEW
317
                                klog.Error(err)
×
NEW
318
                                continue
×
319
                        }
320

NEW
321
                        if vpcName == "" {
×
NEW
322
                                vpcName = vpc
×
NEW
323
                        }
×
324

NEW
325
                        if subnetName == "" {
×
NEW
326
                                subnetName = subnet
×
NEW
327
                        }
×
328

NEW
329
                        if vpcName != "" && subnetName != "" {
×
NEW
330
                                return vpcName, subnetName
×
NEW
331
                        }
×
332
                }
333
        }
334

NEW
335
        return vpcName, subnetName
×
336
}
337

338
// findVpcAndSubnetWithNoTargets returns the name of the VPC and Subnet for endpoints with no targets
NEW
339
func (c *Controller) findVpcAndSubnetWithNoTargets(endpointSlices []*discoveryv1.EndpointSlice, pods []*v1.Pod) (vpcName, subnetName string) {
×
NEW
340
        for _, slice := range endpointSlices {
×
NEW
341
                for _, endpoint := range slice.Endpoints {
×
NEW
342
                        for _, pod := range pods {
×
NEW
343
                                vpc, subnet, err := c.getEndpointVpcAndSubnet(pod, endpoint.Addresses)
×
NEW
344
                                if err != nil {
×
NEW
345
                                        err := fmt.Errorf("couldn't retrieve subnet/vpc for pod %s/%s: %w", pod.Namespace, pod.Name, err)
×
NEW
346
                                        klog.Error(err)
×
NEW
347
                                        continue
×
348
                                }
349

NEW
350
                                if vpcName == "" {
×
NEW
351
                                        vpcName = vpc
×
NEW
352
                                }
×
353

NEW
354
                                if subnetName == "" {
×
NEW
355
                                        subnetName = subnet
×
NEW
356
                                }
×
357

NEW
358
                                if vpcName != "" && subnetName != "" {
×
NEW
359
                                        return vpcName, subnetName
×
NEW
360
                                }
×
361
                        }
362
                }
363
        }
364

UNCOV
365
        return vpcName, subnetName
×
366
}
367

368
// getHealthCheckVip get health check vip for load balancer, the vip name is the subnet name
369
// the vip is used to check the health of the backend pod
370
func (c *Controller) getHealthCheckVip(subnetName, lbVip string) (string, error) {
×
371
        var (
×
372
                needCreateHealthCheckVip bool
×
373
                checkVip                 *kubeovnv1.Vip
×
374
                checkIP                  string
×
375
                err                      error
×
376
        )
×
377
        vipName := subnetName
×
378
        checkVip, err = c.virtualIpsLister.Get(vipName)
×
379
        if err != nil {
×
380
                if errors.IsNotFound(err) {
×
381
                        needCreateHealthCheckVip = true
×
382
                } else {
×
383
                        klog.Errorf("failed to get health check vip %s, %v", vipName, err)
×
384
                        return "", err
×
385
                }
×
386
        }
387
        if needCreateHealthCheckVip {
×
388
                vip := &kubeovnv1.Vip{
×
389
                        ObjectMeta: metav1.ObjectMeta{
×
390
                                Name: vipName,
×
391
                        },
×
392
                        Spec: kubeovnv1.VipSpec{
×
393
                                Subnet: subnetName,
×
394
                        },
×
395
                }
×
396
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vips().Create(context.Background(), vip, metav1.CreateOptions{}); err != nil {
×
397
                        klog.Errorf("failed to create health check vip %s, %v", vipName, err)
×
398
                        return "", err
×
399
                }
×
400

401
                // wait for vip created
402
                time.Sleep(1 * time.Second)
×
403
                checkVip, err = c.virtualIpsLister.Get(vipName)
×
404
                if err != nil {
×
405
                        klog.Errorf("failed to get health check vip %s, %v", vipName, err)
×
406
                        return "", err
×
407
                }
×
408
        }
409

410
        if checkVip.Status.V4ip == "" && checkVip.Status.V6ip == "" {
×
411
                err = fmt.Errorf("vip %s is not ready", vipName)
×
412
                klog.Error(err)
×
413
                return "", err
×
414
        }
×
415

416
        switch util.CheckProtocol(lbVip) {
×
417
        case kubeovnv1.ProtocolIPv4:
×
418
                checkIP = checkVip.Status.V4ip
×
419
        case kubeovnv1.ProtocolIPv6:
×
420
                checkIP = checkVip.Status.V6ip
×
421
        }
422
        if checkIP == "" {
×
423
                err = fmt.Errorf("failed to get health check vip subnet %s", vipName)
×
424
                klog.Error(err)
×
425
                return "", err
×
426
        }
×
427

428
        return checkIP, nil
×
429
}
430

431
func (c *Controller) getIPPortMappingBackend(endpointSlices []*discoveryv1.EndpointSlice, servicePort v1.ServicePort, serviceIP, checkVip string, isGenIPPortMapping bool) (map[string]string, []string) {
×
432
        var (
×
433
                ipPortMapping = map[string]string{}
×
434
                backends      = []string{}
×
435
                protocol      = util.CheckProtocol(serviceIP)
×
436
        )
×
437

×
438
        for _, endpointSlice := range endpointSlices {
×
439
                var targetPort int32
×
440
                for _, port := range endpointSlice.Ports {
×
441
                        if port.Name != nil && *port.Name == servicePort.Name {
×
442
                                targetPort = *port.Port
×
443
                                break
×
444
                        }
445
                }
446
                if targetPort == 0 {
×
447
                        continue
×
448
                }
449

450
                for _, endpoint := range endpointSlice.Endpoints {
×
451
                        if isGenIPPortMapping && endpoint.TargetRef.Name != "" {
×
NEW
452
                                pod, err := c.podsLister.Pods(endpoint.TargetRef.Namespace).Get(endpoint.TargetRef.Name)
×
NEW
453
                                if err != nil {
×
NEW
454
                                        err := fmt.Errorf("failed to get pod %s/%s: %w", endpoint.TargetRef.Namespace, endpoint.TargetRef.Name, err)
×
NEW
455
                                        klog.Error(err)
×
NEW
456
                                        continue
×
457
                                }
458

NEW
459
                                lspName, err := c.getEndpointTargetLSPName(pod, endpoint.Addresses)
×
460
                                if err != nil {
×
461
                                        err := fmt.Errorf("couldn't get LSP for the endpoint's target: %w", err)
×
462
                                        klog.Error(err)
×
463
                                        continue
×
464
                                }
465

466
                                for _, address := range endpoint.Addresses {
×
467
                                        ipPortMapping[address] = fmt.Sprintf(util.HealthCheckNamedVipTemplate, lspName, checkVip)
×
468
                                }
×
469
                        }
470
                }
471

472
                for _, endpoint := range endpointSlice.Endpoints {
×
473
                        if !endpointReady(endpoint) {
×
474
                                continue
×
475
                        }
476

477
                        for _, address := range endpoint.Addresses {
×
478
                                if util.CheckProtocol(address) == protocol {
×
479
                                        backends = append(backends, util.JoinHostPort(address, targetPort))
×
480
                                }
×
481
                        }
482
                }
483
        }
484

485
        return ipPortMapping, backends
×
486
}
487

488
func endpointReady(endpoint discoveryv1.Endpoint) bool {
1✔
489
        return endpoint.Conditions.Ready == nil || *endpoint.Conditions.Ready
1✔
490
}
1✔
491

492
// getPodProviders returns all the providers available on a pod
NEW
493
func (c *Controller) getPodProviders(pod *v1.Pod) ([]string, error) {
×
NEW
494
        // Get all the networks to which the pod is attached
×
NEW
495
        podNetworks, err := c.getPodKubeovnNets(pod)
×
NEW
496
        if err != nil {
×
NEW
497
                return nil, fmt.Errorf("failed to get pod networks: %w", err)
×
NEW
498
        }
×
499

500
        // Retrieve all the providers
NEW
501
        var providers []string
×
NEW
502
        for _, podNetwork := range podNetworks {
×
NEW
503
                providers = append(providers, podNetwork.ProviderName)
×
NEW
504
        }
×
505

NEW
506
        return providers, nil
×
507
}
508

509
// getMatchingProviderForAddress returns the provider linked to a subnet in which a particular address is present
510
func getMatchingProviderForAddress(pod *v1.Pod, providers []string, address string) string {
1✔
511
        if pod.Annotations == nil {
2✔
512
                return ""
1✔
513
        }
1✔
514

515
        // Find which provider is linked to this address
516
        for _, provider := range providers {
2✔
517
                ipsForProvider, exists := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, provider)]
1✔
518
                if !exists {
1✔
519
                        continue
×
520
                }
521

522
                ips := strings.Split(ipsForProvider, ",")
1✔
523
                if slices.Contains(ips, address) {
2✔
524
                        return provider
1✔
525
                }
1✔
526
        }
527

528
        return ""
1✔
529
}
530

531
// getEndpointProvider returns the provider linked to the addresses of an endpoint
NEW
532
func (c *Controller) getEndpointProvider(pod *v1.Pod, addresses []string) (string, error) {
×
NEW
533
        // Retrieve all the providers of the pod
×
NEW
534
        providers, err := c.getPodProviders(pod)
×
NEW
535
        if err != nil {
×
NEW
536
                return "", err
×
NEW
537
        }
×
538

539
        // Get the first matching provider for any of the address in the endpoint
NEW
540
        var provider string
×
NEW
541
        for _, address := range addresses {
×
NEW
542
                if provider = getMatchingProviderForAddress(pod, providers, address); provider != "" {
×
NEW
543
                        return provider, nil
×
NEW
544
                }
×
545
        }
546

NEW
547
        return "", nil
×
548
}
549

550
// getEndpointTargetLSPNameFromProvider returns the name of the LSP for a pod targeted by an endpoint.
551
// A custom provider can be specified if the LSP is within a subnet that doesn't use
552
// the default "ovn" provider.
553
func getEndpointTargetLSPNameFromProvider(pod *v1.Pod, provider string) string {
1✔
554
        // If no provider is specified, use the default one
1✔
555
        if provider == "" {
2✔
556
                provider = util.OvnProvider
1✔
557
        }
1✔
558

559
        target := pod.Name
1✔
560

1✔
561
        // If this pod is a VM launcher pod, we need to retrieve the name of the VM. This is necessary
1✔
562
        // because we do not use the same syntax for the LSP of normal pods and for VM pods
1✔
563
        if vmName, exists := pod.Annotations[fmt.Sprintf(util.VMAnnotationTemplate, provider)]; exists {
2✔
564
                target = vmName
1✔
565
        }
1✔
566

567
        return ovs.PodNameToPortName(target, pod.Namespace, provider)
1✔
568
}
569

570
// getEndpointTargetLSP returns the name of the LSP on which addresses are attached for a specific pod
NEW
571
func (c *Controller) getEndpointTargetLSPName(pod *v1.Pod, addresses []string) (string, error) {
×
NEW
572
        // Retrieve the provider for those addresses
×
NEW
573
        provider, err := c.getEndpointProvider(pod, addresses)
×
574
        if err != nil {
×
NEW
575
                return "", err
×
576
        }
×
577

NEW
578
        return getEndpointTargetLSPNameFromProvider(pod, provider), nil
×
579
}
580

581
// getSubnetByProvider returns the subnet linked to a provider on a pod
NEW
582
func (c *Controller) getSubnetByProvider(pod *v1.Pod, provider string) (string, error) {
×
NEW
583
        subnetName, exists := pod.Annotations[fmt.Sprintf(util.LogicalSwitchAnnotationTemplate, provider)]
×
NEW
584
        if !exists {
×
NEW
585
                return "", fmt.Errorf("couldn't find subnet linked to provider %s", provider)
×
NEW
586
        }
×
587

NEW
588
        return subnetName, nil
×
589
}
590

591
// getVpcByProvider returns the VPC linked to a provider on a pod
NEW
592
func (c *Controller) getVpcByProvider(pod *v1.Pod, provider string) (string, error) {
×
NEW
593
        vpcName, exists := pod.Annotations[fmt.Sprintf(util.LogicalRouterAnnotationTemplate, provider)]
×
NEW
594
        if !exists {
×
NEW
595
                return "", fmt.Errorf("couldn't find vpc linked to provider %s", provider)
×
NEW
596
        }
×
597

NEW
598
        return vpcName, nil
×
599
}
600

601
// getEndpointVpcAndSubnet returns the VPC/subnet for a pod and a set of addresses attached to it
NEW
602
func (c *Controller) getEndpointVpcAndSubnet(pod *v1.Pod, addresses []string) (string, string, error) {
×
NEW
603
        // Retrieve the provider for those addresses
×
NEW
604
        provider, err := c.getEndpointProvider(pod, addresses)
×
605
        if err != nil {
×
NEW
606
                return "", "", err
×
607
        }
×
608

NEW
609
        if provider == "" {
×
NEW
610
                return "", "", nil
×
UNCOV
611
        }
×
612

613
        // Retrieve the subnet
NEW
614
        subnet, err := c.getSubnetByProvider(pod, provider)
×
NEW
615
        if err != nil {
×
NEW
616
                return "", "", err
×
NEW
617
        }
×
618

619
        // Retrieve the VPC
NEW
620
        vpc, err := c.getVpcByProvider(pod, provider)
×
NEW
621
        if err != nil {
×
NEW
622
                return "", "", err
×
UNCOV
623
        }
×
624

NEW
625
        return vpc, subnet, nil
×
626
}
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