-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Recently, we've been writing extensive unit test coverage of our controllers using fake.Client but a few of our controllers use client.Apply, which is not supported by fake.Client.
We can't migrate these controllers from client.Apply to client.Merge because with upgrades, any CRs managed by the previous version will have owned fields and the API server will reject the request (my understanding, is this true?) based on the SSA docs it seems the server would only reject requests based on ownership when using SSA, so using update would work.
Envtest takes ~5-15s to set up for each test case, meaning a test suite which would take 0.05s with fake.Client, takes 5 minutes with envtest. In many cases, we can isolate tests to namespaces and reuse the same client, but not always. We prefer our unit tests to be as isolated as possible.
Would it be possible to support server-side apply / client.Apply / SSA in the fake client?
Activity
alvaroaleman commentedon May 23, 2023
The reason this isn't currently supported is because upstream client-go doesn't support this: kubernetes/kubernetes#115598
We could build something downstream I suppose and the create case is going to be simple. I think the Update case is going to be pretty complicated though (if a field that is currently present but not in the submitted applyconfig, should we keep it or not is a non-trivial question to answer), so if possible I'd prefer to wait for upstream.
In the meantime when using todays 0.15 release of controller-runtime, you can use the interceptor to set up a createOrUpdate logic in
Patch
when an applypatch is submitted that works for your case.vincepri commentedon May 24, 2023
Hm, envtest is usually setup once per test package, is there a reason why envtest in this case once for every test case?
nathanperkins commentedon May 24, 2023
I need to review our cases. We want to ensure that our test cases are isolated. Most of the time you can isolate them into unique namespaces but some of the controllers have specific requirements around that. I think we may be able to get around it by specifying which namespace to use as a field on the reconciler struct.
Thank you for the response! I will look into this :)
Sounds reasonable to me. I mostly wanted to create an issue so that we have something to represent that it would make our lives a bit easier. If the work to benefit ratio doesn't make sense, that's fair.
Given that we can run envtest and isolate cases in namespaces, that is probably the way to go. It's a small bummer that we have to run our unit tests alongside an external dependency which takes some time to start up. We're going to reorganize things a bit and improve our scripts to make this easier for our developers.
nathanperkins commentedon May 24, 2023
Found a case where it is a bit of a drag to use envtest. Any test which involves cluster scoped objects cannot be fully isolated in namespaces, leading to some issues:
alvaroaleman commentedon May 24, 2023
@nathanperkins could we keep the discussion around if and how and when to use envtest separate? We are aware that this is lacking right now, but this is non-trivial which is the reason upstream hasn't done it. This essentially requires to have the serverside SSA logic in the fake client.
nathanperkins commentedon May 24, 2023
Sure, it's totally understood this is not going to be resolved anytime soon.
I think that people searching for this issue will find it useful if there is clarity on what they can do in the meantime. The discussion on when / how to use envtest effectively instead of the fake client seems useful to that end. Maybe there is a doc or blog post that could be linked?
vincepri commentedon May 24, 2023
Do you have an example handy to show? Just to wrap my head around a bit more 😄
A few thoughts:
controller-runtime/pkg/cache/cache.go
Lines 147 to 153 in 30eae58
nathanperkins commentedon May 26, 2023
I couldn't show the code without going through a bunch of approvals. I can tell you it's a controller which reconciles on corev1.Service and looks at corev1.Node status to create some internals CRs for network configuration. Our test is using envtest and we can't fully isolate the nodes between test cases without cleaning up the nodes.
Great idea! I'll share this with my teammate and see if it works for our case.
I agree, sharing some of these patterns would really help. Last I looked at the kubebuilder docs, they have the example which uses ginkgo and gomega and relies on the manager to run reconciliation. I've been finding that it's easier to write exhaustive and accurate test coverage using more traditional table driven tests which call reconcile directly. We still write integration tests with ginkgo and gomega which use the manager, but less exhaustive and focusing more on ensuring the event handlers work correctly.
I'd love to see more discussion in the community about this, whether in docs or blog posts :)
nathanperkins commentedon May 28, 2023
@vincepri, I'm moving discussion of using envtest with isolated unittest cases to #2358
sbueringer commentedon May 31, 2023
@nathanperkins we have some general guidance here: https://cluster-api.sigs.k8s.io/developer/testing.html (not sure in which issue we want to dig deeper into pro/con of fake client vs envtest)
jakobmoellerdev commentedon Aug 23, 2023
For anyone looking for a workaround until the fake Client supports
client.Apply
:troy0820 commentedon Aug 28, 2023
/kind support feature
@nathanperkins can we close this issue? I see you moved it to a different issue
44 remaining items