-
Notifications
You must be signed in to change notification settings - Fork 1k
Add peer selector to BGPAdvertisement object #1171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
da90110 to
2b6dd7f
Compare
|
I think it's worth waiting until #942 settles, it may have substantial impacts on this. |
0be9247 to
2c53451
Compare
335e246 to
17b13e6
Compare
d6499c0 to
5c04fda
Compare
internal/bgp/frr/config.go
Outdated
| address-family ipv6 unicast | ||
| neighbor {{$n.Addr}} activate | ||
| neighbor {{$n.Addr}} route-map {{$n.Addr}}-in in | ||
| neighbor {{$n.Addr}} route-map {{$n.Addr}}-out out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have this below already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only added if the neighbor has advertisements. Not setting a route-map for a neighbor without advertisements advertises all ips to that neighbor, while setting it for the neighbor without actually creating it is doing the filtering.
internal/bgp/native/native.go
Outdated
| err := validate(adv) | ||
| if err != nil { | ||
| return err | ||
| addAdv := true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
e2etest/bgptests/bgp.go
Outdated
| table.DescribeTable("A service IP will not be advertised to peers outside the BGPAdvertisement peers list", func(addressRange string, ipFamily ipfamily.Family) { | ||
| var testContainers []*frrcontainer.FRR | ||
| for _, c := range FRRContainers { | ||
| if !strings.Contains(c.Name, "multi") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this? If you want to pick just two, be explicit and take the first two of the array (after the len check)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still unresolved I think?
e2etest/bgptests/bgp.go
Outdated
| } | ||
| } | ||
| }, | ||
| table.Entry("IPV4", "192.168.10.0/24", ipfamily.IPv4), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a test where you have the same service, two different advertisements advertised to two different peers (you can check the community for example)
|
|
||
| if len(crdAd.Spec.Peers) > 0 { | ||
| ad.Peers = make([]string, 0, len(crdAd.Spec.Peers)) | ||
| ad.Peers = append(ad.Peers, crdAd.Spec.Peers...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you extend the unit tests with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, this part is not covered by unit tests (I think).
|
Looks good overall, need to do another pass and focus on the frr config. |
cf16b56 to
2b98cc6
Compare
7cf80c7 to
e61780e
Compare
b2986a3 to
4090676
Compare
internal/bgp/frr/config.go
Outdated
| route-map {{$n.Addr}}-out permit {{counter $n.Addr}} | ||
| match ip address prefix-list {{.LocalPreference}}-v4localpref-prefixes | ||
| set local-preference {{.LocalPreference}} | ||
| match ip address prefix-list {{$n.Addr}}-{{$pl.LocalPreference}}-v4localpref-prefixes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
having a prefix per addr per local pref is gonna make the configuration explode. How about a custom function isPrefixValidForNeighbor that checks that (all the advertisements configured for the neigh) and then we add the route map or not? Same for community.
internal/bgp/frr/config.go
Outdated
| {{- end }} | ||
| {{- range $.PrefixesV4ForCommunity }} | ||
| {{- if eq ($a.IPFamily.String) "ipv4" }} | ||
| ip prefix-list {{$n.Addr}}-plv4 permit {{$a.Prefix}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we create multiple records of the shape of:
route-map 10.2.2.255-out permit 2
match ip address prefix-list 10.2.2.255-plv4
on-match next
route-map 10.2.2.255-out permit 5
match ip address prefix-list 10.2.2.255-plv4
on-match next
I think building one prefix-list with all the addresses and doing the route map once will be more readable, and will add probably less churn on frr (because all the route maps are different, so I don't think it's gonna squash them).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest building such list in go and adding to the structure, so you can just leverage it.
| address-family ipv4 unicast | ||
| neighbor {{$n.Addr}} activate | ||
| neighbor {{$n.Addr}} route-map {{$n.Addr}}-in in | ||
| neighbor {{$n.Addr}} route-map {{$n.Addr}}-out out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this if we don't have any advertisement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if the neighbor doesn't have a route-map all ips are advertised to that neighbor, while setting it for the neighbor without actually creating it, is doing the filtering.
internal/bgp/frr/frr.go
Outdated
| found := false | ||
| for _, peer := range adv.Peers { | ||
| if peer == s.name { | ||
| found = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you can break here
internal/bgp/native/native.go
Outdated
| found := false | ||
| for _, peer := range adv.Peers { | ||
| if peer == s.name { | ||
| found = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
e2etest/bgptests/bgp.go
Outdated
| err = ConfigUpdater.Update(resources) | ||
| framework.ExpectNoError(err) | ||
|
|
||
| for _, c := range FRRContainers { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my suggestoin above to pick named peers.
For the notAdvertised, I would add a function that filters FRRContainers out of a container and iterate over those.
e2etest/bgptests/bgp.go
Outdated
| } | ||
|
|
||
| ginkgo.By("checking same service advertised through two different advertisements to two different peers") | ||
| for i := 0; i < 2; i++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same considerations as above, following by index is hard. Be explicit on naming.
One pool, PeerForAdv1 , PeerForAdv2, etc
Also, this should be a different test and not a by in the same. It's already long.
e2etest/bgptests/bgp.go
Outdated
| } | ||
| } | ||
|
|
||
| for i := 0; i < 2; i++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, avoid indexes, give peers a name
e2etest/bgptests/validate.go
Outdated
| } | ||
| } | ||
|
|
||
| func validateServiceInRoutesForCommunity(c *frrcontainer.FRR, community string, family ipfamily.Family, svc *corev1.Service) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put the eventually inside here, it will make the test body lighter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i use this function twice, once the eventually expects no error, the other time it expects an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can wrap it with two functinos. Also, you may want to check that the error is what we expect with MatchError(ContainSubstring("not in routes")
e2etest/bgptests/validate.go
Outdated
| ingressIP := e2eservice.GetIngressPoint(&ip) | ||
|
|
||
| Eventually(func() bool { | ||
| frrRoutesV4, frrRoutesV6, err := frr.Routes(c) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use frr.RoutesForFamily here (it's new)
8347a48 to
1c64bfe
Compare
internal/bgp/frr/config.go
Outdated
| {{- range $a := .Advertisements }} | ||
| {{- range $pl := prefixesForLocalPref $a $n.IPFamily.String }} | ||
| {{- range $p := $pl.Prefixes }} | ||
| {{ipCmd $n.IPFamily.String}} prefix-list {{$n.Addr}}-{{$pl.LocalPreference}}-{{ipCmd $n.IPFamily.String}}-localpref-prefixes permit {{$p}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ipCmd can take IPFamily instead of string
also, how about renaming ipCmd to frrIPFamily?
internal/bgp/frr/config.go
Outdated
| route-map {{$n.Addr}}-out permit {{counter $n.Addr}} | ||
| match ipv6 address prefix-list {{.LocalPreference}}-v6localpref-prefixes | ||
| set local-preference {{.LocalPreference}} | ||
| match {{ipCmd $n.IPFamily.String}} address prefix-list {{$n.Addr}}-{{$pl.LocalPreference}}-{{ipCmd $n.IPFamily.String}}-localpref-prefixes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should hide names like
{{$n.Addr}}-{{$pl.LocalPreference}}-{{ipCmd $n.IPFamily.String}}-localpref-prefixes behind a function that takes the neighbour and the local pref.
Something like localPrefPrefixList (neighbour, localpref)
Same for the others: communityPrefixListName() , allowedPrefixListName.
Wdyt?
internal/bgp/frr/config.go
Outdated
| } | ||
| return "ip" | ||
| }, | ||
| "prefixesForLocalPref": func(advConfig advertisementConfig, ipFamily string) []localPrefPrefixes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more "localPrefPrefixesForAdv". Also, can't you use the ipfamily in advertisementConfig?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i need the neighbor ip family to avoid mixing ipv4 prefixes with neighbor ipv6 addresses
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which is something we do not support today right? So that can be solved by filtering when we build the adv list for neighbour?
internal/bgp/frr/config.go
Outdated
| } | ||
| return advConfig.PrefixesV6ForLocalPref | ||
| }, | ||
| "prefixesForCommunity": func(advConfig advertisementConfig, ipFamily string) []communityPrefixes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
internal/bgp/frr/frr.go
Outdated
| addPrefixForLocalPref(prefixesV4ForLocalPref, prefixesV6ForLocalPref, adv.Prefix.String(), family, adv.LocalPref) | ||
| } | ||
|
|
||
| // The maps must be converted in sorted slice to make the rendering stable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be embedded in the advertisementConfig literal init above (same for prefixesV4ForLocalPref, moving the addPrefixForLocalPref before the init)
e2etest/bgptests/bgppeer_selector.go
Outdated
|
|
||
| ginkgo.By(fmt.Sprintf("setting peer selector for addresspool number 1 to peer %s", peerForAdv1.Name)) | ||
| peer := "" | ||
| for _, p := range resources.Peers { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is repeating and distracting. How about initializing peer1 / peer2 in beforeEach (and peers: metallb.PeersForContainers(FRRContainers, ipFamily))?
I'd rename peerForAdv1 to something else too, because we must distinguish between peer - the container and peer - the BGPPeer we give to metallb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need the ipfamily for PeersForContainers which we don't have in before each
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh right. Wrapping it in a function?
e2etest/bgptests/bgppeer_selector.go
Outdated
| ginkgo.By(fmt.Sprintf("checking service in routes of peer %s for community 1", c.Name)) | ||
| Eventually(func() error { | ||
| return validateServiceInRoutesForCommunity(c, community1, ipFamily, svc) | ||
| }, 4*time.Minute, 1*time.Second).Should(BeNil()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With errors we use Should(HaveOccurred)
1a0f766 to
19d6c2c
Compare
A service using an IP from a pool with a list of bgp-peers will not advertise the IP to peers outside that list.
A service IP from a pool with a list of bgp-peers will not be advertised to peers outside that list.
Need to fix the test config files due to the change in the frr config to support the new BGP Peer Selector.
19d6c2c to
3d934fd
Compare
|
LGTM |
LGTM. Just a few nits I spotted, I wouldn't take issue merging this PR without addressing them. |
Ack, let's discuss them offline so we can follow up |
A service using an IP from a pool with a list of bgp-peers will not advertise the IP to peers outside that list.
In order to be able to select a specific set of peers for a given pool we need to: