Skip to content
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

Make the configuration conversion stable #1990

Merged
merged 2 commits into from Jun 29, 2023

Conversation

fedepaol
Copy link
Member

When translating the configuration coming from the k8s api to the internal one, the outcome depends on the order in which the cache returns us the various items. This may cause unnecessary reloads.

In order to avoid this, we pre-order the list of items to feed the conversion with.
The sorting is done in a conversion function in the controller (rather than the config package) in order to make it harder to forget to add the sorting action.

@fedepaol fedepaol force-pushed the stablereconcile branch 4 times, most recently from 4f440c8 to dfdddc1 Compare June 27, 2023 15:34
internal/bgp/frr/frr.go Outdated Show resolved Hide resolved
if err != nil {
t.Fatalf("Could not advertise prefix: %s", err)
}
err = session1.Set(adv1, adv2)

rand.Shuffle(len(advs), func(i, j int) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add more attempts to shuffle and check like in config_conversion_test.go so we'll be more confident?

Copy link
Member Author

Choose a reason for hiding this comment

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

This will require a bit of reworking, as I don't want to pay the FRR extra effort every iteration. Let me try to fix it.

internal/k8s/controllers/config_conversion_test.go Outdated Show resolved Hide resolved
v1.ConfigMap{})

if err != nil {
t.Fatalf("conversion failed")
Copy link
Contributor

Choose a reason for hiding this comment

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

err print missing

v1.ConfigMap{})

if err != nil {
t.Fatalf("conversion failed")
Copy link
Contributor

Choose a reason for hiding this comment

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

err print missing

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

}

if !reflect.DeepEqual(config, oldConfig) {
t.Fatalf("conversion is not stable, seed %d, %v\n %v\n", seed, spew.Sdump(config), spew.Sdump(oldConfig))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: let's add these small details so the output will be clearer

Suggested change
t.Fatalf("conversion is not stable, seed %d, %v\n %v\n", seed, spew.Sdump(config), spew.Sdump(oldConfig))
t.Fatalf("conversion is not stable, seed %d, got: %v\n expected: %v\n", seed, spew.Sdump(config), spew.Sdump(oldConfig))

Copy link
Member Author

Choose a reason for hiding this comment

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

The got - expected here makes less sense because we don't know which version is the expected (right) one. I'll name them current - previous

Copy link
Member Author

Choose a reason for hiding this comment

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

changed

if !reflect.DeepEqual(config, oldConfig) {
t.Fatalf("conversion is not stable, seed %d, %v\n %v\n", seed, spew.Sdump(config), spew.Sdump(oldConfig))
}
oldConfig = config
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can omit this assignment as the value doesn't change, and keep comparing to the same oldConfig.

Copy link
Member Author

Choose a reason for hiding this comment

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

yep, makes sense. Fixed

Comment on lines 87 to 98
cfg, err := toConfig(r.ValidateConfig,
ipAddressPools.Items,
[]metallbv1beta2.BGPPeer{},
[]metallbv1beta1.BFDProfile{},
[]metallbv1beta1.L2Advertisement{},
[]metallbv1beta1.BGPAdvertisement{},
addressPools.Items,
communities.Items,
map[string]corev1.Secret{},
[]corev1.Node{},
namespaces.Items,
corev1.ConfigMap{})
Copy link
Contributor

Choose a reason for hiding this comment

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

That's a little hard to read, plus we'll need to update the parameters list when a new CRD is added. why not reuse the ClusterResources struct, consume it as the input, and re-assign its fields with the sorted copy?
I would go even a step further and split toConfig into two methods, first only the logic of sorting the resources, so we'll have a sortedResources, and then call the config.For on it.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a little hard to read, plus we'll need to update the parameters list when a new CRD is added.

What I was totally afraid of was to forget the sorting in case a new CRD is added. I wanted to make it really hard to forget, and that's why I came up with the long list. But I guess I can fix that by doing the sorting in the toConfig as you said. Let me try to get a stab at it

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, with this new version, assuming we add e2e tests covering new CRD, we'll be forced to add it

When translating the configuration coming from the k8s api to the
internal one, the outcome depends on the order in which the cache
returns us the various items. This may cause unnecessary reloads.

In order to avoid this, we pre-order the list of items to feed the
conversion with.
The sorting is done in a conversion function in the controller
(rather than the config package) in order to make it harder to
forget to add the sorting action.

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

liornoy commented Jun 28, 2023

/lgtm

@fedepaol fedepaol added this pull request to the merge queue Jun 29, 2023
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jun 29, 2023
@fedepaol fedepaol added this pull request to the merge queue Jun 29, 2023
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jun 29, 2023
@fedepaol fedepaol added this pull request to the merge queue Jun 29, 2023
@fedepaol fedepaol removed this pull request from the merge queue due to a manual request Jun 29, 2023
The resulting frr configuration was influenced by the order of the
advertisements, which ultimately come from a map (which doesn't
guarantee the order). Here we sort to make the configuration stable, and
to allow us to catch non-changing configurations updates.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
@fedepaol fedepaol enabled auto-merge June 29, 2023 10:30
@fedepaol fedepaol added this pull request to the merge queue Jun 29, 2023
Merged via the queue into metallb:main with commit 651427b Jun 29, 2023
25 checks passed
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.

None yet

2 participants