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

Ignore ctrlreg in deletion when computing required ctrlinsts #2612

Merged
merged 1 commit into from
Aug 13, 2020

Conversation

vpnachev
Copy link
Member

How to categorize this PR?

/area quality
/kind bug
/priority normal

What this PR does / why we need it:
Ignore ControllerRegistration in deletion when computing required ControllerInstalations.

To properly apply gardener/gardener-extension-os-suse-chost#20 on existing landscapes, it is necessary to manually delete the existing controller installations. However, on large landscapes GCM creates new controller installations before the last old one to be deleted. With this change, once the deletion of the controller registration is triggered, no new installations will be made out of it.

Which issue(s) this PR fixes:
Fixes #

Special notes for your reviewer:

Release note:

`ControllerInstallation`s are no longer created for `ControllerRegistration`s that are in deletion.

@vpnachev vpnachev requested a review from a team as a code owner July 21, 2020 17:12
@gardener-robot gardener-robot added area/quality Output qualification (tests, checks, scans, automation in general, etc.) related kind/bug Bug priority/normal labels Jul 21, 2020
Copy link
Member

@rfranzke rfranzke left a comment

Choose a reason for hiding this comment

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

Thanks, the change makes sense 👍 Can you add a new ControllerRegistration with a deletionTimestamp and support for resource kind/type that are used by one of the shoots here? That'd probably be enough to test this case (expectation would be that the computed list of required controller registrations doesn't change).

@vpnachev
Copy link
Member Author

I have amend it to make it easier for testing.

Copy link
Member

@timebertt timebertt left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, makes sense!

@rfranzke
Copy link
Member

/status author-action

@gardener-robot
Copy link

@vpnachev The pull request was assigned to you under author-action. Please unassign yourself when you are done. Thank you.

@vpnachev
Copy link
Member Author

An admission plugin has been implemented, in this way it is guaranteed that creation of a controller installation for controller registration in deletion will not succeed.

The change in the controller is re-worked again and it now silently skips the creation of controller installation, however it still can update existing installations. I tried to implement some unit test about this change, something similar to

Describe("#deployNeededInstallations", func() {
It("should return an error", func() {
var (
wantedControllerRegistrations = sets.NewString(controllerRegistration2.Name)
registrationNameToInstallationName = map[string]string{
controllerRegistration1.Name: controllerInstallation1.Name,
controllerRegistration2.Name: controllerInstallation2.Name,
controllerRegistration3.Name: controllerInstallation3.Name,
}
fakeErr = errors.New("err")
)
k8sClient.EXPECT().Get(ctx, kutil.Key(controllerInstallation2.Name), gomock.AssignableToTypeOf(&gardencorev1beta1.ControllerInstallation{})).Return(fakeErr)
err := deployNeededInstallations(ctx, nopLogger, k8sClient, seedWithShootDNSEnabled, wantedControllerRegistrations, controllerRegistrations, registrationNameToInstallationName)
Expect(err).To(Equal(fakeErr))
})
It("should correctly deploy needed controller installations", func() {
var (
wantedControllerRegistrations = sets.NewString(controllerRegistration2.Name, controllerRegistration3.Name)
registrationNameToInstallationName = map[string]string{
controllerRegistration1.Name: controllerInstallation1.Name,
controllerRegistration2.Name: controllerInstallation2.Name,
controllerRegistration3.Name: controllerInstallation3.Name,
}
)
installation2 := controllerInstallation2.DeepCopy()
installation2.Labels = map[string]string{
common.RegistrationSpecHash: "b24405c0d68a538e",
common.SeedSpecHash: "6668c8b5c30659ab",
}
installation3 := controllerInstallation3.DeepCopy()
installation3.Labels = map[string]string{
common.RegistrationSpecHash: "b24405c0d68a538e",
common.SeedSpecHash: "6668c8b5c30659ab",
}
k8sClient.EXPECT().Get(ctx, kutil.Key(controllerInstallation2.Name), gomock.AssignableToTypeOf(&gardencorev1beta1.ControllerInstallation{}))
k8sClient.EXPECT().Update(ctx, installation2)
k8sClient.EXPECT().Get(ctx, kutil.Key(controllerInstallation3.Name), gomock.AssignableToTypeOf(&gardencorev1beta1.ControllerInstallation{}))
k8sClient.EXPECT().Update(ctx, installation3)
err := deployNeededInstallations(ctx, nopLogger, k8sClient, seedWithShootDNSEnabled, wantedControllerRegistrations, controllerRegistrations, registrationNameToInstallationName)
Expect(err).NotTo(HaveOccurred())
})
})
but I failed to understand how these tests works and whether it is possible such a test to be implemented.

@rfranzke
Copy link
Member

rfranzke commented Aug 12, 2020

An admission plugin has been implemented, in this way it is guaranteed that creation of a controller installation for controller registration in deletion will not succeed.

Hm, I can't follow - why is such an admission plugin needed? I mean, what's the reason this is so important that we have to forbid it API-wise? Can't we save this code for good? The only code that creates ControllerRegistration in Gardener is the controller you already changed, and if a user creates a ControllerInstallation manually then why should we forbid this?

but I failed to understand how these tests works and whether it is possible such a test to be implemented.

What exactly didn't you understand? If I see it correctly, the only thing you have to do is to add a ControllerRegistration with a deletionTimestamp to this list. As it's not expected that it's going to be created you don't need EXPECT() calls for the k8sClient.
As a second test case, you could also add a ControllerRegistration with a deletionTimestamp which already has an existing ControllerInstallation. In this case you would expect that an Update() call is sent.

@vpnachev
Copy link
Member Author

... and if a user creates a ControllerInstallation manually then why should we forbid this?

I have no strong opinion, it was implemented just as a second prevention mechanism. Basically, applying the same approach by k8s community with namespaces - if it is in deletion, no new resources can be created in the namespace.

What exactly didn't you understand?

Exactly these .EXPECT(). calls are not clear to me what are they doing and how are they supposed to be used.
Looking through the generated code also does not help. I tried to find some documentation or guides or to look through https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/client but failed here as well. Could you point me to some guides/documentation about this framework?

What I really tried to do was to call deployNeededInstallations with a ControllerRegistration with deletionTimestamp set and then list the ControllerInstallations with the provided fake client expecting empty list as result - this failed on multiple places, so I have decided that I am doing it completely wrong.

@vpnachev
Copy link
Member Author

Ok, I was thinking that this is coming from k8s.io, while it is actually from gomock - I will spend some time with this package.

Together with @ialidzhikov we have taken a look through the change and he helped me a lot to understand more about the tests, especially for mocks and fake clients, and to implement one (thanks for that, @ialidzhikov !).

In parallel, there is a validation that disallow ControllerRigistration to be updated once it is in deletion, thus we have decided to also not update existing installations because it should not possible due to this validation.

@rfranzke
Copy link
Member

/invite @timuthy @danielfoehrKn @BeckerMax

@ialidzhikov
Copy link
Member

/assign @ialidzhikov

@vpnachev
Copy link
Member Author

The admission plugin has been removed.

Copy link
Member

@rfranzke rfranzke left a comment

Choose a reason for hiding this comment

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

/lgtm

Copy link
Member

@ialidzhikov ialidzhikov left a comment

Choose a reason for hiding this comment

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

/lgtm

@rfranzke rfranzke merged commit 606a6af into gardener:master Aug 13, 2020
@vpnachev vpnachev deleted the ctrlreg/ignore-in-deletion branch August 13, 2020 05:24
@gardener-robot gardener-robot added priority/3 Priority (lower number equals higher priority) and removed priority/3 Priority (lower number equals higher priority) labels Mar 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/quality Output qualification (tests, checks, scans, automation in general, etc.) related kind/bug Bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants