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

kubeovn / kube-ovn / 16460593740

23 Jul 2025 03:11AM UTC coverage: 21.539% (-0.006%) from 21.545%
16460593740

push

github

oilbeater
fix concurrent map race (#5510)

* [fix] use map to replace  the informer cache modify

Signed-off-by: zhanglin02 <zhanglin02@kanzhun.com>

* [fix] change of conditons

Signed-off-by: zhanglin02 <zhanglin02@kanzhun.com>

* [fix] clean up cached entry

Signed-off-by: zhanglin02 <zhanglin02@kanzhun.com>

---------

Signed-off-by: zhanglin02 <zhanglin02@kanzhun.com>
Co-authored-by: zhanglin02 <zhanglin02@kanzhun.com>
(cherry picked from commit 5f150bc48)

0 of 15 new or added lines in 3 files covered. (0.0%)

4 existing lines in 3 files now uncovered.

10504 of 48768 relevant lines covered (21.54%)

0.25 hits per line

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

0.0
/pkg/controller/vpc.go
1
package controller
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "errors"
7
        "fmt"
8
        "maps"
9
        "math"
10
        "net"
11
        "reflect"
12
        "slices"
13
        "sort"
14
        "strings"
15
        "time"
16

17
        v1 "k8s.io/api/core/v1"
18
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
19
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20
        "k8s.io/apimachinery/pkg/labels"
21
        "k8s.io/apimachinery/pkg/types"
22
        "k8s.io/client-go/tools/cache"
23
        "k8s.io/klog/v2"
24
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
25

26
        kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
27
        "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
28
        "github.com/kubeovn/kube-ovn/pkg/util"
29
)
30

31
func (c *Controller) enqueueAddVpc(obj any) {
×
32
        vpc := obj.(*kubeovnv1.Vpc)
×
33
        key := cache.MetaObjectToName(vpc).String()
×
34
        if _, ok := vpc.Labels[util.VpcExternalLabel]; !ok {
×
35
                klog.V(3).Infof("enqueue add vpc %s", key)
×
36
                c.addOrUpdateVpcQueue.Add(key)
×
37
        }
×
38
}
39

40
func vpcBFDPortChanged(oldObj, newObj *kubeovnv1.BFDPort) bool {
×
41
        if oldObj == nil && newObj == nil {
×
42
                return false
×
43
        }
×
44
        if oldObj == nil || newObj == nil {
×
45
                return true
×
46
        }
×
47
        return oldObj.Enabled != newObj.Enabled || oldObj.IP != newObj.IP || !reflect.DeepEqual(oldObj.NodeSelector, newObj.NodeSelector)
×
48
}
49

50
func (c *Controller) enqueueUpdateVpc(oldObj, newObj any) {
×
51
        oldVpc := oldObj.(*kubeovnv1.Vpc)
×
52
        newVpc := newObj.(*kubeovnv1.Vpc)
×
53

×
54
        if !newVpc.DeletionTimestamp.IsZero() ||
×
55
                !slices.Equal(oldVpc.Spec.Namespaces, newVpc.Spec.Namespaces) ||
×
56
                !reflect.DeepEqual(oldVpc.Spec.StaticRoutes, newVpc.Spec.StaticRoutes) ||
×
57
                !reflect.DeepEqual(oldVpc.Spec.PolicyRoutes, newVpc.Spec.PolicyRoutes) ||
×
58
                !reflect.DeepEqual(oldVpc.Spec.VpcPeerings, newVpc.Spec.VpcPeerings) ||
×
59
                !maps.Equal(oldVpc.Annotations, newVpc.Annotations) ||
×
60
                !slices.Equal(oldVpc.Spec.ExtraExternalSubnets, newVpc.Spec.ExtraExternalSubnets) ||
×
61
                oldVpc.Spec.EnableExternal != newVpc.Spec.EnableExternal ||
×
62
                oldVpc.Spec.EnableBfd != newVpc.Spec.EnableBfd ||
×
63
                vpcBFDPortChanged(oldVpc.Spec.BFDPort, newVpc.Spec.BFDPort) ||
×
64
                oldVpc.Labels[util.VpcExternalLabel] != newVpc.Labels[util.VpcExternalLabel] ||
×
65
                !slices.Equal(oldVpc.Status.Subnets, newVpc.Status.Subnets) {
×
66
                // TODO:// label VpcExternalLabel replace with spec enable external
×
67

×
NEW
68
                // recode last policies
×
NEW
69
                c.vpcLastPoliciesMap.Store(newVpc.Name, convertPolicies(oldVpc.Spec.PolicyRoutes))
×
70

×
71
                key := cache.MetaObjectToName(newVpc).String()
×
72
                klog.Infof("enqueue update vpc %s", key)
×
73
                c.addOrUpdateVpcQueue.Add(key)
×
UNCOV
74
        }
×
75
}
76

77
func (c *Controller) enqueueDelVpc(obj any) {
×
78
        vpc := obj.(*kubeovnv1.Vpc)
×
79
        if _, ok := vpc.Labels[util.VpcExternalLabel]; !vpc.Status.Default || !ok {
×
80
                klog.V(3).Infof("enqueue delete vpc %s", vpc.Name)
×
81
                c.delVpcQueue.Add(vpc)
×
82
        }
×
83
}
84

85
func (c *Controller) handleDelVpc(vpc *kubeovnv1.Vpc) error {
×
86
        c.vpcKeyMutex.LockKey(vpc.Name)
×
87
        defer func() { _ = c.vpcKeyMutex.UnlockKey(vpc.Name) }()
×
88
        klog.Infof("handle delete vpc %s", vpc.Name)
×
89

×
90
        // should delete vpc subnets first
×
91
        var err error
×
92
        for _, subnet := range vpc.Status.Subnets {
×
93
                if _, err = c.subnetsLister.Get(subnet); err != nil {
×
94
                        if k8serrors.IsNotFound(err) {
×
95
                                continue
×
96
                        }
97
                        err = fmt.Errorf("failed to get subnet %s for vpc %s: %w", subnet, vpc.Name, err)
×
98
                } else {
×
99
                        err = fmt.Errorf("failed to delete vpc %s, please delete subnet %s first", vpc.Name, subnet)
×
100
                }
×
101
                klog.Error(err)
×
102
                return err
×
103
        }
104

105
        // clean up vpc last policies cached
NEW
106
        c.vpcLastPoliciesMap.Delete(vpc.Name)
×
NEW
107

×
108
        if err := c.deleteVpcLb(vpc); err != nil {
×
109
                klog.Error(err)
×
110
                return err
×
111
        }
×
112

113
        if err := c.handleDelVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
114
                klog.Errorf("failed to delete external connection for vpc %s, error %v", vpc.Name, err)
×
115
                return err
×
116
        }
×
117

118
        for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
119
                klog.Infof("disconnect external network %s to vpc %s", subnet, vpc.Name)
×
120
                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
121
                        klog.Error(err)
×
122
                        return err
×
123
                }
×
124
        }
125

126
        if err := c.deleteVpcRouter(vpc.Status.Router); err != nil {
×
127
                klog.Error(err)
×
128
                return err
×
129
        }
×
130

131
        return nil
×
132
}
133

134
func (c *Controller) handleUpdateVpcStatus(key string) error {
×
135
        c.vpcKeyMutex.LockKey(key)
×
136
        defer func() { _ = c.vpcKeyMutex.UnlockKey(key) }()
×
137
        klog.Infof("handle status update for vpc %s", key)
×
138

×
139
        cachedVpc, err := c.vpcsLister.Get(key)
×
140
        if err != nil {
×
141
                if k8serrors.IsNotFound(err) {
×
142
                        return nil
×
143
                }
×
144
                klog.Error(err)
×
145
                return err
×
146
        }
147
        vpc := cachedVpc.DeepCopy()
×
148

×
149
        subnets, defaultSubnet, err := c.getVpcSubnets(vpc)
×
150
        if err != nil {
×
151
                klog.Error(err)
×
152
                return err
×
153
        }
×
154

155
        change := vpc.Status.DefaultLogicalSwitch != defaultSubnet
×
156

×
157
        vpc.Status.DefaultLogicalSwitch = defaultSubnet
×
158
        vpc.Status.Subnets = subnets
×
159

×
160
        if !vpc.Spec.BFDPort.IsEnabled() && !vpc.Status.BFDPort.IsEmpty() {
×
161
                vpc.Status.BFDPort.Clear()
×
162
        }
×
163
        bytes, err := vpc.Status.Bytes()
×
164
        if err != nil {
×
165
                klog.Error(err)
×
166
                return err
×
167
        }
×
168

169
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
170
        if err != nil {
×
171
                klog.Error(err)
×
172
                return err
×
173
        }
×
174

175
        if len(vpc.Status.Subnets) == 0 {
×
176
                klog.Infof("vpc %s has no subnets, add to queue", vpc.Name)
×
177
                c.addOrUpdateVpcQueue.AddAfter(vpc.Name, 5*time.Second)
×
178
        }
×
179

180
        if change {
×
181
                for _, ns := range vpc.Spec.Namespaces {
×
182
                        c.addNamespaceQueue.Add(ns)
×
183
                }
×
184
        }
185

186
        natGws, err := c.vpcNatGatewayLister.List(labels.Everything())
×
187
        if err != nil {
×
188
                klog.Error(err)
×
189
                return err
×
190
        }
×
191
        for _, gw := range natGws {
×
192
                if key == gw.Spec.Vpc {
×
193
                        c.updateVpcSubnetQueue.Add(gw.Name)
×
194
                }
×
195
        }
196
        return nil
×
197
}
198

199
type VpcLoadBalancer struct {
200
        TCPLoadBalancer      string
201
        TCPSessLoadBalancer  string
202
        UDPLoadBalancer      string
203
        UDPSessLoadBalancer  string
204
        SctpLoadBalancer     string
205
        SctpSessLoadBalancer string
206
}
207

208
func (c *Controller) GenVpcLoadBalancer(vpcKey string) *VpcLoadBalancer {
×
209
        if vpcKey == c.config.ClusterRouter || vpcKey == "" {
×
210
                return &VpcLoadBalancer{
×
211
                        TCPLoadBalancer:      c.config.ClusterTCPLoadBalancer,
×
212
                        TCPSessLoadBalancer:  c.config.ClusterTCPSessionLoadBalancer,
×
213
                        UDPLoadBalancer:      c.config.ClusterUDPLoadBalancer,
×
214
                        UDPSessLoadBalancer:  c.config.ClusterUDPSessionLoadBalancer,
×
215
                        SctpLoadBalancer:     c.config.ClusterSctpLoadBalancer,
×
216
                        SctpSessLoadBalancer: c.config.ClusterSctpSessionLoadBalancer,
×
217
                }
×
218
        }
×
219
        return &VpcLoadBalancer{
×
220
                TCPLoadBalancer:      fmt.Sprintf("vpc-%s-tcp-load", vpcKey),
×
221
                TCPSessLoadBalancer:  fmt.Sprintf("vpc-%s-tcp-sess-load", vpcKey),
×
222
                UDPLoadBalancer:      fmt.Sprintf("vpc-%s-udp-load", vpcKey),
×
223
                UDPSessLoadBalancer:  fmt.Sprintf("vpc-%s-udp-sess-load", vpcKey),
×
224
                SctpLoadBalancer:     fmt.Sprintf("vpc-%s-sctp-load", vpcKey),
×
225
                SctpSessLoadBalancer: fmt.Sprintf("vpc-%s-sctp-sess-load", vpcKey),
×
226
        }
×
227
}
228

229
func (c *Controller) addLoadBalancer(vpc string) (*VpcLoadBalancer, error) {
×
230
        vpcLbConfig := c.GenVpcLoadBalancer(vpc)
×
231
        if err := c.initLB(vpcLbConfig.TCPLoadBalancer, string(v1.ProtocolTCP), false); err != nil {
×
232
                return nil, err
×
233
        }
×
234
        if err := c.initLB(vpcLbConfig.TCPSessLoadBalancer, string(v1.ProtocolTCP), true); err != nil {
×
235
                return nil, err
×
236
        }
×
237
        if err := c.initLB(vpcLbConfig.UDPLoadBalancer, string(v1.ProtocolUDP), false); err != nil {
×
238
                return nil, err
×
239
        }
×
240
        if err := c.initLB(vpcLbConfig.UDPSessLoadBalancer, string(v1.ProtocolUDP), true); err != nil {
×
241
                return nil, err
×
242
        }
×
243
        if err := c.initLB(vpcLbConfig.SctpLoadBalancer, string(v1.ProtocolSCTP), false); err != nil {
×
244
                return nil, err
×
245
        }
×
246
        if err := c.initLB(vpcLbConfig.SctpSessLoadBalancer, string(v1.ProtocolSCTP), true); err != nil {
×
247
                return nil, err
×
248
        }
×
249

250
        return vpcLbConfig, nil
×
251
}
252

253
func (c *Controller) handleAddOrUpdateVpc(key string) error {
×
254
        c.vpcKeyMutex.LockKey(key)
×
255
        defer func() { _ = c.vpcKeyMutex.UnlockKey(key) }()
×
256
        klog.Infof("handle add/update vpc %s", key)
×
257

×
258
        cachedVpc, err := c.vpcsLister.Get(key)
×
259
        if err != nil {
×
260
                if k8serrors.IsNotFound(err) {
×
261
                        return nil
×
262
                }
×
263
                klog.Error(err)
×
264
                return err
×
265
        }
266

267
        vpc, err := c.formatVpc(cachedVpc.DeepCopy())
×
268
        if err != nil {
×
269
                klog.Errorf("failed to format vpc %s: %v", key, err)
×
270
                return err
×
271
        }
×
272

273
        learnFromARPRequest := vpc.Spec.EnableExternal
×
274
        if !learnFromARPRequest {
×
275
                for _, subnetName := range vpc.Status.Subnets {
×
276
                        subnet, err := c.subnetsLister.Get(subnetName)
×
277
                        if err != nil {
×
278
                                if k8serrors.IsNotFound(err) {
×
279
                                        continue
×
280
                                }
281
                                klog.Errorf("failed to get subnet %s for vpc %s: %v", subnetName, key, err)
×
282
                                return err
×
283
                        }
284
                        if subnet.Spec.Vlan != "" && subnet.Spec.U2OInterconnection {
×
285
                                learnFromARPRequest = true
×
286
                                break
×
287
                        }
288
                }
289
        }
290

291
        if err = c.createVpcRouter(key, learnFromARPRequest); err != nil {
×
292
                klog.Errorf("failed to create vpc router for vpc %s: %v", key, err)
×
293
                return err
×
294
        }
×
295

296
        var newPeers []string
×
297
        for _, peering := range vpc.Spec.VpcPeerings {
×
298
                if err = util.CheckCidrs(peering.LocalConnectIP); err != nil {
×
299
                        klog.Errorf("invalid cidr %s", peering.LocalConnectIP)
×
300
                        return err
×
301
                }
×
302

303
                newPeers = append(newPeers, peering.RemoteVpc)
×
304
                if err := c.OVNNbClient.CreatePeerRouterPort(vpc.Name, peering.RemoteVpc, peering.LocalConnectIP); err != nil {
×
305
                        klog.Errorf("create peer router port for vpc %s, %v", vpc.Name, err)
×
306
                        return err
×
307
                }
×
308
        }
309
        for _, oldPeer := range vpc.Status.VpcPeerings {
×
310
                if !slices.Contains(newPeers, oldPeer) {
×
311
                        if err = c.OVNNbClient.DeleteLogicalRouterPort(fmt.Sprintf("%s-%s", vpc.Name, oldPeer)); err != nil {
×
312
                                klog.Errorf("delete peer router port for vpc %s, %v", vpc.Name, err)
×
313
                                return err
×
314
                        }
×
315
                }
316
        }
317

318
        // handle static route
319
        var (
×
320
                staticExistedRoutes []*ovnnb.LogicalRouterStaticRoute
×
321
                staticTargetRoutes  []*kubeovnv1.StaticRoute
×
322
                staticRouteMapping  map[string][]*kubeovnv1.StaticRoute
×
323
        )
×
324

×
325
        staticExistedRoutes, err = c.OVNNbClient.ListLogicalRouterStaticRoutes(vpc.Name, nil, nil, "", nil)
×
326
        if err != nil {
×
327
                klog.Errorf("failed to get vpc %s static route list, %v", vpc.Name, err)
×
328
                return err
×
329
        }
×
330

331
        var externalSubnet *kubeovnv1.Subnet
×
332
        externalSubnetExist := false
×
333
        externalSubnetGW := ""
×
334
        if c.config.EnableEipSnat {
×
335
                externalSubnet, err = c.subnetsLister.Get(c.config.ExternalGatewaySwitch)
×
336
                if err != nil {
×
337
                        klog.Warningf("enable-eip-snat need external subnet %s to be exist: %v", c.config.ExternalGatewaySwitch, err)
×
338
                } else {
×
339
                        if !externalSubnet.Spec.LogicalGateway {
×
340
                                // logical gw external subnet can not access external
×
341
                                externalSubnetExist = true
×
342
                                externalSubnetGW = externalSubnet.Spec.Gateway
×
343
                        } else {
×
344
                                klog.Infof("default external subnet %s using logical gw", c.config.ExternalGatewaySwitch)
×
345
                        }
×
346
                }
347
        }
348

349
        staticRouteMapping = c.getRouteTablesByVpc(vpc)
×
350
        staticTargetRoutes = vpc.Spec.StaticRoutes
×
351
        if vpc.Name == c.config.ClusterRouter {
×
352
                if _, ok := staticRouteMapping[util.MainRouteTable]; !ok {
×
353
                        staticRouteMapping[util.MainRouteTable] = nil
×
354
                }
×
355

356
                joinSubnet, err := c.subnetsLister.Get(c.config.NodeSwitch)
×
357
                if err != nil {
×
358
                        if !k8serrors.IsNotFound(err) {
×
359
                                klog.Errorf("failed to get node switch subnet %s: %v", c.config.NodeSwitch, err)
×
360
                                return err
×
361
                        }
×
362
                        c.addOrUpdateVpcQueue.Add(vpc.Name)
×
363
                        return nil
×
364
                }
365
                gatewayV4, gatewayV6 := util.SplitStringIP(joinSubnet.Spec.Gateway)
×
366
                if gatewayV4 != "" {
×
367
                        for table := range staticRouteMapping {
×
368
                                staticTargetRoutes = append(
×
369
                                        staticTargetRoutes,
×
370
                                        &kubeovnv1.StaticRoute{
×
371
                                                Policy:     kubeovnv1.PolicyDst,
×
372
                                                CIDR:       "0.0.0.0/0",
×
373
                                                NextHopIP:  gatewayV4,
×
374
                                                RouteTable: table,
×
375
                                        },
×
376
                                )
×
377
                        }
×
378
                }
379
                if gatewayV6 != "" {
×
380
                        for table := range staticRouteMapping {
×
381
                                staticTargetRoutes = append(
×
382
                                        staticTargetRoutes,
×
383
                                        &kubeovnv1.StaticRoute{
×
384
                                                Policy:     kubeovnv1.PolicyDst,
×
385
                                                CIDR:       "::/0",
×
386
                                                NextHopIP:  gatewayV6,
×
387
                                                RouteTable: table,
×
388
                                        },
×
389
                                )
×
390
                        }
×
391
                }
392
                if c.config.EnableEipSnat {
×
393
                        cm, err := c.configMapsLister.ConfigMaps(c.config.ExternalGatewayConfigNS).Get(util.ExternalGatewayConfig)
×
394
                        if err == nil {
×
395
                                nextHop := cm.Data["external-gw-addr"]
×
396
                                if nextHop == "" {
×
397
                                        if !externalSubnetExist {
×
398
                                                err = fmt.Errorf("failed to get external subnet %s", c.config.ExternalGatewaySwitch)
×
399
                                                klog.Error(err)
×
400
                                                return err
×
401
                                        }
×
402
                                        nextHop = externalSubnet.Spec.Gateway
×
403
                                        if nextHop == "" {
×
404
                                                err := fmt.Errorf("subnet %s has no gateway configuration", externalSubnet.Name)
×
405
                                                klog.Error(err)
×
406
                                                return err
×
407
                                        }
×
408
                                }
409
                                if strings.Contains(nextHop, "/") {
×
410
                                        nextHop = strings.Split(nextHop, "/")[0]
×
411
                                }
×
412

413
                                lr, err := c.OVNNbClient.GetLogicalRouter(vpc.Name, false)
×
414
                                if err != nil {
×
415
                                        klog.Errorf("failed to get logical router %s: %v", vpc.Name, err)
×
416
                                        return err
×
417
                                }
×
418

419
                                for _, nat := range lr.Nat {
×
420
                                        info, err := c.OVNNbClient.GetNATByUUID(nat)
×
421
                                        if err != nil {
×
422
                                                klog.Errorf("failed to get nat ip info for vpc %s, %v", vpc.Name, err)
×
423
                                                return err
×
424
                                        }
×
425
                                        if info.LogicalIP != "" {
×
426
                                                for table := range staticRouteMapping {
×
427
                                                        staticTargetRoutes = append(
×
428
                                                                staticTargetRoutes,
×
429
                                                                &kubeovnv1.StaticRoute{
×
430
                                                                        Policy:     kubeovnv1.PolicySrc,
×
431
                                                                        CIDR:       info.LogicalIP,
×
432
                                                                        NextHopIP:  nextHop,
×
433
                                                                        RouteTable: table,
×
434
                                                                },
×
435
                                                        )
×
436
                                                }
×
437
                                        }
438
                                }
439
                        }
440
                }
441
        }
442

443
        routeNeedDel, routeNeedAdd, err := diffStaticRoute(staticExistedRoutes, staticTargetRoutes)
×
444
        if err != nil {
×
445
                klog.Errorf("failed to diff vpc %s static route, %v", vpc.Name, err)
×
446
                return err
×
447
        }
×
448

449
        for _, item := range routeNeedDel {
×
450
                klog.Infof("vpc %s del static route: %+v", vpc.Name, item)
×
451
                policy := convertPolicy(item.Policy)
×
452
                if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(vpc.Name, &item.RouteTable, &policy, item.CIDR, item.NextHopIP); err != nil {
×
453
                        klog.Errorf("del vpc %s static route failed, %v", vpc.Name, err)
×
454
                        return err
×
455
                }
×
456
        }
457

458
        for _, item := range routeNeedAdd {
×
459
                if item.BfdID != "" {
×
460
                        klog.Infof("vpc %s add static ecmp route: %+v", vpc.Name, item)
×
461
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
×
462
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, &item.BfdID, nil, item.NextHopIP,
×
463
                        ); err != nil {
×
464
                                klog.Errorf("failed to add bfd static route to vpc %s , %v", vpc.Name, err)
×
465
                                return err
×
466
                        }
×
467
                } else {
×
468
                        klog.Infof("vpc %s add static route: %+v", vpc.Name, item)
×
469
                        if err = c.OVNNbClient.AddLogicalRouterStaticRoute(
×
470
                                vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, nil, nil, item.NextHopIP,
×
471
                        ); err != nil {
×
472
                                klog.Errorf("failed to add normal static route to vpc %s , %v", vpc.Name, err)
×
473
                                return err
×
474
                        }
×
475
                }
476
        }
477

478
        // handle policy route
479
        var (
×
480
                policyRouteExisted, policyRouteNeedDel, policyRouteNeedAdd []*kubeovnv1.PolicyRoute
×
481
                policyRouteLogical                                         []*ovnnb.LogicalRouterPolicy
×
482
                externalIDs                                                = map[string]string{"vendor": util.CniTypeName}
×
483
        )
×
484

×
485
        if vpc.Name == c.config.ClusterRouter {
×
NEW
486
                lastPolicies, _ := c.vpcLastPoliciesMap.Load(vpc.Name)
×
NEW
487
                policyRouteExisted = reversePolicies(lastPolicies)
×
488
                // diff list
×
489
                policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithExisted(policyRouteExisted, vpc.Spec.PolicyRoutes)
×
490
        } else {
×
491
                if vpc.Spec.PolicyRoutes == nil {
×
492
                        // do not clean default vpc policy routes
×
493
                        if err = c.OVNNbClient.ClearLogicalRouterPolicy(vpc.Name); err != nil {
×
494
                                klog.Errorf("clean all vpc %s policy route failed, %v", vpc.Name, err)
×
495
                                return err
×
496
                        }
×
497
                } else {
×
498
                        policyRouteLogical, err = c.OVNNbClient.ListLogicalRouterPolicies(vpc.Name, -1, nil, true)
×
499
                        if err != nil {
×
500
                                klog.Errorf("failed to get vpc %s policy route list, %v", vpc.Name, err)
×
501
                                return err
×
502
                        }
×
503
                        // diff vpc policy route
504
                        policyRouteNeedDel, policyRouteNeedAdd = diffPolicyRouteWithLogical(policyRouteLogical, vpc.Spec.PolicyRoutes)
×
505
                }
506
        }
507
        // delete policies non-exist
508
        for _, item := range policyRouteNeedDel {
×
509
                klog.Infof("delete policy route for router: %s, priority: %d, match %s", vpc.Name, item.Priority, item.Match)
×
510
                if err = c.OVNNbClient.DeleteLogicalRouterPolicy(vpc.Name, item.Priority, item.Match); err != nil {
×
511
                        klog.Errorf("del vpc %s policy route failed, %v", vpc.Name, err)
×
512
                        return err
×
513
                }
×
514
        }
515
        // add new policies
516
        for _, item := range policyRouteNeedAdd {
×
517
                klog.Infof("add policy route for router: %s, match %s, action %s, nexthop %s, externalID %v", c.config.ClusterRouter, item.Match, string(item.Action), item.NextHopIP, externalIDs)
×
518
                if err = c.OVNNbClient.AddLogicalRouterPolicy(vpc.Name, item.Priority, item.Match, string(item.Action), []string{item.NextHopIP}, nil, externalIDs); err != nil {
×
519
                        klog.Errorf("add policy route to vpc %s failed, %v", vpc.Name, err)
×
520
                        return err
×
521
                }
×
522
        }
523

524
        vpcSubnets, defaultSubnet, err := c.getVpcSubnets(vpc)
×
525
        if err != nil {
×
526
                klog.Error(err)
×
527
                return err
×
528
        }
×
529

530
        vpc.Status.Subnets = vpcSubnets
×
531
        vpc.Status.DefaultLogicalSwitch = defaultSubnet
×
532
        vpc.Status.Router = key
×
533
        vpc.Status.Standby = true
×
534
        vpc.Status.VpcPeerings = newPeers
×
535
        if c.config.EnableLb {
×
536
                vpcLb, err := c.addLoadBalancer(key)
×
537
                if err != nil {
×
538
                        klog.Error(err)
×
539
                        return err
×
540
                }
×
541
                vpc.Status.TCPLoadBalancer = vpcLb.TCPLoadBalancer
×
542
                vpc.Status.TCPSessionLoadBalancer = vpcLb.TCPSessLoadBalancer
×
543
                vpc.Status.UDPLoadBalancer = vpcLb.UDPLoadBalancer
×
544
                vpc.Status.UDPSessionLoadBalancer = vpcLb.UDPSessLoadBalancer
×
545
                vpc.Status.SctpLoadBalancer = vpcLb.SctpLoadBalancer
×
546
                vpc.Status.SctpSessionLoadBalancer = vpcLb.SctpSessLoadBalancer
×
547
        }
548
        bytes, err := vpc.Status.Bytes()
×
549
        if err != nil {
×
550
                klog.Error(err)
×
551
                return err
×
552
        }
×
553
        vpc, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(), vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status")
×
554
        if err != nil {
×
555
                klog.Error(err)
×
556
                return err
×
557
        }
×
558

559
        if len(vpc.Annotations) != 0 && strings.ToLower(vpc.Annotations[util.VpcLbAnnotation]) == "on" {
×
560
                if err = c.createVpcLb(vpc); err != nil {
×
561
                        klog.Error(err)
×
562
                        return err
×
563
                }
×
564
        } else if err = c.deleteVpcLb(vpc); err != nil {
×
565
                klog.Error(err)
×
566
                return err
×
567
        }
×
568

569
        subnets, err := c.subnetsLister.List(labels.Everything())
×
570
        if err != nil {
×
571
                klog.Error(err)
×
572
                return err
×
573
        }
×
574
        custVpcEnableExternalEcmp := false
×
575
        for _, subnet := range subnets {
×
576
                if subnet.Spec.Vpc == key {
×
577
                        c.addOrUpdateSubnetQueue.Add(subnet.Name)
×
578
                        if vpc.Name != util.DefaultVpc && vpc.Spec.EnableBfd && subnet.Spec.EnableEcmp {
×
579
                                custVpcEnableExternalEcmp = true
×
580
                        }
×
581
                }
582
        }
583

584
        if vpc.Spec.EnableExternal || vpc.Status.EnableExternal {
×
585
                if err = c.handleUpdateVpcExternal(cachedVpc, custVpcEnableExternalEcmp, externalSubnetExist, externalSubnetGW); err != nil {
×
586
                        klog.Errorf("failed to handle update external subnet for vpc %s, %v", key, err)
×
587
                        return err
×
588
                }
×
589
        }
590

591
        bfdPortName, bfdPortNodes, err := c.reconcileVpcBfdLRP(vpc)
×
592
        if err != nil {
×
593
                klog.Error(err)
×
594
                return err
×
595
        }
×
596
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
×
597
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{}
×
598
        } else {
×
599
                vpc.Status.BFDPort = kubeovnv1.BFDPortStatus{
×
600
                        Name:  bfdPortName,
×
601
                        IP:    vpc.Spec.BFDPort.IP,
×
602
                        Nodes: bfdPortNodes,
×
603
                }
×
604
        }
×
605
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().
×
606
                UpdateStatus(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
607
                klog.Error(err)
×
608
                return err
×
609
        }
×
610

611
        return nil
×
612
}
613

614
func (c *Controller) handleUpdateVpcExternal(vpc *kubeovnv1.Vpc, custVpcEnableExternalEcmp, defaultExternalSubnetExist bool, externalSubnetGW string) error {
×
615
        if c.config.EnableEipSnat && vpc.Name == util.DefaultVpc {
×
616
                klog.Infof("external_gw handle ovn default external gw %s", vpc.Name)
×
617
                return nil
×
618
        }
×
619

620
        if !vpc.Spec.EnableExternal && !vpc.Status.EnableExternal {
×
621
                // no need to handle external connection
×
622
                return nil
×
623
        }
×
624

625
        // handle any vpc external
626
        if vpc.Spec.EnableExternal && !defaultExternalSubnetExist && vpc.Spec.ExtraExternalSubnets == nil {
×
627
                // at least have a external subnet
×
628
                err := fmt.Errorf("failed to get external subnet for enable external vpc %s", vpc.Name)
×
629
                klog.Error(err)
×
630
                return err
×
631
        }
×
632

633
        if !vpc.Spec.EnableExternal && vpc.Status.EnableExternal {
×
634
                // just del all external subnets connection
×
635
                klog.Infof("disconnect default external subnet %s to vpc %s", c.config.ExternalGatewaySwitch, vpc.Name)
×
636
                if err := c.handleDelVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
637
                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", c.config.ExternalGatewaySwitch, vpc.Name, err)
×
638
                        return err
×
639
                }
×
640
                for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
641
                        klog.Infof("disconnect external subnet %s to vpc %s", subnet, vpc.Name)
×
642
                        if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
643
                                klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
644
                                return err
×
645
                        }
×
646
                }
647
        }
648

649
        if vpc.Spec.EnableExternal {
×
650
                if !vpc.Status.EnableExternal {
×
651
                        // just add external connection
×
652
                        if vpc.Spec.ExtraExternalSubnets == nil && defaultExternalSubnetExist {
×
653
                                // only connect default external subnet
×
654
                                klog.Infof("connect default external subnet %s with vpc %s", c.config.ExternalGatewaySwitch, vpc.Name)
×
655
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, c.config.ExternalGatewaySwitch); err != nil {
×
656
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", c.config.ExternalGatewaySwitch, vpc.Name, err)
×
657
                                        return err
×
658
                                }
×
659
                        }
660

661
                        // only connect provider network vlan external subnet
662
                        for _, subnet := range vpc.Spec.ExtraExternalSubnets {
×
663
                                klog.Infof("connect external subnet %s with vpc %s", subnet, vpc.Name)
×
664
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
665
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
666
                                        return err
×
667
                                }
×
668
                        }
669
                }
670

671
                // diff to add
672
                for _, subnet := range vpc.Spec.ExtraExternalSubnets {
×
673
                        if !slices.Contains(vpc.Status.ExtraExternalSubnets, subnet) {
×
674
                                klog.Infof("connect external subnet %s with vpc %s", subnet, vpc.Name)
×
675
                                if err := c.handleAddVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
676
                                        klog.Errorf("failed to add external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
677
                                        return err
×
678
                                }
×
679
                        }
680
                }
681

682
                // diff to del
683
                for _, subnet := range vpc.Status.ExtraExternalSubnets {
×
684
                        if !slices.Contains(vpc.Spec.ExtraExternalSubnets, subnet) {
×
685
                                klog.Infof("disconnect external subnet %s to vpc %s", subnet, vpc.Name)
×
686
                                if err := c.handleDelVpcExternalSubnet(vpc.Name, subnet); err != nil {
×
687
                                        klog.Errorf("failed to delete external subnet %s connection for vpc %s, error %v", subnet, vpc.Name, err)
×
688
                                        return err
×
689
                                }
×
690
                        }
691
                }
692
        }
693

694
        // custom vpc enable bfd
695
        if vpc.Spec.EnableBfd && vpc.Name != util.DefaultVpc && defaultExternalSubnetExist {
×
696
                // create bfd between lrp and physical switch gw
×
697
                // bfd status down means current lrp binding chassis node external nic lost external network connectivity
×
698
                // should switch lrp to another node
×
699
                lrpEipName := fmt.Sprintf("%s-%s", vpc.Name, c.config.ExternalGatewaySwitch)
×
700
                v4ExtGw, _ := util.SplitStringIP(externalSubnetGW)
×
701
                // TODO: dualstack
×
702
                if _, err := c.OVNNbClient.CreateBFD(lrpEipName, v4ExtGw, c.config.BfdMinRx, c.config.BfdMinTx, c.config.BfdDetectMult, nil); err != nil {
×
703
                        klog.Error(err)
×
704
                        return err
×
705
                }
×
706
                // TODO: support multi external nic
707
                if custVpcEnableExternalEcmp {
×
708
                        klog.Infof("remove normal static ecmp route for vpc %s", vpc.Name)
×
709
                        // auto remove normal type static route, if using ecmp based bfd
×
710
                        if err := c.reconcileCustomVpcDelNormalStaticRoute(vpc.Name); err != nil {
×
711
                                klog.Errorf("failed to reconcile del vpc %q normal static route", vpc.Name)
×
712
                                return err
×
713
                        }
×
714
                }
715
        }
716

717
        if !vpc.Spec.EnableBfd && vpc.Status.EnableBfd {
×
718
                lrpEipName := fmt.Sprintf("%s-%s", vpc.Name, c.config.ExternalGatewaySwitch)
×
719
                if err := c.OVNNbClient.DeleteBFDByDstIP(lrpEipName, ""); err != nil {
×
720
                        klog.Error(err)
×
721
                        return err
×
722
                }
×
723
                if err := c.handleDeleteVpcStaticRoute(vpc.Name); err != nil {
×
724
                        klog.Errorf("failed to delete bfd route for vpc %s, error %v", vpc.Name, err)
×
725
                        return err
×
726
                }
×
727
        }
728

729
        if err := c.updateVpcExternalStatus(vpc.Name, vpc.Spec.EnableExternal); err != nil {
×
730
                klog.Errorf("failed to update vpc external subnets status, %v", err)
×
731
                return err
×
732
        }
×
733
        return nil
×
734
}
735

736
func (c *Controller) reconcileVpcBfdLRP(vpc *kubeovnv1.Vpc) (string, []string, error) {
×
737
        portName := "bfd@" + vpc.Name
×
738
        if vpc.Spec.BFDPort == nil || !vpc.Spec.BFDPort.Enabled {
×
739
                if err := c.OVNNbClient.DeleteLogicalRouterPort(portName); err != nil {
×
740
                        err = fmt.Errorf("failed to delete BFD LRP %s: %w", portName, err)
×
741
                        klog.Error(err)
×
742
                        return portName, nil, err
×
743
                }
×
744
                if err := c.OVNNbClient.DeleteHAChassisGroup(portName); err != nil {
×
745
                        err = fmt.Errorf("failed to delete HA chassis group %s: %w", portName, err)
×
746
                        klog.Error(err)
×
747
                        return portName, nil, err
×
748
                }
×
749
                return portName, nil, nil
×
750
        }
751

752
        var err error
×
753
        chassisCount := 3
×
754
        selector := labels.Everything()
×
755
        if vpc.Spec.BFDPort.NodeSelector != nil {
×
756
                chassisCount = math.MaxInt
×
757
                if selector, err = metav1.LabelSelectorAsSelector(vpc.Spec.BFDPort.NodeSelector); err != nil {
×
758
                        err = fmt.Errorf("failed to parse node selector %q: %w", vpc.Spec.BFDPort.NodeSelector.String(), err)
×
759
                        klog.Error(err)
×
760
                        return portName, nil, err
×
761
                }
×
762
        }
763

764
        nodes, err := c.nodesLister.List(selector)
×
765
        if err != nil {
×
766
                err = fmt.Errorf("failed to list nodes with selector %q: %w", vpc.Spec.BFDPort.NodeSelector, err)
×
767
                klog.Error(err)
×
768
                return portName, nil, err
×
769
        }
×
770
        if len(nodes) == 0 {
×
771
                err = fmt.Errorf("no nodes found by selector %q", selector.String())
×
772
                klog.Error(err)
×
773
                return portName, nil, err
×
774
        }
×
775

776
        nodeNames := make([]string, 0, len(nodes))
×
777
        chassisCount = min(chassisCount, len(nodes))
×
778
        chassisNames := make([]string, 0, chassisCount)
×
779
        for _, nodes := range nodes[:chassisCount] {
×
780
                chassis, err := c.OVNSbClient.GetChassisByHost(nodes.Name)
×
781
                if err != nil {
×
782
                        err = fmt.Errorf("failed to get chassis of node %s: %w", nodes.Name, err)
×
783
                        klog.Error(err)
×
784
                        return portName, nil, err
×
785
                }
×
786
                chassisNames = append(chassisNames, chassis.Name)
×
787
                nodeNames = append(nodeNames, nodes.Name)
×
788
        }
789

790
        networks := strings.Split(vpc.Spec.BFDPort.IP, ",")
×
791
        if err = c.OVNNbClient.CreateLogicalRouterPort(vpc.Name, portName, "", networks); err != nil {
×
792
                klog.Error(err)
×
793
                return portName, nil, err
×
794
        }
×
795
        if err = c.OVNNbClient.UpdateLogicalRouterPortNetworks(portName, networks); err != nil {
×
796
                klog.Error(err)
×
797
                return portName, nil, err
×
798
        }
×
799
        if err = c.OVNNbClient.UpdateLogicalRouterPortOptions(portName, map[string]string{"bfd-only": "true"}); err != nil {
×
800
                klog.Error(err)
×
801
                return portName, nil, err
×
802
        }
×
803
        if err = c.OVNNbClient.CreateHAChassisGroup(portName, chassisNames, map[string]string{"lrp": portName}); err != nil {
×
804
                klog.Error(err)
×
805
                return portName, nil, err
×
806
        }
×
807
        if err = c.OVNNbClient.SetLogicalRouterPortHAChassisGroup(portName, portName); err != nil {
×
808
                klog.Error(err)
×
809
                return portName, nil, err
×
810
        }
×
811

812
        return portName, nodeNames, nil
×
813
}
814

815
func (c *Controller) addPolicyRouteToVpc(vpcName string, policy *kubeovnv1.PolicyRoute, externalIDs map[string]string) error {
×
816
        var (
×
817
                nextHops []string
×
818
                err      error
×
819
        )
×
820

×
821
        if policy.NextHopIP != "" {
×
822
                nextHops = strings.Split(policy.NextHopIP, ",")
×
823
        }
×
824

825
        if err = c.OVNNbClient.AddLogicalRouterPolicy(vpcName, policy.Priority, policy.Match, string(policy.Action), nextHops, nil, externalIDs); err != nil {
×
826
                klog.Errorf("add policy route to vpc %s failed, %v", vpcName, err)
×
827
                return err
×
828
        }
×
829
        return nil
×
830
}
831

832
func buildExternalIDsMapKey(match, action string, priority int) string {
×
833
        return fmt.Sprintf("%s-%s-%d", match, action, priority)
×
834
}
×
835

836
func (c *Controller) batchAddPolicyRouteToVpc(name string, policies []*kubeovnv1.PolicyRoute, externalIDs map[string]map[string]string) error {
×
837
        if len(policies) == 0 {
×
838
                return nil
×
839
        }
×
840
        start := time.Now()
×
841
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
842
        for _, policy := range policies {
×
843
                var nextHops []string
×
844
                if policy.NextHopIP != "" {
×
845
                        nextHops = strings.Split(policy.NextHopIP, ",")
×
846
                }
×
847
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
848
                        Priority:    policy.Priority,
×
849
                        Nexthops:    nextHops,
×
850
                        Action:      string(policy.Action),
×
851
                        Match:       policy.Match,
×
852
                        ExternalIDs: externalIDs[buildExternalIDsMapKey(policy.Match, string(policy.Action), policy.Priority)],
×
853
                })
×
854
        }
855

856
        if err := c.OVNNbClient.BatchAddLogicalRouterPolicy(name, routerPolicies...); err != nil {
×
857
                klog.Errorf("batch add policy route to vpc %s failed, %v", name, err)
×
858
                return err
×
859
        }
×
860
        klog.Infof("take to %v batch add policy route to vpc %s policies %d", time.Since(start), name, len(policies))
×
861
        return nil
×
862
}
863

864
func (c *Controller) deletePolicyRouteFromVpc(vpcName string, priority int, match string) error {
×
865
        var (
×
866
                vpc, cachedVpc *kubeovnv1.Vpc
×
867
                err            error
×
868
        )
×
869

×
870
        if err = c.OVNNbClient.DeleteLogicalRouterPolicy(vpcName, priority, match); err != nil {
×
871
                klog.Error(err)
×
872
                return err
×
873
        }
×
874

875
        cachedVpc, err = c.vpcsLister.Get(vpcName)
×
876
        if err != nil {
×
877
                if k8serrors.IsNotFound(err) {
×
878
                        return nil
×
879
                }
×
880
                klog.Error(err)
×
881
                return err
×
882
        }
883
        vpc = cachedVpc.DeepCopy()
×
884
        // make sure custom policies not be deleted
×
885
        _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
886
        if err != nil {
×
887
                klog.Error(err)
×
888
                return err
×
889
        }
×
890
        return nil
×
891
}
892

893
func (c *Controller) batchDeletePolicyRouteFromVpc(name string, policies []*kubeovnv1.PolicyRoute) error {
×
894
        var (
×
895
                vpc, cachedVpc *kubeovnv1.Vpc
×
896
                err            error
×
897
        )
×
898

×
899
        start := time.Now()
×
900
        routerPolicies := make([]*ovnnb.LogicalRouterPolicy, 0, len(policies))
×
901
        for _, policy := range policies {
×
902
                routerPolicies = append(routerPolicies, &ovnnb.LogicalRouterPolicy{
×
903
                        Priority: policy.Priority,
×
904
                        Match:    policy.Match,
×
905
                })
×
906
        }
×
907

908
        if err = c.OVNNbClient.BatchDeleteLogicalRouterPolicy(name, routerPolicies); err != nil {
×
909
                return err
×
910
        }
×
911
        klog.V(3).Infof("take to %v batch delete policy route from vpc %s policies %d", time.Since(start), name, len(policies))
×
912

×
913
        cachedVpc, err = c.vpcsLister.Get(name)
×
914
        if err != nil {
×
915
                if k8serrors.IsNotFound(err) {
×
916
                        return nil
×
917
                }
×
918
                klog.Error(err)
×
919
                return err
×
920
        }
921
        vpc = cachedVpc.DeepCopy()
×
922
        // make sure custom policies not be deleted
×
923
        _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
924
        if err != nil {
×
925
                klog.Error(err)
×
926
                return err
×
927
        }
×
928
        return nil
×
929
}
930

931
func (c *Controller) addStaticRouteToVpc(name string, route *kubeovnv1.StaticRoute) error {
×
932
        if route.BfdID != "" {
×
933
                klog.Infof("vpc %s add static ecmp route: %+v", name, route)
×
934
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
935
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, &route.BfdID, nil, route.NextHopIP,
×
936
                ); err != nil {
×
937
                        klog.Errorf("failed to add bfd static route to vpc %s , %v", name, err)
×
938
                        return err
×
939
                }
×
940
        } else {
×
941
                klog.Infof("vpc %s add static route: %+v", name, route)
×
942
                if err := c.OVNNbClient.AddLogicalRouterStaticRoute(
×
943
                        name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, nil, nil, route.NextHopIP,
×
944
                ); err != nil {
×
945
                        klog.Errorf("failed to add normal static route to vpc %s , %v", name, err)
×
946
                        return err
×
947
                }
×
948
        }
949
        return nil
×
950
}
951

952
func (c *Controller) deleteStaticRouteFromVpc(name, table, cidr, nextHop string, policy kubeovnv1.RoutePolicy) error {
×
953
        var (
×
954
                policyStr string
×
955
                err       error
×
956
        )
×
957

×
958
        policyStr = convertPolicy(policy)
×
959
        if err = c.OVNNbClient.DeleteLogicalRouterStaticRoute(name, &table, &policyStr, cidr, nextHop); err != nil {
×
960
                klog.Errorf("del vpc %s static route failed, %v", name, err)
×
961
                return err
×
962
        }
×
963

964
        return nil
×
965
}
966

967
func (c *Controller) batchDeleteStaticRouteFromVpc(name string, staticRoutes []*kubeovnv1.StaticRoute) error {
×
968
        var (
×
969
                vpc, cachedVpc *kubeovnv1.Vpc
×
970
                err            error
×
971
        )
×
972
        start := time.Now()
×
973
        routeCount := len(staticRoutes)
×
974
        delRoutes := make([]*ovnnb.LogicalRouterStaticRoute, 0, routeCount)
×
975
        for _, sr := range staticRoutes {
×
976
                policyStr := convertPolicy(sr.Policy)
×
977
                newRoute := &ovnnb.LogicalRouterStaticRoute{
×
978
                        RouteTable: sr.RouteTable,
×
979
                        Nexthop:    sr.NextHopIP,
×
980
                        Policy:     &policyStr,
×
981
                        IPPrefix:   sr.CIDR,
×
982
                }
×
983
                delRoutes = append(delRoutes, newRoute)
×
984
        }
×
985
        if err = c.OVNNbClient.BatchDeleteLogicalRouterStaticRoute(name, delRoutes); err != nil {
×
986
                klog.Errorf("batch del vpc %s static route %d failed, %v", name, routeCount, err)
×
987
                return err
×
988
        }
×
989
        klog.V(3).Infof("take to %v batch delete static route from vpc %s static routes %d", time.Since(start), name, len(delRoutes))
×
990

×
991
        cachedVpc, err = c.vpcsLister.Get(name)
×
992
        if err != nil {
×
993
                if k8serrors.IsNotFound(err) {
×
994
                        return nil
×
995
                }
×
996
                klog.Error(err)
×
997
                return err
×
998
        }
999
        vpc = cachedVpc.DeepCopy()
×
1000
        // make sure custom policies not be deleted
×
1001
        _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
1002
        if err != nil {
×
1003
                klog.Error(err)
×
1004
                return err
×
1005
        }
×
1006
        return nil
×
1007
}
1008

1009
func diffPolicyRouteWithExisted(exists, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
×
1010
        var (
×
1011
                dels, adds []*kubeovnv1.PolicyRoute
×
1012
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
1013
                key        string
×
1014
                ok         bool
×
1015
        )
×
1016

×
1017
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
×
1018
        for _, item := range exists {
×
1019
                existsMap[getPolicyRouteItemKey(item)] = item
×
1020
        }
×
1021
        // load policies to add
1022
        for _, item := range target {
×
1023
                key = getPolicyRouteItemKey(item)
×
1024

×
1025
                if _, ok = existsMap[key]; ok {
×
1026
                        delete(existsMap, key)
×
1027
                } else {
×
1028
                        adds = append(adds, item)
×
1029
                }
×
1030
        }
1031
        // load policies to delete
1032
        for _, item := range existsMap {
×
1033
                dels = append(dels, item)
×
1034
        }
×
1035
        return dels, adds
×
1036
}
1037

1038
func diffPolicyRouteWithLogical(exists []*ovnnb.LogicalRouterPolicy, target []*kubeovnv1.PolicyRoute) ([]*kubeovnv1.PolicyRoute, []*kubeovnv1.PolicyRoute) {
×
1039
        var (
×
1040
                dels, adds []*kubeovnv1.PolicyRoute
×
1041
                existsMap  map[string]*kubeovnv1.PolicyRoute
×
1042
                key        string
×
1043
                ok         bool
×
1044
        )
×
1045
        existsMap = make(map[string]*kubeovnv1.PolicyRoute, len(exists))
×
1046

×
1047
        for _, item := range exists {
×
1048
                policy := &kubeovnv1.PolicyRoute{
×
1049
                        Priority: item.Priority,
×
1050
                        Match:    item.Match,
×
1051
                        Action:   kubeovnv1.PolicyRouteAction(item.Action),
×
1052
                }
×
1053
                existsMap[getPolicyRouteItemKey(policy)] = policy
×
1054
        }
×
1055

1056
        for _, item := range target {
×
1057
                key = getPolicyRouteItemKey(item)
×
1058

×
1059
                if _, ok = existsMap[key]; ok {
×
1060
                        delete(existsMap, key)
×
1061
                } else {
×
1062
                        adds = append(adds, item)
×
1063
                }
×
1064
        }
1065

1066
        for _, item := range existsMap {
×
1067
                dels = append(dels, item)
×
1068
        }
×
1069
        return dels, adds
×
1070
}
1071

1072
func getPolicyRouteItemKey(item *kubeovnv1.PolicyRoute) (key string) {
×
1073
        return fmt.Sprintf("%d:%s:%s:%s", item.Priority, item.Match, item.Action, item.NextHopIP)
×
1074
}
×
1075

1076
func diffStaticRoute(exist []*ovnnb.LogicalRouterStaticRoute, target []*kubeovnv1.StaticRoute) (routeNeedDel, routeNeedAdd []*kubeovnv1.StaticRoute, err error) {
×
1077
        existRouteMap := make(map[string]*kubeovnv1.StaticRoute, len(exist))
×
1078
        for _, item := range exist {
×
1079
                policy := kubeovnv1.PolicyDst
×
1080
                if item.Policy != nil && *item.Policy == ovnnb.LogicalRouterStaticRoutePolicySrcIP {
×
1081
                        policy = kubeovnv1.PolicySrc
×
1082
                }
×
1083
                route := &kubeovnv1.StaticRoute{
×
1084
                        Policy:     policy,
×
1085
                        CIDR:       item.IPPrefix,
×
1086
                        NextHopIP:  item.Nexthop,
×
1087
                        RouteTable: item.RouteTable,
×
1088
                        ECMPMode:   util.StaticRouteBfdEcmp,
×
1089
                }
×
1090
                if item.BFD != nil {
×
1091
                        route.BfdID = *item.BFD
×
1092
                }
×
1093
                existRouteMap[getStaticRouteItemKey(route)] = route
×
1094
        }
1095

1096
        for _, item := range target {
×
1097
                key := getStaticRouteItemKey(item)
×
1098
                if _, ok := existRouteMap[key]; ok {
×
1099
                        delete(existRouteMap, key)
×
1100
                } else {
×
1101
                        routeNeedAdd = append(routeNeedAdd, item)
×
1102
                }
×
1103
        }
1104
        for _, item := range existRouteMap {
×
1105
                routeNeedDel = append(routeNeedDel, item)
×
1106
        }
×
1107
        return
×
1108
}
1109

1110
func getStaticRouteItemKey(item *kubeovnv1.StaticRoute) string {
×
1111
        var key string
×
1112
        if item.Policy == kubeovnv1.PolicyDst {
×
1113
                key = fmt.Sprintf("%s:dst:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
×
1114
        } else {
×
1115
                key = fmt.Sprintf("%s:src:%s=>%s", item.RouteTable, item.CIDR, item.NextHopIP)
×
1116
        }
×
1117
        return key
×
1118
}
1119

1120
func (c *Controller) formatVpc(vpc *kubeovnv1.Vpc) (*kubeovnv1.Vpc, error) {
×
1121
        var changed bool
×
1122
        for _, item := range vpc.Spec.StaticRoutes {
×
1123
                // check policy
×
1124
                if item.Policy == "" {
×
1125
                        item.Policy = kubeovnv1.PolicyDst
×
1126
                        changed = true
×
1127
                }
×
1128
                if item.Policy != kubeovnv1.PolicyDst && item.Policy != kubeovnv1.PolicySrc {
×
1129
                        return nil, fmt.Errorf("unknown policy type: %q", item.Policy)
×
1130
                }
×
1131
                // check cidr
1132
                if strings.Contains(item.CIDR, "/") {
×
1133
                        if _, _, err := net.ParseCIDR(item.CIDR); err != nil {
×
1134
                                return nil, fmt.Errorf("invalid cidr %q: %w", item.CIDR, err)
×
1135
                        }
×
1136
                } else if ip := net.ParseIP(item.CIDR); ip == nil {
×
1137
                        return nil, fmt.Errorf("invalid ip %q", item.CIDR)
×
1138
                }
×
1139
                // check next hop ip
1140
                if ip := net.ParseIP(item.NextHopIP); ip == nil {
×
1141
                        return nil, fmt.Errorf("invalid next hop ip %q", item.NextHopIP)
×
1142
                }
×
1143
        }
1144

1145
        for _, route := range vpc.Spec.PolicyRoutes {
×
1146
                if route.Action != kubeovnv1.PolicyRouteActionReroute {
×
1147
                        if route.NextHopIP != "" {
×
1148
                                route.NextHopIP = ""
×
1149
                                changed = true
×
1150
                        }
×
1151
                } else {
×
1152
                        // ecmp policy route may reroute to multiple next hop ips
×
1153
                        for ipStr := range strings.SplitSeq(route.NextHopIP, ",") {
×
1154
                                if ip := net.ParseIP(ipStr); ip == nil {
×
1155
                                        err := fmt.Errorf("invalid next hop ips: %s", route.NextHopIP)
×
1156
                                        klog.Error(err)
×
1157
                                        return nil, err
×
1158
                                }
×
1159
                        }
1160
                }
1161
        }
1162

1163
        if vpc.DeletionTimestamp.IsZero() && !slices.Contains(vpc.GetFinalizers(), util.KubeOVNControllerFinalizer) {
×
1164
                controllerutil.AddFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1165
                changed = true
×
1166
        }
×
1167

1168
        if !vpc.DeletionTimestamp.IsZero() && len(vpc.Status.Subnets) == 0 {
×
1169
                controllerutil.RemoveFinalizer(vpc, util.KubeOVNControllerFinalizer)
×
1170
                changed = true
×
1171
        }
×
1172

1173
        if changed {
×
1174
                newVpc, err := c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{})
×
1175
                if err != nil {
×
1176
                        klog.Errorf("failed to update vpc %s: %v", vpc.Name, err)
×
1177
                        return nil, err
×
1178
                }
×
1179
                return newVpc, nil
×
1180
        }
1181

1182
        return vpc, nil
×
1183
}
1184

1185
func convertPolicies(list []*kubeovnv1.PolicyRoute) string {
×
1186
        if list == nil {
×
1187
                return ""
×
1188
        }
×
1189

1190
        var (
×
1191
                res []byte
×
1192
                err error
×
1193
        )
×
1194

×
1195
        if res, err = json.Marshal(list); err != nil {
×
1196
                klog.Errorf("failed to serialize policy routes %v , reason : %v", list, err)
×
1197
                return ""
×
1198
        }
×
1199
        return string(res)
×
1200
}
1201

1202
func reversePolicies(origin string) []*kubeovnv1.PolicyRoute {
×
1203
        if origin == "" {
×
1204
                return nil
×
1205
        }
×
1206

1207
        var (
×
1208
                list []*kubeovnv1.PolicyRoute
×
1209
                err  error
×
1210
        )
×
1211

×
1212
        if err = json.Unmarshal([]byte(origin), &list); err != nil {
×
1213
                klog.Errorf("failed to deserialize policy routes %v , reason : %v", list, err)
×
1214
                return nil
×
1215
        }
×
1216
        return list
×
1217
}
1218

1219
func convertPolicy(origin kubeovnv1.RoutePolicy) string {
×
1220
        if origin == kubeovnv1.PolicyDst {
×
1221
                return ovnnb.LogicalRouterStaticRoutePolicyDstIP
×
1222
        }
×
1223
        return ovnnb.LogicalRouterStaticRoutePolicySrcIP
×
1224
}
1225

1226
func reversePolicy(origin ovnnb.LogicalRouterStaticRoutePolicy) kubeovnv1.RoutePolicy {
×
1227
        if origin == ovnnb.LogicalRouterStaticRoutePolicyDstIP {
×
1228
                return kubeovnv1.PolicyDst
×
1229
        }
×
1230
        return kubeovnv1.PolicySrc
×
1231
}
1232

1233
func (c *Controller) getVpcSubnets(vpc *kubeovnv1.Vpc) (subnets []string, defaultSubnet string, err error) {
×
1234
        subnets = []string{}
×
1235
        allSubnets, err := c.subnetsLister.List(labels.Everything())
×
1236
        if err != nil {
×
1237
                klog.Error(err)
×
1238
                return nil, "", err
×
1239
        }
×
1240

1241
        for _, subnet := range allSubnets {
×
1242
                if subnet.Spec.Vpc != vpc.Name || !subnet.DeletionTimestamp.IsZero() || !isOvnSubnet(subnet) {
×
1243
                        continue
×
1244
                }
1245

1246
                subnets = append(subnets, subnet.Name)
×
1247
                if subnet.Spec.Default {
×
1248
                        defaultSubnet = subnet.Name
×
1249
                }
×
1250

1251
                if vpc.Name != util.DefaultVpc && vpc.Spec.DefaultSubnet != "" && vpc.Spec.DefaultSubnet == subnet.Name {
×
1252
                        defaultSubnet = vpc.Spec.DefaultSubnet
×
1253
                }
×
1254
        }
1255
        sort.Strings(subnets)
×
1256
        return
×
1257
}
1258

1259
// createVpcRouter create router to connect logical switches in vpc
1260
func (c *Controller) createVpcRouter(lr string, learnFromARPRequest bool) error {
×
1261
        if err := c.OVNNbClient.CreateLogicalRouter(lr); err != nil {
×
1262
                klog.Errorf("create logical router %s failed: %v", lr, err)
×
1263
                return err
×
1264
        }
×
1265

1266
        vpcRouter, err := c.OVNNbClient.GetLogicalRouter(lr, false)
×
1267
        if err != nil {
×
1268
                klog.Errorf("get logical router %s failed: %v", lr, err)
×
1269
                return err
×
1270
        }
×
1271

1272
        lrOptions := map[string]string{
×
1273
                "mac_binding_age_threshold": "300",
×
1274
                "dynamic_neigh_routers":     "true",
×
1275
        }
×
1276
        if !learnFromARPRequest {
×
1277
                lrOptions["always_learn_from_arp_request"] = "false"
×
1278
        }
×
1279
        if !maps.Equal(vpcRouter.Options, lrOptions) {
×
1280
                vpcRouter.Options = lrOptions
×
1281
                if err = c.OVNNbClient.UpdateLogicalRouter(vpcRouter, &vpcRouter.Options); err != nil {
×
1282
                        klog.Errorf("failed to update options of logical router %s: %v", lr, err)
×
1283
                        return err
×
1284
                }
×
1285
        }
1286

1287
        return nil
×
1288
}
1289

1290
// deleteVpcRouter delete router to connect logical switches in vpc
1291
func (c *Controller) deleteVpcRouter(lr string) error {
×
1292
        return c.OVNNbClient.DeleteLogicalRouter(lr)
×
1293
}
×
1294

1295
func (c *Controller) handleAddVpcExternalSubnet(key, subnet string) error {
×
1296
        cachedSubnet, err := c.subnetsLister.Get(subnet)
×
1297
        if err != nil {
×
1298
                klog.Error(err)
×
1299
                return err
×
1300
        }
×
1301
        lrpEipName := fmt.Sprintf("%s-%s", key, subnet)
×
1302
        cachedEip, err := c.ovnEipsLister.Get(lrpEipName)
×
1303
        var needCreateEip bool
×
1304
        if err != nil {
×
1305
                if !k8serrors.IsNotFound(err) {
×
1306
                        klog.Error(err)
×
1307
                        return err
×
1308
                }
×
1309
                needCreateEip = true
×
1310
        }
1311
        var v4ip, v6ip, mac string
×
1312
        klog.V(3).Infof("create vpc lrp eip %s", lrpEipName)
×
1313
        if needCreateEip {
×
1314
                if v4ip, v6ip, mac, err = c.acquireIPAddress(subnet, lrpEipName, lrpEipName); err != nil {
×
1315
                        klog.Errorf("failed to acquire ip address for lrp eip %s, %v", lrpEipName, err)
×
1316
                        return err
×
1317
                }
×
1318
                if err := c.createOrUpdateOvnEipCR(lrpEipName, subnet, v4ip, v6ip, mac, util.OvnEipTypeLRP); err != nil {
×
1319
                        klog.Errorf("failed to create ovn eip for lrp %s: %v", lrpEipName, err)
×
1320
                        return err
×
1321
                }
×
1322
        } else {
×
1323
                v4ip = cachedEip.Spec.V4Ip
×
1324
                mac = cachedEip.Spec.MacAddress
×
1325
        }
×
1326
        if v4ip == "" || mac == "" {
×
1327
                err := fmt.Errorf("lrp '%s' ip or mac should not be empty", lrpEipName)
×
1328
                klog.Error(err)
×
1329
                return err
×
1330
        }
×
1331
        // init lrp gw chassis group
1332
        chassises := []string{}
×
1333
        sel, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: map[string]string{util.ExGatewayLabel: "true"}})
×
1334
        gwNodes, err := c.nodesLister.List(sel)
×
1335
        if err != nil {
×
1336
                klog.Errorf("failed to list external gw nodes, %v", err)
×
1337
                return err
×
1338
        }
×
1339
        for _, gwNode := range gwNodes {
×
1340
                annoChassisName := gwNode.Annotations[util.ChassisAnnotation]
×
1341
                if annoChassisName == "" {
×
1342
                        err := fmt.Errorf("node %s has no chassis annotation, kube-ovn-cni not ready", gwNode.Name)
×
1343
                        klog.Error(err)
×
1344
                        return err
×
1345
                }
×
1346
                klog.Infof("get node %s chassis: %s", gwNode.Name, annoChassisName)
×
1347
                chassis, err := c.OVNSbClient.GetChassis(annoChassisName, false)
×
1348
                if err != nil {
×
1349
                        klog.Errorf("failed to get node %s chassis: %s, %v", gwNode.Name, annoChassisName, err)
×
1350
                        return err
×
1351
                }
×
1352
                chassises = append(chassises, chassis.Name)
×
1353
        }
1354

1355
        if len(chassises) == 0 {
×
1356
                err := errors.New("no external gw nodes")
×
1357
                klog.Error(err)
×
1358
                return err
×
1359
        }
×
1360

1361
        v4ipCidr, err := util.GetIPAddrWithMask(v4ip, cachedSubnet.Spec.CIDRBlock)
×
1362
        if err != nil {
×
1363
                klog.Error(err)
×
1364
                return err
×
1365
        }
×
1366
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1367
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1368

×
1369
        if err := c.OVNNbClient.CreateLogicalPatchPort(subnet, key, lspName, lrpName, v4ipCidr, mac, chassises...); err != nil {
×
1370
                klog.Errorf("failed to connect router '%s' to external: %v", key, err)
×
1371
                return err
×
1372
        }
×
1373
        return nil
×
1374
}
1375

1376
func (c *Controller) handleDeleteVpcStaticRoute(key string) error {
×
1377
        vpc, err := c.vpcsLister.Get(key)
×
1378
        if err != nil {
×
1379
                if k8serrors.IsNotFound(err) {
×
1380
                        return nil
×
1381
                }
×
1382
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1383
                return err
×
1384
        }
1385
        needUpdate := false
×
1386
        newStaticRoutes := make([]*kubeovnv1.StaticRoute, 0, len(vpc.Spec.StaticRoutes))
×
1387
        for _, route := range vpc.Spec.StaticRoutes {
×
1388
                if route.ECMPMode != util.StaticRouteBfdEcmp {
×
1389
                        newStaticRoutes = append(newStaticRoutes, route)
×
1390
                        needUpdate = true
×
1391
                }
×
1392
        }
1393
        // keep non ecmp bfd routes
1394
        vpc.Spec.StaticRoutes = newStaticRoutes
×
1395
        if needUpdate {
×
1396
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Update(context.Background(), vpc, metav1.UpdateOptions{}); err != nil {
×
1397
                        klog.Errorf("failed to update vpc spec static route %s, %v", vpc.Name, err)
×
1398
                        return err
×
1399
                }
×
1400
        }
1401
        if err = c.patchVpcBfdStatus(vpc.Name); err != nil {
×
1402
                klog.Errorf("failed to patch vpc %s, %v", vpc.Name, err)
×
1403
                return err
×
1404
        }
×
1405
        return nil
×
1406
}
1407

1408
func (c *Controller) handleDelVpcExternalSubnet(key, subnet string) error {
×
1409
        lspName := fmt.Sprintf("%s-%s", subnet, key)
×
1410
        lrpName := fmt.Sprintf("%s-%s", key, subnet)
×
1411
        klog.Infof("delete vpc lrp %s", lrpName)
×
1412
        if err := c.OVNNbClient.RemoveLogicalPatchPort(lspName, lrpName); err != nil {
×
1413
                klog.Errorf("failed to disconnect router '%s' to external, %v", key, err)
×
1414
                return err
×
1415
        }
×
1416
        if err := c.config.KubeOvnClient.KubeovnV1().OvnEips().Delete(context.Background(), lrpName, metav1.DeleteOptions{}); err != nil {
×
1417
                if !k8serrors.IsNotFound(err) {
×
1418
                        klog.Errorf("failed to delete ovn eip %s, %v", lrpName, err)
×
1419
                        return err
×
1420
                }
×
1421
        }
1422
        if err := c.OVNNbClient.DeleteBFDByDstIP(lrpName, ""); err != nil {
×
1423
                klog.Error(err)
×
1424
                return err
×
1425
        }
×
1426
        return nil
×
1427
}
1428

1429
func (c *Controller) patchVpcBfdStatus(key string) error {
×
1430
        cachedVpc, err := c.vpcsLister.Get(key)
×
1431
        if err != nil {
×
1432
                if k8serrors.IsNotFound(err) {
×
1433
                        return nil
×
1434
                }
×
1435
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1436
                return err
×
1437
        }
1438

1439
        if cachedVpc.Status.EnableBfd != cachedVpc.Spec.EnableBfd {
×
1440
                status := cachedVpc.Status.DeepCopy()
×
1441
                status.EnableExternal = cachedVpc.Spec.EnableExternal
×
1442
                status.EnableBfd = cachedVpc.Spec.EnableBfd
×
1443
                bytes, err := status.Bytes()
×
1444
                if err != nil {
×
1445
                        klog.Errorf("failed to marshal vpc status: %v", err)
×
1446
                        return err
×
1447
                }
×
1448
                if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1449
                        cachedVpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1450
                        klog.Error(err)
×
1451
                        return err
×
1452
                }
×
1453
        }
1454
        return nil
×
1455
}
1456

1457
func (c *Controller) getRouteTablesByVpc(vpc *kubeovnv1.Vpc) map[string][]*kubeovnv1.StaticRoute {
×
1458
        rtbs := make(map[string][]*kubeovnv1.StaticRoute)
×
1459
        for _, route := range vpc.Spec.StaticRoutes {
×
1460
                rtbs[route.RouteTable] = append(rtbs[route.RouteTable], route)
×
1461
        }
×
1462
        return rtbs
×
1463
}
1464

1465
func (c *Controller) updateVpcExternalStatus(key string, enableExternal bool) error {
×
1466
        cachedVpc, err := c.vpcsLister.Get(key)
×
1467
        if err != nil {
×
1468
                klog.Errorf("failed to get vpc %s, %v", key, err)
×
1469
                return err
×
1470
        }
×
1471
        vpc := cachedVpc.DeepCopy()
×
1472
        vpc.Status.EnableExternal = vpc.Spec.EnableExternal
×
1473
        vpc.Status.EnableBfd = vpc.Spec.EnableBfd
×
1474

×
1475
        if enableExternal {
×
1476
                sort.Strings(vpc.Spec.ExtraExternalSubnets)
×
1477
                vpc.Status.ExtraExternalSubnets = vpc.Spec.ExtraExternalSubnets
×
1478
        } else {
×
1479
                vpc.Status.ExtraExternalSubnets = nil
×
1480
        }
×
1481

1482
        bytes, err := vpc.Status.Bytes()
×
1483
        if err != nil {
×
1484
                klog.Errorf("failed to get vpc bytes, %v", err)
×
1485
                return err
×
1486
        }
×
1487
        if _, err = c.config.KubeOvnClient.KubeovnV1().Vpcs().Patch(context.Background(),
×
1488
                vpc.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
×
1489
                klog.Errorf("failed to patch vpc %s, %v", key, err)
×
1490
                return err
×
1491
        }
×
1492

1493
        return nil
×
1494
}
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