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

Refactor Controller Unit Tests #118

Merged

Conversation

razo7
Copy link
Member

@razo7 razo7 commented Feb 7, 2024

  • Using 'normal' testEnv test amd setup NodeMaintenanceReconciler. Refactor taint functionality
  • Refactor initMaintenanceStatus and exclude remediation label functionality
  • Refactor Reconciliation functionality
    ECOPROJECT-1271

razo7 added 3 commits February 7, 2024 13:37
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
Copy link
Contributor

openshift-ci bot commented Feb 7, 2024

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@mshitrit
Copy link
Member

mshitrit commented Feb 7, 2024

/test ?

Copy link
Contributor

openshift-ci bot commented Feb 7, 2024

@mshitrit: The following commands are available to trigger required jobs:

  • /test 4.12-ci-bundle-my-bundle
  • /test 4.12-images
  • /test 4.12-openshift-e2e
  • /test 4.13-ci-bundle-my-bundle
  • /test 4.13-images
  • /test 4.13-openshift-e2e
  • /test 4.14-ci-bundle-my-bundle
  • /test 4.14-images
  • /test 4.14-openshift-e2e
  • /test 4.15-ci-bundle-my-bundle
  • /test 4.15-images
  • /test 4.15-openshift-e2e

Use /test all to run all jobs.

In response to this:

/test ?

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.

@mshitrit
Copy link
Member

mshitrit commented Feb 7, 2024

/test 4.15-openshift-e2e

Metrics: metricsServer.Options{BindAddress: "0"},
})
Expect(err).ToNot(HaveOccurred())
mockManager, _ := lease.NewManager(k8sClient, "")
Copy link
Member

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 ?

Copy link
Member Author

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?

Copy link
Member

@slintes slintes left a 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...

@@ -21,17 +21,18 @@ import (
"path/filepath"
"testing"

"github.com/medik8s/common/pkg/lease"
Copy link
Member

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

Copy link
Member Author

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 👍🏻

Copy link
Member Author

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"

Copy link
Member

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?

Copy link
Member

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

Copy link
Member Author

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

Copy link
Member

Choose a reason for hiding this comment

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

filename still is wrong

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

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())
Copy link
Member

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)
Copy link
Member

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))
Copy link
Member

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))
Copy link
Member

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())
Copy link
Member

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))
Copy link
Member

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?

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 "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.

Copy link
Member

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() {
Copy link
Member

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?

Copy link
Member Author

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?

Copy link
Member

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))
Copy link
Member

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?

Copy link
Member Author

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)

Copy link
Member

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 {
Copy link
Member

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?

Copy link
Member Author

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
@razo7
Copy link
Member Author

razo7 commented Feb 8, 2024

/test 4.15-openshift-e2e

Refactor the usage of context from background, and simply pass it between tests
@razo7 razo7 force-pushed the refactor-unit-test-controllers branch from e06006b to b3ced39 Compare February 18, 2024 08:34
@razo7
Copy link
Member Author

razo7 commented Feb 18, 2024

/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())
Copy link
Member

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.

Copy link
Contributor

openshift-ci bot commented Feb 19, 2024

[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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@razo7 razo7 marked this pull request as ready for review February 19, 2024 12:05
@openshift-ci openshift-ci bot requested review from clobrano and slintes February 19, 2024 12:05
@razo7
Copy link
Member Author

razo7 commented Feb 19, 2024

/retest

@openshift-merge-bot openshift-merge-bot bot merged commit 2fb090b into medik8s:main Feb 19, 2024
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants