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

Add ginkgo tests for machineset controller #544

Merged

Conversation

alexander-demicev
Copy link
Contributor

@alexander-demicev alexander-demicev commented Apr 1, 2020

This PR adds ginkgo unit tests for machineset controller and one reconcile test, the tests are similar to upstream https://github.com/kubernetes-sigs/cluster-api/blob/09f87a2aadcd590952e17684f5c05259337e4da9/controllers/machineset_controller_test.go

@openshift-ci-robot openshift-ci-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 1, 2020
Comment on lines 109 to 116
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) reconcile.Reconciler {
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
return result, err
})
return fn
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this actually does anything for us? I've seen this pattern before where it writes to a channel, but I always found relying on that to be annoying and not help us to write good tests, my suggestion would be to remove this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 I thought about removing it too

Comment on lines 81 to 97
By("setting up a new manager")
mgr, err := manager.New(cfg, manager.Options{MetricsBindAddress: "0"})
Expect(err).NotTo(HaveOccurred())

// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
k8sClient = mgr.GetClient()

// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, t *testing.T) (chan struct{}, chan error) {
t.Helper()
By("setting up a new reconciler")
r := newReconciler(mgr)
reconciler = SetupTestReconcile(r)

stop := make(chan struct{})
errs := make(chan error, 1)
err = add(mgr, reconciler, r.MachineToMachineSets)
Expect(err).NotTo(HaveOccurred())

By("starting the manager")
go func() {
errs <- mgr.Start(stop)
Expect(mgr.Start(doneMgr)).To(Succeed())
}()
Copy link
Contributor

Choose a reason for hiding this comment

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

This could potentially be done in a BeforeEach within the test suite, that way we start a new manager and reconciler for each test and there is less potential for side effects between individual tests

Comment on lines 95 to 97
go func() {
errs <- mgr.Start(stop)
Expect(mgr.Start(doneMgr)).To(Succeed())
}()
Copy link
Contributor

Choose a reason for hiding this comment

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

Separate go routines calling gomega expectations require a defer GinkgoRecover() at the top

Comment on lines 39 to 64
AfterEach(func() {
By("Deleting the namespace")
Expect(k8sClient.Delete(ctx, namespace)).To(Succeed())
})
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no GC running so you'll need to delete all Machines and MachineSets too, probably worth a helper function that does this (list all machines/machinesets, iterate through and delete). Doing this way means you don't have to clean up within your test IT's, just rely on the suite to clean everything

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

Comment on lines 72 to 96
Eventually(func() int {
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
return -1
}
return len(machines.Items)
}, timeout).Should(BeEquivalentTo(replicas))
Copy link
Contributor

Choose a reason for hiding this comment

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

You can return a second parameter here (I learnt this recently) and Eventually will check the non-first params are their zero values/nil, that way you don't have to make up "error" values as we have done before. Might be worth doing the same on the others so that errors are surfaced properly

Suggested change
Eventually(func() int {
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
return -1
}
return len(machines.Items)
}, timeout).Should(BeEquivalentTo(replicas))
Eventually(func() (int, error) {
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
return 0, err
}
return len(machines.Items), nil
}, timeout).Should(BeEquivalentTo(replicas))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@alexander-demicev alexander-demicev changed the title [WIP]Add ginkgo tests for machineset controller Add ginkgo tests for machineset controller Apr 1, 2020
@openshift-ci-robot openshift-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 1, 2020
@@ -262,3 +266,67 @@ func TestAdoptOrphan(t *testing.T) {
}
}
}

func TestMachineSetReconcile(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason not to use Ginkgo for these two tests as well?

var _ = Describe("MachineSet Reconcile", func() {
  var r reconciler
  var result reconile.Result
  var reconcileErr error

  BeforeEach(func() {
    // set up reconciler as r with scheme and new fake recorder
  })

  JustBeforeEach(func() {
    result, reconcileErr = r.Reconcile(request)
  })

  Context("with a valid machineset", func() {
    BeforeEach(func() {
       // set up machinset
       // set up fake client
       // override r.client
    })

    It("has the expected result", func() {
      Expect(result).To(Equal())
     })

    It("has the expected error", func() {
      Expect(reconcileErr).To(Equal())
     })

    It("did something with events", func() {
      g.Eventually(r.recorder.Events).Should(Receive())
     })
  })

  Context("with an invalid machineset", func() {
    // This should look basically the same as above
  })
})

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

By("Deleting the namespace")
Expect(k8sClient.Delete(ctx, namespace)).To(Succeed())

By("Deleting the machinesets")
cleanResources()
Copy link
Contributor

Choose a reason for hiding this comment

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

I would swap the order of these around so you clean the resources before deleting the namespace

return 0, err
}
return len(machines.Items), nil
}, timeout).Should(BeEquivalentTo(replicas))
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume there's a type mismatch if you use Equal here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right

// Verify that the Machine has been deleted.
Eventually(func() bool {
key := client.ObjectKey{Name: machineToBeDeleted.Name, Namespace: machineToBeDeleted.Namespace}
if err := k8sClient.Get(ctx, key, &machineToBeDeleted); apierrors.IsNotFound(err) || !machineToBeDeleted.DeletionTimestamp.IsZero() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there anything adding a finalizer to the Machines? Do you expect them not to be deleted straight away?, I'd be tempted to remove the second check and see if this still passes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, the check can be removed

Comment on lines 113 to 107
Eventually(func() (ready int) {
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
return -1
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Eventually(func() (ready int) {
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
return -1
}
Eventually(func() (int, error) {
ready := 0
if err := k8sClient.List(ctx, machines, client.InNamespace(namespace.Name)); err != nil {
return 0, err
}

Comment on lines 133 to 128
for _, machineSet := range machineSets.Items {
if err := k8sClient.Delete(ctx, &machineSet); err != nil {
return err
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Taking the address of a loop variable in go is asking for trouble! 😅 Do this instead to avoid confusion later 😉

Suggested change
for _, machineSet := range machineSets.Items {
if err := k8sClient.Delete(ctx, &machineSet); err != nil {
return err
}
for _, machineSet := range machineSets.Items {
ms := machineSet
if err := k8sClient.Delete(ctx, &ms); err != nil {
return err
}

While I don't think it makes a problem here, it can in certain cases so it's best to just avoid it

Have a read of https://github.com/golang/go/wiki/CommonMistakes#using-reference-to-loop-iterator-variable for more info

Please also fix the one below

@JoelSpeed
Copy link
Contributor

We aren't reducing the test cases right, as far as I can see the new file with the tests in that call Reconcile directly match two of the cases removed from the ones converted to Ginkgo?

@alexander-demicev
Copy link
Contributor Author

@JoelSpeed Yes, we are not reducing coverage and all comments are fixed now.

Copy link
Contributor

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

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

In terms of refactoring, looks great! Added a couple of suggestions for improving the test naming and also potentially for adding some extra cases but I'm not fussed about those going in this PR, just ideas for future improvements

Expect(result).To(Equal(reconcile.Result{}))
})

It("has the expected error", func() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It("has the expected error", func() {
It("does not return an error", func() {

Comment on lines +337 to +339
It("did something with events", func() {
Eventually(rec.Events).Should(Receive())
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to also check the result and error here? Do you know what they're expected to be? /could you work it out from the code? (Could be a follow up)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That should be possible with Eventually(rec.Events).Should(Receive(Equal("..")))

Comment on lines 308 to 310
It("has the expected result", func() {
Expect(result).To(Equal(reconcile.Result{}))
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be any events in this case? Could check those? (Could be a follow up)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, in this case, there shouldn't

r.Client = fake.NewFakeClientWithScheme(scheme.Scheme, ms)
})

It("has the expected result", func() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It("has the expected result", func() {
It("returns an empty result", func() {

@alexander-demicev
Copy link
Contributor Author

@JoelSpeed Can one more review round?

Copy link
Contributor

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

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

/lgtm

Maybe worth a follow up to improve the reconcile test but that's another concern

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 1, 2020
@enxebre
Copy link
Member

enxebre commented Apr 3, 2020

/approve

@openshift-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: enxebre

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

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 3, 2020
@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

1 similar comment
@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-ci-robot openshift-ci-robot removed the lgtm Indicates that a PR is ready to be merged. label Apr 3, 2020
@enxebre
Copy link
Member

enxebre commented Apr 6, 2020

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 6, 2020
@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

1 similar comment
@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Apr 6, 2020

@alexander-demichev: The following tests failed, say /retest to rerun all failed tests:

Test name Commit Details Rerun command
ci/prow/e2e-azure 5947d94 link /test e2e-azure
ci/prow/e2e-aws-scaleup-rhel7 5947d94 link /test e2e-aws-scaleup-rhel7

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

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.

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-merge-robot openshift-merge-robot merged commit 79ccf91 into openshift:master Apr 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants