-
Notifications
You must be signed in to change notification settings - Fork 16
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
Refactor Controller Unit Tests #118
Refactor Controller Unit Tests #118
Conversation
Using 'normal' testEnv test amd setup NodeMaintenanceReconciler. In the past it wasn't needed as the old controller tests called relevant funcs of the controller themself. It is needed for testing events. v1->corev1. Testing only taints functionality ATM
…label Modify setOwnerRefToNode, addExcludeRemediationLabel, removeExcludeRemediationLabel, and initMaintenanceStatus functions to be non-reconcile functions so there behavior can be easily tested in unit tests
Combine old logic into less tests but with steps
Skipping CI for Draft Pull Request. |
/test ? |
@mshitrit: The following commands are available to trigger required 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 4.15-openshift-e2e |
controllers/controller_suite_test.go
Outdated
Metrics: metricsServer.Options{BindAddress: "0"}, | ||
}) | ||
Expect(err).ToNot(HaveOccurred()) | ||
mockManager, _ := lease.NewManager(k8sClient, "") |
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: there are a lot of managers involved, maybe mockLeaseManager
is better name ?
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.
mockLeaseManager
is already used in nodemaintenance_controller_test.go.
Maybe tempMockManager
?
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.
since the diff is difficult to review, I did a review of the complete new version. Can be that some comments are not related to changes of this PR...
controllers/controller_suite_test.go
Outdated
@@ -21,17 +21,18 @@ import ( | |||
"path/filepath" | |||
"testing" | |||
|
|||
"github.com/medik8s/common/pkg/lease" |
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.
about the file name: eiher just keep suite_test.go (good enough IMHO), or use <package_name>_suite_test.go please
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 wanted to go with <package_name>_suite_test.go
, and I forgot the suffix s
👍🏻
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.
BTW should nodemaintenance_controller.go
and nodemaintenance_controller_test
follow the same pattern? Since they both use "controller" and not "controllers"
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.
no, each file is about a single controller, not?
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.
but the package can contain multiple
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.
but the package can contain multiple
Correct, as SNR has multiple controllers
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.
filename still is wrong
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.
done
Expect(nm.Status.EvictionPods).To(Equal(2)) | ||
Expect(nm.Status.TotalPods).To(Equal(2)) | ||
Expect(nm.Status.DrainProgress).To(Equal(0)) | ||
Expect(nm.Status.LastUpdate.IsZero()).To(BeFalse()) |
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.
Expect(nm.Status.LastUpdate).ToNot(BeZero())
}) | ||
When("Status was initalized", func() { | ||
It("should be set for running with 2 pods to drain", func() { | ||
initMaintenanceStatus(nm, r.drainer, r.Client) |
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.
unhandled error
node := &corev1.Node{} | ||
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: taintedNodeName}, node)).To(Succeed()) | ||
setOwnerRefToNode(nm, node, r.logger) | ||
Expect(len(nm.ObjectMeta.GetOwnerReferences())).To(Equal(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.
Expect(nm.ObjectMeta.GetOwnerReferences()).To(HaveLen(1))
nmCopy = nm.DeepCopy() | ||
nmCopy.Status.Phase = nodemaintenanceapi.MaintenanceFailed | ||
initMaintenanceStatus(nmCopy, r.drainer, r.Client) | ||
Expect(nmCopy.Status.Phase).NotTo(Equal(nodemaintenanceapi.MaintenanceRunning)) |
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.
IMHO the test should be that the CR keeps the same phase as before. Same for other fields.
Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: taintedNodeName}, taintedNode)).To(Succeed()) | ||
Expect(isTaintExist(taintedNode, medik8sDrainTaint.Key, medik8sDrainTaint.Effect)).To(BeTrue()) | ||
Expect(isTaintExist(taintedNode, NodeUnschedulableTaint.Key, NodeUnschedulableTaint.Effect)).To(BeTrue()) | ||
Expect(isTaintExist(taintedNode, dummyTaintKey, corev1.TaintEffectPreferNoSchedule)).To(BeTrue()) |
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.
maybe add a comment that this is an existing taint which should not have been removed?
Expect(isTaintExist(taintedNode, NodeUnschedulableTaint.Key, NodeUnschedulableTaint.Effect)).To(BeTrue()) | ||
Expect(isTaintExist(taintedNode, dummyTaintKey, corev1.TaintEffectPreferNoSchedule)).To(BeTrue()) | ||
// there is also a not-ready taint | ||
Expect(len(taintedNode.Spec.Taints)).To(Equal(4)) |
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.
Where is this 4th taint coming from? Do we have to care for it?
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.
The "node.kubernetes.io/not-ready" taint is added automatically when a node is created with a taint that of PreferNoSchedule effect.
Do we have to care for it?
We don't but checking the amount of taints is another verification (could be an overkill, and redundant check indeed) for the addition/removal of taints.
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 should only verify our own changes, so yes, it's "overkill" indeed
}) | ||
}) | ||
|
||
When("Adding and then removing a taint", func() { |
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.
similar to label test, why test adding only first, and then adding + removing?
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.
Originally, I was trying to keep the logic of the old tests and not remove some as it could leave us with missing tests, and there is a small value in keeping the current behavior.
In the beginning, I was more keen on going with the adding + removing direction.
The value I see here is that we separate dependency between adding and removing (not necessarily here..., but the addition could be not as part of the test) of taint/label. Another argument for adding + removing direction is that by using "steps" (once for addition and for removal) we can gain this nice separation for debugging the cause of a possible error.
Do you see this dependency separation as valuable for these functionality tests?
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.
The value I see here is that we separate dependency between adding and removing
Either I don't understand what you mean, or i just don't see any value 🤔
we can gain this nice separation for debugging the cause of a possible error.
you always get the line number of a failed test 🤷🏼♂️
It("should fail on non existing node", func() { | ||
By("check nm CR status and whether LastError was updated") | ||
maintenance := getNMAfter1Sec(nm) | ||
Expect(maintenance.Status.Phase).To(Equal(nodemaintenanceapi.MaintenanceRunning)) |
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.
don't we have a "failed" phase?
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, we do. but only when we can't extend the owned lease... I think we can also put a failed phase when a node doesn't exist (or 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.
I think we can also put a failed phase when a node doesn't exist
I think we should! (in a new PR)
}, timeout, pollTime).Should(BeNil()) | ||
return maintenance | ||
} | ||
func getNMAfter1Sec(nm *nodemaintenanceapi.NodeMaintenance) *nodemaintenanceapi.NodeMaintenance { |
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.
hm.... what are we doing here? Why not just sleeping for a second? What's the value of getting the CR 5 times very 200ms?
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.
Not so nice. Would simply sleeping for one second or polling for the same amount of time as the timeout would be better? I think the first option
One check for add+removal label/taint, sleep between testing reconcile tests and better expect statments
/test 4.15-openshift-e2e |
Refactor the usage of context from background, and simply pass it between tests
e06006b
to
b3ced39
Compare
/test 4.15-openshift-e2e |
}) | ||
When("Status was initalized", func() { | ||
It("should be set for running with 2 pods to drain", func() { | ||
Expect(initMaintenanceStatus(nm, r.drainer, r.Client)).To(HaveOccurred()) |
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.
Expecting an error here is a nice indication IMHO that the implementation has an issue. Here: it would be better to separate setting status fields, and doing the actual update. Out of scope for this PR though.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: razo7, slintes 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 |
/retest |
ECOPROJECT-1271