Skip to content

Conversation

@fedepaol
Copy link
Member

Following the frr splitting design proposal, here we add a new BGP implementation, whcih is based on FRR-K8s. What this implemetation does is to generate the corresponding frr-k8s configuration so that the FRR-k8s daemon can configure FRR.

This will allow external actors to add additional FRR knobs that are not related to MetalLB.

Those might be required also by the frrk8s implementation.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
@fedepaol fedepaol changed the title Enable a frr-k8s based bgp backend WIP: Enable a frr-k8s based bgp backend Nov 13, 2023
@fedepaol fedepaol force-pushed the withfrrk8s_topush branch 8 times, most recently from d5dc257 to 8b6d342 Compare November 14, 2023 20:08
@fedepaol fedepaol changed the title WIP: Enable a frr-k8s based bgp backend Enable a frr-k8s based bgp backend Nov 15, 2023
Comment on lines 24 to 25
metallbNamespace string
currentNode string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these could be just namespace, node

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change them to what they mean: targetNamespace and nodeToConfigure or something along that line
wdyt?

toAdd := frrv1beta1.BFDProfile{
Name: bfd.Name,
}
if bfd.ReceiveInterval != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do the ifs here do something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they prevent the speaker from panicking if the value is nil

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I'm missing something but how when both are pointers (toAdd.x and bfd.x)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah dang you are right!

newConfig.Spec.BGP.Routers = append(newConfig.Spec.BGP.Routers, toAdd)
}

for _, bfd := range sm.bfdProfiles {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you leave a short comment that sm.BfdProfiles is already sorted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move the sorting here, I think it makes more sense

}
neighbor.ToAdvertise.PrefixesWithCommunity = toAdvertiseWithCommunity(prefixesForCommunity)
neighbor.ToAdvertise.PrefixesWithLocalPref = toAdvertiseWithLocalPref(prefixesForLocalPref)
sort.Slice(neighbor.ToAdvertise.Allowed.Prefixes, func(i, j int) bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use sort.Strings

res = append(res, frrv1beta1.CommunityPrefixes{Community: c, Prefixes: prefixes})
}
sort.Slice(res, func(i, j int) bool {
return res[i].Community > res[j].Community
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i < j for increasing order

}
if err := frrk8sController.SetupWithManager(mgr); err != nil {
level.Error(c.logger).Log("error", err, "unable to create controller", "node")
fmt.Println("FEDE error")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

Reload: make(chan event.GenericEvent),
}
if err := frrk8sController.SetupWithManager(mgr); err != nil {
level.Error(c.logger).Log("error", err, "unable to create controller", "node")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "node" -> "frr-k8s"

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/metallb/frr-k8s/config/default/?timeout=120&ref=main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should remember setting this to != main before merging this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll have to do a tag first

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but adapted the type. We need it because the controller serves as webhook endpoint and it must know what needs to validate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@fedepaol fedepaol force-pushed the withfrrk8s_topush branch 2 times, most recently from 657d8af to 166dd6c Compare November 17, 2023 16:35
configChangedCallback func(interface{})
}

func (sm *sessionManager) SetEventListener(callback func(interface{})) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you saw in the next commit how the session manager is buried into the speaker abstraction and it's not easy to extract (unless we want to have a singleton variable as part of the module, which I am not sure I like).

yep let's keep it that way

Here we set a listener that listens to event. The name does not seem terrible to me, but I can switch to callback if you want.

I think it makes a bit more sense since we don't listen here, I guess SetEventCallback?


func (sm *sessionManager) SyncExtraInfo(extraInfo string) error {
if extraInfo != "" {
return errors.New("extra info not supported in frr-k8s mode")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with that but we should remember to document it (more like "highlighting" since this mode unlocks more frr capabilities) when we get to it

toAdd := frrv1beta1.BFDProfile{
Name: bfd.Name,
}
if bfd.ReceiveInterval != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I'm missing something but how when both are pointers (toAdd.x and bfd.x)?

Comment on lines +326 to +316
toNotify := sm.desiredConfig.DeepCopy()
sm.configChangedCallback(*toNotify)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what I'm more worried about is us creating a ton of events that the controller will process (polluting the logs at a minimum) + it'd be nice to save a few api calls (since every speaker generates them).

return ctrl.Result{}, nil
}

toApply := &frrv1beta1.FRRConfiguration{ObjectMeta: metav1.ObjectMeta{Name: r.desiredConfiguration.Name, Namespace: r.desiredConfiguration.Namespace}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you indent this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

g.Expect(err).ToNot(HaveOccurred())
m, err := manager.New(cfg, manager.Options{Metrics: metricsserver.Options{BindAddress: "0"}})
g.Expect(err).ToNot(HaveOccurred())

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we start the test with a "stale" config and see that the controller first deletes it (since desired == nil)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

The real reason why we need this, is to be able to inject a listener
that listens to generated frrk8s changes. It has to be a listener
because it has to added AFTER the creation of the manager, that in turn
is passed to the various controllers.

In order to keep the event generic, we pass an empty interface to the
callback, so the consumer can assert the type and handle the event
accordingly to its type.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
Here we add another bgp session manager implementation where starting
from the exposed interface we generate an FRR-K8s configuration tied to
the running node.

The consuming side is notified via a callback.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
We expose a method that gets short-circuited with the protocol
implementation. This will allow us to set a listener to the frrk8s
implementation.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
@fedepaol fedepaol force-pushed the withfrrk8s_topush branch 3 times, most recently from 61c78f7 to 9b95287 Compare November 22, 2023 11:38
The controller is driven in two ways:
- it listens for changes on the frrconfig related to the metallb
  namespace and the node the controller is running on
- it listen for changes in the UpdateConfig method

Given calling UpdateConfig is asynchronous, the passed data is stored
and an event is sent to a channel so it can be processed inside
the reconciliation loop.

The notification callback has empty interface as a field so this
callback an be used as a bgp event listener.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
We need to:
- pass the implementation to the controller, so services and config can
  be translated
- expose a way for the controller to set an event listener, so the
  implementation can notify the contorller of a new event to process

Given this circular dependency, the controller sets the listener after
its creation, to the already existing frrk8s implementation.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
@fedepaol fedepaol force-pushed the withfrrk8s_topush branch 2 times, most recently from 3251ca4 to abd1b2b Compare November 22, 2023 14:35
Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
We don't have password support yet, but we want to see a basic smoke
test working. Here we run a service connectivity test with
ebgp-single-hop which is the container configured without password.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
Adding a new lane that just deploys the machinery and runs the smoke
test we added.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
@oribon
Copy link
Member

oribon commented Nov 27, 2023

lgtm 😄

@fedepaol fedepaol added this pull request to the merge queue Nov 27, 2023
Merged via the queue into metallb:main with commit 91a7d56 Nov 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants