-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Vm controller unit tests: fix return args #11322
Conversation
Skipping CI for Draft Pull Request. |
/retest pull-kubevirt-unit-test |
@xpivarc: The
The following commands are available to trigger optional jobs:
Use
In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
/test pull-kubevirt-unit-test |
I do wonder why |
So the way the mechanism works is the following:
So you see the volume request in step 2. That looks like what is failing in the test. |
All the steps are performed but there is an assumption that the volume requests are not cleared. I do wonder why. This PR exposes that the assumption was wrong from the beginning. I wonder if I can simply remove it from a unit test. WDYT? Note: See |
/cc |
A real Kubernetes API returns the modified VM. Signed-off-by: Luboslav Pivarc <lpivarc@redhat.com>
Based on strategy a VMI creation is expected Signed-off-by: Luboslav Pivarc <lpivarc@redhat.com>
4b92721
to
3b2c3e2
Compare
/hold |
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.
/approve
That's amazing, thank you! Just a couple unimportant nits in-line.
#11372 is included here, on purpose I assume, a rebase will probably be needed once that merges.
vmInterface.EXPECT().Update(context.Background(), gomock.Any()).DoAndReturn(func(ctx context.Context, vm *virtv1.VirtualMachine) (*virtv1.VirtualMachine, error) { | ||
Expect(vm.Spec.Template.Spec.Volumes[0].Name).To(Equal("vol1")) | ||
return vm, nil | ||
}) |
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: no need to use DoAndReturn()
when no return value has to be computed. Just fixing the func arg in the Do()
above would look better IMO. More instances of that present in the PR.
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.
+1
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 you mean using Do
and Return
? This doesn't work because I still want to return the updated vm
and I can only do it with DoAndReturn
. Did I miss anything?
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.
Right, my bad
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: jean-edouard The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
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.
Only nits, looks good to me otherwise
vmInterface.EXPECT().Update(context.Background(), gomock.Any()).DoAndReturn(func(ctx context.Context, vm *virtv1.VirtualMachine) (*virtv1.VirtualMachine, error) { | ||
Expect(vm.Spec.Template.Spec.Volumes[0].Name).To(Equal("vol1")) | ||
return vm, nil | ||
}) |
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.
+1
3b2c3e2
to
71f0042
Compare
/hold cancel |
1. Remove a wrong assumption where we expect requests to not be removed in the same sync as we update the spec. 2. Cover more assumption, especially if update or hotplug fails. Signed-off-by: Luboslav Pivarc <lpivarc@redhat.com>
71f0042
to
6a0bee7
Compare
Fixed |
} | ||
|
||
shouldExpectVMFinalizerRemoval := func(vm *virtv1.VirtualMachine) { | ||
patch := fmt.Sprintf(`[{ "op": "test", "path": "/metadata/finalizers", "value": ["%s"] }, { "op": "replace", "path": "/metadata/finalizers", "value": [] }]`, virtv1.VirtualMachineControllerFinalizer) | ||
|
||
vmInterface.EXPECT().Patch(context.Background(), vm.Name, types.JSONPatchType, []byte(patch), &metav1.PatchOptions{}).Return(vm, nil) | ||
vmInterface.EXPECT().Patch(context.Background(), vm.Name, types.JSONPatchType, []byte(patch), &metav1.PatchOptions{}).DoAndReturn( |
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.
Is this now needed because of the addition of sanityExecute?
Previously this function was doing the right thing as it was just expecting a specific operation and not an actual object change.
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.
Well, the change should be done by the client and the modified object should be returned. This is what happens if you are running the code against a real Kubernetes API server. Therefore, before it was wrong as we were returning a pointer to some VM, which on top of it was modified by the sync
.
Note: once we switch to the fake client, this will be handled for us.
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 wasn't doing any modifications. It was only expecting a patch. Yes, it did return a VM object, but the same one it got.
But now this actually modifies the object, so I'm a bit confused. Why is it the right thing to do?
I could expect the actual controller code to modify the object and not the test code.
What am I missing?
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 the previous code, not only did the caller get back a pointer to a VM object that's actively used by the test code, but it also didn't include the requested patch.
Now we actually apply the patch (easy since we already know what it is) and return a pointer to an object that we don't rely on and that won't change over time.
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.
@jean-edouard I'm still confused... previous code only had to observe the patch - not to perform it.
and now we are performing the patch while there was something else in the controller that had to apply the patch.
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.
@jean-edouard +100.
The fact that we did nothing in the past is wrong, it does not reflect what happens if you run the code against real API.
Note: This can be still wrong but it is at least slightly better. We need #11297 to be as close as we can to the real behavior.
EDIT: I am happy to wait for #11297
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.
perhaps it's better to wait.
The fact that we did nothing in the past is wrong, it does not reflect what happens if you run the code against real API.
Mainly because it's not a real API.
It's a unit test; All we needed to do was to observe the call being made.
I would expect complex flows to be tested in a functional test.
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 expect complex flows to be tested in a functional test.
If our "unit tests" really were unit tests (they're already much more than that) and if functional test were cheap (they're not) then I would 100% agree with you.
However, in the current state of things, I think we should always prefer unit tests when possible, no matter how complex.
Just my opinion, not really related to this PR.
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 agree that controllers are difficult to test. There must be a balance between how much you can mock and the effectiveness of the test.
How do we know that the fake client behaves the same way as a real client?
Do we really need to play a catchup game between the fake client and the real one?
We are responsible for the code in our controller and this is what we should test in the unit tests (whatever they are).
A unit test or integration test will never be a substitute for a functional test that exercises the functionality altogether.
Paying "cheap" for a test will make us pay a much higher price later.
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.
How do we know that the fake client behaves the same way as a real client?
Do we really need to play a catchup game between the fake client and the real one?
This is standard tooling from Kubernetes. We are not going to play any catch up as long as we don't want to test specific scenarios/bugs.
We are responsible for the code in our controller and this is what we should test in the unit tests (whatever they are).
Right but we should make sure we are testing realistic scenarios. For example, right now we don't. The patch should return an object that is only adjusted by the changes expressed in the request but until now we would return an object that contains every change or non (depending if we are looking at the code before or after fixing the cache corruption).
A unit test or integration test will never be a substitute for a functional test that exercises the functionality altogether.
Paying "cheap" for a test will make us pay a much higher price later.
+100, we should not remove functional test unless they are duplicates.
/lgtm |
Required labels detected, running phase 2 presubmits: |
/hold |
@xpivarc: The following tests failed, say
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
PR needs rebase. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
/close |
@xpivarc: Closed this PR. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
What this PR does
This PR fixes unit tests that were returning the wrong VM in
Update
operation. This reveals that some assumptions were wrong around the volume requests and so they are fixed as well.Fixes #
Why we need it and why it was done in this way
Special notes for your reviewer
Release note