Skip to content

Commit 6b6d570

Browse files
authored
Merge pull request #5099 from jcaamano/egressip-bgp-v2
Add support to advertise EIPs for UDNs
2 parents d66a807 + 9a6ef80 commit 6b6d570

File tree

6 files changed

+324
-142
lines changed

6 files changed

+324
-142
lines changed

go-controller/pkg/clustermanager/routeadvertisements/controller.go

Lines changed: 137 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,14 @@ var (
5757

5858
// Controller reconciles RouteAdvertisements
5959
type Controller struct {
60-
eipLister egressiplisters.EgressIPLister
61-
frrLister frrlisters.FRRConfigurationLister
62-
nadLister nadlisters.NetworkAttachmentDefinitionLister
63-
nodeLister corelisters.NodeLister
64-
raLister ralisters.RouteAdvertisementsLister
60+
wf *factory.WatchFactory
61+
62+
eipLister egressiplisters.EgressIPLister
63+
frrLister frrlisters.FRRConfigurationLister
64+
nadLister nadlisters.NetworkAttachmentDefinitionLister
65+
nodeLister corelisters.NodeLister
66+
raLister ralisters.RouteAdvertisementsLister
67+
namespaceLister corelisters.NamespaceLister
6568

6669
frrClient frrclientset.Interface
6770
nadClient nadclientset.Interface
@@ -72,6 +75,7 @@ type Controller struct {
7275
nadController controllerutil.Controller
7376
nodeController controllerutil.Controller
7477
raController controllerutil.Controller
78+
nsController controllerutil.Controller
7579

7680
nm networkmanager.Interface
7781
}
@@ -83,15 +87,17 @@ func NewController(
8387
ovnClient *util.OVNClusterManagerClientset,
8488
) *Controller {
8589
c := &Controller{
86-
eipLister: wf.EgressIPInformer().Lister(),
87-
frrLister: wf.FRRConfigurationsInformer().Lister(),
88-
nadLister: wf.NADInformer().Lister(),
89-
nodeLister: wf.NodeCoreInformer().Lister(),
90-
raLister: wf.RouteAdvertisementsInformer().Lister(),
91-
frrClient: ovnClient.FRRClient,
92-
nadClient: ovnClient.NetworkAttchDefClient,
93-
raClient: ovnClient.RouteAdvertisementsClient,
94-
nm: nm,
90+
wf: wf,
91+
eipLister: wf.EgressIPInformer().Lister(),
92+
frrLister: wf.FRRConfigurationsInformer().Lister(),
93+
nadLister: wf.NADInformer().Lister(),
94+
nodeLister: wf.NodeCoreInformer().Lister(),
95+
raLister: wf.RouteAdvertisementsInformer().Lister(),
96+
namespaceLister: wf.NamespaceInformer().Lister(),
97+
frrClient: ovnClient.FRRClient,
98+
nadClient: ovnClient.NetworkAttchDefClient,
99+
raClient: ovnClient.RouteAdvertisementsClient,
100+
nm: nm,
95101
}
96102

97103
handleError := func(key string, errorstatus error) error {
@@ -153,14 +159,24 @@ func NewController(
153159

154160
eipConfig := &controllerutil.ControllerConfig[eiptypes.EgressIP]{
155161
RateLimiter: workqueue.DefaultTypedControllerRateLimiter[string](),
156-
Reconcile: c.reconcileEgressIP,
162+
Reconcile: c.reconcileEgressIPs,
157163
Threadiness: 1,
158164
Informer: wf.EgressIPInformer().Informer(),
159165
Lister: wf.EgressIPInformer().Lister().List,
160166
ObjNeedsUpdate: egressIPNeedsUpdate,
161167
}
162168
c.eipController = controllerutil.NewController("clustermanager routeadvertisements egressip controller", eipConfig)
163169

170+
nsConfig := &controllerutil.ControllerConfig[corev1.Namespace]{
171+
RateLimiter: workqueue.DefaultTypedControllerRateLimiter[string](),
172+
Reconcile: c.reconcileEgressIPs,
173+
Threadiness: 1,
174+
Informer: wf.NamespaceInformer().Informer(),
175+
Lister: wf.NamespaceInformer().Lister().List,
176+
ObjNeedsUpdate: nsNeedsUpdate,
177+
}
178+
c.nsController = controllerutil.NewController("clustermanager routeadvertisements namespace controller", nsConfig)
179+
164180
return c
165181
}
166182

@@ -171,6 +187,7 @@ func (c *Controller) Start() error {
171187
c.frrController,
172188
c.nadController,
173189
c.nodeController,
190+
c.nsController,
174191
c.raController,
175192
)
176193
}
@@ -181,20 +198,33 @@ func (c *Controller) Stop() {
181198
c.frrController,
182199
c.nadController,
183200
c.nodeController,
201+
c.nsController,
184202
c.raController,
185203
)
186-
klog.Infof("Cluster manager routeadvertisements stoppedu")
204+
klog.Infof("Cluster manager routeadvertisements stopped")
187205
}
188206

189207
func (c *Controller) ReconcileNetwork(_ string, old, new util.NetInfo) {
190-
// This controller already listens on NAD events however we skip NADs
191-
// pointing to networks that network manager is still not aware of; so we
192-
// only need to signal the reconciliation of new networks. Reconcile one of
193-
// the NADs of the network to do s
194-
if new == nil || old != nil {
195-
return
196-
}
197-
c.nadController.Reconcile(new.GetNADs()[0])
208+
// This controller already listens on NAD events but there is two additional
209+
// scenarios we need to cover for:
210+
// - for newly created networks, we need to wait until network manager is
211+
// aware of them.
212+
// - if the namespaces served by a network change.
213+
oldNamespaces, newNamespaces := sets.New[string](), sets.New[string]()
214+
if old != nil {
215+
oldNamespaces.Insert(old.GetNADNamespaces()...)
216+
}
217+
if new != nil {
218+
newNamespaces.Insert(new.GetNADNamespaces()...)
219+
}
220+
if new != nil && !newNamespaces.Equal(oldNamespaces) {
221+
// we use one of the NADs of the network to reconcile it
222+
c.nadController.Reconcile(new.GetNADs()[0])
223+
// if the namespaces served by a network changed, it is possible that
224+
// those namespaces are served or no longer served by the default
225+
// network, so reconcile it as well
226+
c.nadController.Reconcile(config.Kubernetes.OVNConfigNamespace + "/" + types.DefaultNetworkName)
227+
}
198228
}
199229

200230
// Reconcile RouteAdvertisements. For each selected FRRConfiguration and node,
@@ -206,7 +236,8 @@ func (c *Controller) ReconcileNetwork(_ string, old, new util.NetInfo) {
206236
//
207237
// - If EgressIP advertisements are enabled, the generated FRRConfiguration will
208238
// announce from the node the EgressIPs allocated to it on the matching target
209-
// VRFs.
239+
// VRFs. Selected EgressIP are those that serve the same namespaces as the
240+
// selected networks. Target VRF `auto` is not supported for EgressIPs.
210241
//
211242
// - If pod network advertisements are enabled, the generated FRRConfiguration
212243
// will import the target VRFs on the selected networks as required.
@@ -222,7 +253,7 @@ func (c *Controller) ReconcileNetwork(_ string, old, new util.NetInfo) {
222253
// Finally, it will update the status of the RouteAdvertisements.
223254
//
224255
// The controller processes selected events of RouteAdvertisements,
225-
// FRRConfigurations, Nodes, EgressIPs and NADs.
256+
// FRRConfigurations, Nodes, EgressIPs, NADs and namespaces.
226257
func (c *Controller) reconcile(name string) error {
227258
startTime := time.Now()
228259
klog.V(5).Infof("Syncing routeadvertisements %q", name)
@@ -294,6 +325,11 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
294325
return nil, nil, nil
295326
}
296327

328+
advertisements := sets.New(ra.Spec.Advertisements...)
329+
if advertisements.Has(ratypes.EgressIP) && ra.Spec.TargetVRF == "auto" {
330+
return nil, nil, fmt.Errorf("%w: advertising EgressIP not supported with TargetVRF set to 'auto'", errConfig)
331+
}
332+
297333
// if we are matching on the well known default network label, create an
298334
// internal nad for it if it doesn't exist
299335
if matchesDefaultNetworkLabel(ra.Spec.NetworkSelector) {
@@ -369,7 +405,6 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
369405
if err != nil {
370406
return nil, nil, err
371407
}
372-
advertisements := sets.New(ra.Spec.Advertisements...)
373408
if !nodeSelector.Empty() && advertisements.Has(ratypes.PodNetwork) {
374409
return nil, nil, fmt.Errorf("%w: node selector cannot be specified if pod network is advertised", errConfig)
375410
}
@@ -444,15 +479,15 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
444479

445480
// helper to gather egress ips and cache during reconcile
446481
// TODO perhaps cache across reconciles as well
447-
var nodeEgressIPs map[string][]string
448-
getEgressIPs := func(nodeName string) ([]string, error) {
449-
if nodeEgressIPs == nil {
450-
nodeEgressIPs, err = c.getEgressIPsByNode()
482+
var eipsByNodesByNetworks map[string]map[string]sets.Set[string]
483+
getEgressIPsByNode := func(nodeName string) (map[string]sets.Set[string], error) {
484+
if eipsByNodesByNetworks == nil {
485+
eipsByNodesByNetworks, err = c.getEgressIPsByNodesByNetworks(networkSet)
451486
if err != nil {
452487
return nil, err
453488
}
454489
}
455-
return nodeEgressIPs[nodeName], nil
490+
return eipsByNodesByNetworks[nodeName], nil
456491
}
457492

458493
// helper to gather host subnets and egress ips as prefixes
@@ -469,13 +504,11 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
469504
// gather EgressIPs
470505
var eips []string
471506
if advertisements.Has(ratypes.EgressIP) {
472-
if network != types.DefaultNetworkName {
473-
return nil, fmt.Errorf("%w: can't advertise EgressIP in selected non default network %q: %w", errConfig, network, err)
474-
}
475-
eips, err = getEgressIPs(nodeName)
507+
eipsByNode, err := getEgressIPsByNode(nodeName)
476508
if err != nil {
477509
return nil, err
478510
}
511+
eips = eipsByNode[network].UnsortedList()
479512
}
480513

481514
prefixes := make([]string, 0, len(subnets)+len(eips))
@@ -500,8 +533,13 @@ func (c *Controller) generateFRRConfigurations(ra *ratypes.RouteAdvertisements)
500533
// ordered
501534
slices.Sort(selectedNetworks.hostNetworkSubnets[network])
502535
}
503-
// ordered
504-
slices.Sort(selectedNetworks.hostSubnets)
536+
// order, dedup
537+
selectedNetworks.hostSubnets = sets.List(sets.New(selectedNetworks.hostSubnets...))
538+
539+
// if there is no prefixes to advertise for this node, skip it
540+
if len(selectedNetworks.hostSubnets) == 0 {
541+
continue
542+
}
505543

506544
matchedNetworks := sets.New[string]()
507545
for _, frrConfig := range frrConfigs {
@@ -985,26 +1023,69 @@ func (c *Controller) getOrCreateDefaultNetworkNAD() (*nadtypes.NetworkAttachment
9851023
)
9861024
}
9871025

988-
// getEgressIPsByNode iterates all existing egress IPs and returns them indexed
989-
// by node
990-
func (c *Controller) getEgressIPsByNode() (map[string][]string, error) {
1026+
// getEgressIPsByNodesByNetworks iterates all existing egress IPs that apply to
1027+
// any of the provided networks and returns a "node -> network -> eips"
1028+
// map.
1029+
func (c *Controller) getEgressIPsByNodesByNetworks(networks sets.Set[string]) (map[string]map[string]sets.Set[string], error) {
1030+
eipsByNodesByNetworks := map[string]map[string]sets.Set[string]{}
1031+
addEgressIPsByNodesByNetwork := func(eipsByNodes map[string]string, network string) {
1032+
for node, eip := range eipsByNodes {
1033+
if eipsByNodesByNetworks[node] == nil {
1034+
eipsByNodesByNetworks[node] = map[string]sets.Set[string]{}
1035+
}
1036+
if eipsByNodesByNetworks[node][network] == nil {
1037+
eipsByNodesByNetworks[node][network] = sets.New[string]()
1038+
}
1039+
eipsByNodesByNetworks[node][network].Insert(eip)
1040+
}
1041+
}
1042+
1043+
addEgressIPsByNodesByNetworkSelector := func(eipsByNodes map[string]string, namespaceSelector *metav1.LabelSelector) error {
1044+
nsSelector, err := metav1.LabelSelectorAsSelector(namespaceSelector)
1045+
if err != nil {
1046+
return err
1047+
}
1048+
selected, err := c.namespaceLister.List(nsSelector)
1049+
if err != nil {
1050+
return err
1051+
}
1052+
for _, namespace := range selected {
1053+
namespaceNetwork := c.nm.GetActiveNetworkForNamespaceFast(namespace.Name)
1054+
networkName := namespaceNetwork.GetNetworkName()
1055+
if networks.Has(networkName) {
1056+
addEgressIPsByNodesByNetwork(eipsByNodes, networkName)
1057+
}
1058+
}
1059+
return nil
1060+
}
1061+
9911062
eips, err := c.eipLister.List(labels.Everything())
9921063
if err != nil {
9931064
return nil, err
9941065
}
9951066

996-
eipsByNode := map[string][]string{}
9971067
for _, eip := range eips {
1068+
eipsByNodes := make(map[string]string, len(eip.Status.Items))
9981069
for _, item := range eip.Status.Items {
999-
if item.EgressIP == "" {
1070+
// skip unassigned EIPs
1071+
if item.EgressIP == "" || item.Node == "" {
10001072
continue
10011073
}
1074+
10021075
ip := item.EgressIP + util.GetIPFullMaskString(item.EgressIP)
1003-
eipsByNode[item.Node] = append(eipsByNode[item.Node], ip)
1076+
eipsByNodes[item.Node] = ip
1077+
}
1078+
if len(eipsByNodes) == 0 {
1079+
continue
1080+
}
1081+
1082+
err = addEgressIPsByNodesByNetworkSelector(eipsByNodes, &eip.Spec.NamespaceSelector)
1083+
if err != nil {
1084+
return nil, err
10041085
}
10051086
}
10061087

1007-
return eipsByNode, nil
1088+
return eipsByNodesByNetworks, nil
10081089
}
10091090

10101091
// isOwnUpdate checks if an object was updated by us last, as indicated by its
@@ -1055,12 +1136,13 @@ func nadNeedsUpdate(oldObj, newObj *nadtypes.NetworkAttachmentDefinition) bool {
10551136
func nodeNeedsUpdate(oldObj, newObj *corev1.Node) bool {
10561137
return oldObj == nil || newObj == nil ||
10571138
!reflect.DeepEqual(oldObj.Labels, newObj.Labels) ||
1058-
util.NodeSubnetAnnotationChanged(oldObj, newObj)
1139+
util.NodeSubnetAnnotationChanged(oldObj, newObj) ||
1140+
oldObj.Annotations[util.OvnNodeIfAddr] != newObj.Annotations[util.OvnNodeIfAddr]
10591141
}
10601142

10611143
func egressIPNeedsUpdate(oldObj, newObj *eiptypes.EgressIP) bool {
1062-
if oldObj != nil && newObj != nil && reflect.DeepEqual(oldObj.Status, newObj.Status) {
1063-
return false
1144+
if oldObj != nil && newObj != nil {
1145+
return !reflect.DeepEqual(oldObj.Status, newObj.Status) || !reflect.DeepEqual(oldObj.Spec.NamespaceSelector, newObj.Spec.NamespaceSelector)
10641146
}
10651147
if oldObj != nil && len(oldObj.Status.Items) > 0 {
10661148
return true
@@ -1071,6 +1153,12 @@ func egressIPNeedsUpdate(oldObj, newObj *eiptypes.EgressIP) bool {
10711153
return false
10721154
}
10731155

1156+
func nsNeedsUpdate(oldObj, newObj *corev1.Namespace) bool {
1157+
// we only care about label changes, added/deleted namespaces served by a
1158+
// UDN will already be reflected in a network update
1159+
return oldObj != nil && newObj != nil && !reflect.DeepEqual(oldObj.Labels, newObj.Labels)
1160+
}
1161+
10741162
func (c *Controller) reconcileFRRConfiguration(key string) error {
10751163
namespace, name, err := cache.SplitMetaNamespaceKey(key)
10761164
if err != nil {
@@ -1133,7 +1221,7 @@ func (c *Controller) reconcileNAD(key string) error {
11331221
return nil
11341222
}
11351223

1136-
func (c *Controller) reconcileEgressIP(_ string) error {
1224+
func (c *Controller) reconcileEgressIPs(string) error {
11371225
// reconcile RAs that advertise EIPs
11381226
ras, err := c.raLister.List(labels.Everything())
11391227
if err != nil {

0 commit comments

Comments
 (0)