Repository: kubernetes-sigs/controller-runtime

Issue:
CreateOrUpdate performs Updates even if the object is unchanged
The [controllerutil.CreateOrUpdate](https://github.com/kubernetes-sigs/controller-runtime/blob/76067279145e76148cf27c01ea4ec63fed8e463b/pkg/controller/controllerutil/controllerutil.go#L307) does not correctly handle updates.

Given the same resource multiple times, `CreateOrUpdate` will always perform an UPDATE api call, and report that it did so, even if there were no updates to the resource. This can be easily verified by making the following [modifcation](https://github.com/ian-howell/controller-runtime/commit/3782f7642c5734100221c1c3172d01c8997f641a) to the [unit test](https://github.com/kubernetes-sigs/controller-runtime/blob/76067279145e76148cf27c01ea4ec63fed8e463b/pkg/controller/controllerutil/controllerutil_test.go#L532) 

![Image](https://github.com/user-attachments/assets/e420fdd5-608d-4e64-8ade-4f1bcde009e8)

This diff results in the following error:

![Image](https://github.com/user-attachments/assets/6145fe81-781b-4ae8-b5ac-de6518f96576)

This is problematic:
1. It result in unnecessary API calls, 
2. It reduces visibility into what a controller is doing. The function suggests that it will return OperationNone if no changes were made, however controller authors still need to manually check if their resource changed. This reduces the ability to properly log and emit metrics, as well as other problems such as those offered in https://github.com/kubernetes-sigs/controller-runtime/issues/847#issuecomment-599112796

### Root Cause in this comment:
https://github.com/kubernetes-sigs/controller-runtime/issues/3191#issuecomment-2781928962

Hints from issue comments:
@ian-howell: My gut reaction was that this would be related to changing `ResourceVersions`, but that doesn't appear to be the case. This diff results in the same failure as above (i.e., the `ResourceVersion` didn't change):

![Image](https://github.com/user-attachments/assets/59c6ea9a-1f38-4037-b54f-789630ee11f8)@ian-howell: Oh this is even weirder. The Gomega `Equal` matcher is not finding any difference between the before and after. Notably, this diff uses the same `DeepCopyObject` method used inside of `CreateOrUpdate`. The `Expect(deploy).To(Equal(prevDeploy))` passes, but the result check still fails:

![Image](https://github.com/user-attachments/assets/47fd0cb3-c6a2-4df3-95f9-d4b5a5c10efa)@ian-howell: `CreateOrUpdate` depends on `equality.Semantic.DeepEqual` to determine if the object was modified. This rabbit hole goes deeper though, because this addition to the above diff _also_ passes:

`Expect(equality.Semantic.DeepEqual(deploy, prevDeploy)).To(BeTrue())`

Why does that function return `true` in the test, but `false` in `CreateOrUpdate`?@ian-howell: This addition to `CreateOrUpdate` causes the test to pass (I would be very worried if it didn't...):

![Image](https://github.com/user-attachments/assets/8e05d52d-5765-4760-8142-2996292494db)

So the issue must be occurring thanks to the `mutateFn`@alvaroaleman: Please provide `existing` and `obj`, otherwise this is not actionable @ian-howell: I didn't create `existing` and `obj` - they're already in `CreateOrUpdate`:

[obj](https://github.com/kubernetes-sigs/controller-runtime/blob/76067279145e76148cf27c01ea4ec63fed8e463b/pkg/controller/controllerutil/controllerutil.go#L307) is the argument that gets passed to `CreateOrUpdate`.

[existing](https://github.com/kubernetes-sigs/controller-runtime/blob/76067279145e76148cf27c01ea4ec63fed8e463b/pkg/controller/controllerutil/controllerutil.go#L325) is created inside the function.

All of the code I've shown is all in-repo. This [commit](https://github.com/ian-howell/controller-runtime/commit/3782f7642c5734100221c1c3172d01c8997f641a) on my fork demonstrates the issue.@alvaroaleman: If you CreateOrUpdate the same object twice in rapid succession then it will always trigger an update yes. This is because the cache from which it reads is eventually consistent, so it will not see the result of the first CreateOrUpdate in the second and issue an update (or create).@ian-howell: I'm probably missing something, but it doesn't look like the test client uses a cache? Here's it's [initialization](https://github.com/kubernetes-sigs/controller-runtime/blob/76067279145e76148cf27c01ea4ec63fed8e463b/pkg/controller/controllerutil/controllerutil_suite_test.go#L47):

`c, err = client.New(cfg, client.Options{})`

and here's the [docstring for client.New](https://github.com/kubernetes-sigs/controller-runtime/blob/76067279145e76148cf27c01ea4ec63fed8e463b/pkg/client/client.go#L82-L84):

> // The client's read behavior is determined by Options.Cache.
// If either Options.Cache or Options.Cache.Reader is nil,
// the client reads directly from the API server.

Anyway, I did some more testing and I think I've figured it out. 

The following is pulled from my branch. [I recommend pulling and testing the branch](https://github.com/ian-howell/controller-runtime/blob/eebab03c7372395177cde235de76bbc360f3ca4f/pkg/controller/controllerutil/controllerutil_test.go#L532), but I'll leave the contents here as well for convenience:

```go
It("updates only changed objects", func() {                                                                                         
	// Prior to the test, deploy only has ObjectMeta. This is due to the BeforeEach.                                            
	op, err := controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specr)                                                  
                                                                                                                                    
	// After the above CreateOrUpdate, deploy will have deplSpec attached by the specr                                          
	// function.                                                                                                                
	Expect(op).To(BeEquivalentTo(controllerutil.OperationResultCreated))                                                        
	Expect(err).NotTo(HaveOccurred())                                                                                           
                                                                                                                                    
	// Let's save a copy of deploy and then perform a no-op on it. We expect that existing                                      
	// will be equal to deploy after the no-op.                                                                                 
	existing := deploy.DeepCopyObject()                                                                                         
	// For sanity, make sure that DeepCopyObject actually worked.                                                               
	Expect(existing).To(Equal(deploy))                                                                                          
                                                                                                                                    
	// Call specr. This this is the same function called from CreateOrUpdate, we expect                                         
	// that it will be a no-op.                                                                                                 
	err = specr()                                                                                                               
	Expect(err).NotTo(HaveOccurred())                                                                                           
                                                                                                                                    
	// However...                                                                                                               
	Expect(existing).To(Equal(deploy))                                                                                          
                                                                                                                                    
	// The above function fails, but fortunately for us, it also prints the differences.                                        
	// It looks something like this (the common lines have been removed):                                                       
	//                                                                                                                          
	//    <*v1.Deployment | 0xc000543908>: {                      |       <*v1.Deployment | 0xc00014ef08>: {                    
	//            Replicas: 1,                                    |               Replicas: nil,                                
	//                            TerminationMessagePath: "/dev/t |                               TerminationMessagePath: "",   
	//                            TerminationMessagePolicy: "File |                               TerminationMessagePolicy: "", 
	//                            ImagePullPolicy: "Always",      |                               ImagePullPolicy: "",          
	//                                                                                                                          
	// I took a look at the API for deployment, and it turns out that Replicas will                                             
	// default to 1 if not set. The remaining fields are part of the Container API type,                                        
	// and also have default values that align with the above.                                                                  
	//                                                                                                                          
	// We know that the second specr should be a no-op, so what else could be going on?                                         
	// Let's revisit the *first* call to CreateOrUpdate. We made the assumption that after                                      
	// that call, the only contents of deploy would be the ObjectMeta and deplSpec that we                                      
	// defined. However, we can see from the above output that this isn't true. The                                             
	// contents of deploy (at the time) included the default values. I believe this                                             
	// happened because the Defaulter for the deployment would've been run against it.                                          
	// This also means that existing would have the default values. When we call specr, we                                      
	// wipe out those default values, and our test fails.                                                                       
                                                                                                                                    
	// Finally, suppose the above call to specr (and the assertions) is commented out.                                          
	// When we fetch the resource for the second time in the below call to CreateOrUpdate,                                      
	// we'll get the complete object, with defaults and all. But then when we call specr,                                       
	// we'll replace the entire Spec (which has the defaults), with deplSpec (which                                             
	// *doesn't* have the defaults), making the call to equality.Semantic.DeepEqual fail                                        
	// on every call, and thus CreateOrUpdate will *ALWAYS* perform an update.                                                  
	op, err = controllerutil.CreateOrUpdate(context.TODO(), c, deploy, specr)                                                   
	By("returning no error")                                                                                                    
	Expect(err).NotTo(HaveOccurred())                                                                                           
                                                                                                                                    
	By("returning OperationResultNone")                                                                                         
	Expect(op).To(BeEquivalentTo(controllerutil.OperationResultNone))                                                           
})                                                                                                                                  
```



In conclusion, if the mutate function sets a field with a non-zero default to the zero value for that field, then it can never be the case that the resource fetched from the API server is equal to the resource passed to CreateOrUpdate, and it will always perform an update.
@troy0820: I am still trying to understand what the issue is.  If you use the method twice, wouldn't the `resourceVersion` be different? Because you are always `Get`ting the object from the first time you used it?

Therefore the eventual consistency from what @alvaroaleman  is talking about means that the resource is being "updated"? 

I would caution with the edge case only because conflating this behavior as inconsistent to change would likely force an update that will break other users. 

The tooling itself requires the mutateFn to always work on the object and that seems to make it not equal.  Curious to understand more of why the expected behavior of even using this twice would result in a OperationResultNone.@alvaroaleman: I think the issue is that their mutate function resets fields that get defaulted due to golang zero values, which is why there is always a diff. But short of duplicating all possible validations, I don't see how we can do anything about that in c-r@ian-howell: Yes, the defaulting is the problem.

@troy0820's answer made me notice something else though - if there are any values set on `obj` prior to calling `CreateOrUpdate`, they _might_ be overwritten by the call to `Get`.

I agree with @alvaroaleman that c-r shouldn't be responsible for the defaulting problem, but perhaps we could update the documentation? I've created a PR with some potential additions: https://github.com/kubernetes-sigs/controller-runtime/pull/3193

Solution patch:
diff --git a/examples/scratch-env/go.mod b/examples/scratch-env/go.mod
index 410b465e..1e46bc41 100644
--- a/examples/scratch-env/go.mod
+++ b/examples/scratch-env/go.mod
@@ -34,30 +34,30 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
-	github.com/prometheus/client_golang v1.22.0-rc.0 // indirect
+	github.com/prometheus/client_golang v1.22.0 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
 	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/net v0.33.0 // indirect
+	golang.org/x/net v0.38.0 // indirect
 	golang.org/x/oauth2 v0.27.0 // indirect
-	golang.org/x/sync v0.11.0 // indirect
-	golang.org/x/sys v0.30.0 // indirect
-	golang.org/x/term v0.29.0 // indirect
-	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/sync v0.12.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/term v0.30.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
 	golang.org/x/time v0.9.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/protobuf v1.36.5 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/api v0.33.0-beta.0 // indirect
-	k8s.io/apiextensions-apiserver v0.33.0-beta.0 // indirect
-	k8s.io/apimachinery v0.33.0-beta.0 // indirect
-	k8s.io/client-go v0.33.0-beta.0 // indirect
+	k8s.io/api v0.33.0 // indirect
+	k8s.io/apiextensions-apiserver v0.33.0 // indirect
+	k8s.io/apimachinery v0.33.0 // indirect
+	k8s.io/client-go v0.33.0 // indirect
 	k8s.io/klog/v2 v2.130.1 // indirect
-	k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 // indirect
+	k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
 	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
 	sigs.k8s.io/randfill v1.0.0 // indirect
diff --git a/examples/scratch-env/go.sum b/examples/scratch-env/go.sum
index 344a4fd5..e56aae7f 100644
--- a/examples/scratch-env/go.sum
+++ b/examples/scratch-env/go.sum
@@ -83,8 +83,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.22.0-rc.0 h1:meoqLyZIVEIiQxZmyVTOnzk/bA+n2pN2MXN8pSzX2ws=
-github.com/prometheus/client_golang v1.22.0-rc.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
@@ -125,26 +125,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
-golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
 golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -171,18 +171,18 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.33.0-beta.0 h1:/sAUrfXsjKPST2mZjpWhjRdzSR6SD5KlJpiOgCQQhAQ=
-k8s.io/api v0.33.0-beta.0/go.mod h1:TYyCgedkG4OVS4+4D2n25BdbMcexMSLx6Y7OkAzkxLQ=
-k8s.io/apiextensions-apiserver v0.33.0-beta.0 h1:3oqBvfd26IOekt96KEfE8A0wA/k1wDSBfTPirkRun1Q=
-k8s.io/apiextensions-apiserver v0.33.0-beta.0/go.mod h1:TKTeoFcmGvtiDNV+wj8wJfZhamZNOhvi9yOIE2d1iWs=
-k8s.io/apimachinery v0.33.0-beta.0 h1:vLDBChfQwyimk6AbuT7OZOIqxSg/44JlXuxqBk85j68=
-k8s.io/apimachinery v0.33.0-beta.0/go.mod h1:S2OIkExGqJOXYSYcAJwQ9zWcc6BkBUdTJUu4M7z0cvo=
-k8s.io/client-go v0.33.0-beta.0 h1:xRGKK5hU39pb6CFDCDOOlG+LEenB93/RK9hoP4eyAsU=
-k8s.io/client-go v0.33.0-beta.0/go.mod h1:RF6hSu+FncpgHQs1zA1UfGbMq8gxay89r37bCQe+Mj4=
+k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
+k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
+k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
+k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
+k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
+k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
+k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
+k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 h1:t0huyHnz6HsokckRxAF1bY0cqPFwzINKCL7yltEjZQc=
-k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
diff --git a/go.mod b/go.mod
index c72c2dd7..bda2c722 100644
--- a/go.mod
+++ b/go.mod
@@ -13,20 +13,20 @@ require (
 	github.com/google/gofuzz v1.2.0
 	github.com/onsi/ginkgo/v2 v2.22.0
 	github.com/onsi/gomega v1.36.1
-	github.com/prometheus/client_golang v1.22.0-rc.0
+	github.com/prometheus/client_golang v1.22.0
 	github.com/prometheus/client_model v0.6.1
 	go.uber.org/goleak v1.3.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/mod v0.21.0
-	golang.org/x/sync v0.11.0
-	golang.org/x/sys v0.30.0
+	golang.org/x/sync v0.12.0
+	golang.org/x/sys v0.31.0
 	gomodules.xyz/jsonpatch/v2 v2.4.0
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // Using v4 to match upstream
-	k8s.io/api v0.33.0-beta.0
-	k8s.io/apiextensions-apiserver v0.33.0-beta.0
-	k8s.io/apimachinery v0.33.0-beta.0
-	k8s.io/apiserver v0.33.0-beta.0
-	k8s.io/client-go v0.33.0-beta.0
+	k8s.io/api v0.33.0
+	k8s.io/apiextensions-apiserver v0.33.0
+	k8s.io/apimachinery v0.33.0
+	k8s.io/apiserver v0.33.0
+	k8s.io/client-go v0.33.0
 	k8s.io/klog/v2 v2.130.1
 	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
 	sigs.k8s.io/yaml v1.4.0
@@ -79,10 +79,10 @@ require (
 	go.opentelemetry.io/proto/otlp v1.4.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
-	golang.org/x/net v0.33.0 // indirect
+	golang.org/x/net v0.38.0 // indirect
 	golang.org/x/oauth2 v0.27.0 // indirect
-	golang.org/x/term v0.29.0 // indirect
-	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/term v0.30.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
 	golang.org/x/time v0.9.0 // indirect
 	golang.org/x/tools v0.26.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
@@ -91,8 +91,8 @@ require (
 	google.golang.org/protobuf v1.36.5 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/component-base v0.33.0-beta.0 // indirect
-	k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 // indirect
+	k8s.io/component-base v0.33.0 // indirect
+	k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
 	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
 	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
 	sigs.k8s.io/randfill v1.0.0 // indirect
diff --git a/go.sum b/go.sum
index 9f2088c2..ed52e99b 100644
--- a/go.sum
+++ b/go.sum
@@ -102,8 +102,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.22.0-rc.0 h1:meoqLyZIVEIiQxZmyVTOnzk/bA+n2pN2MXN8pSzX2ws=
-github.com/prometheus/client_golang v1.22.0-rc.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
@@ -171,26 +171,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
-golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
 golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
 golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -223,22 +223,22 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.33.0-beta.0 h1:/sAUrfXsjKPST2mZjpWhjRdzSR6SD5KlJpiOgCQQhAQ=
-k8s.io/api v0.33.0-beta.0/go.mod h1:TYyCgedkG4OVS4+4D2n25BdbMcexMSLx6Y7OkAzkxLQ=
-k8s.io/apiextensions-apiserver v0.33.0-beta.0 h1:3oqBvfd26IOekt96KEfE8A0wA/k1wDSBfTPirkRun1Q=
-k8s.io/apiextensions-apiserver v0.33.0-beta.0/go.mod h1:TKTeoFcmGvtiDNV+wj8wJfZhamZNOhvi9yOIE2d1iWs=
-k8s.io/apimachinery v0.33.0-beta.0 h1:vLDBChfQwyimk6AbuT7OZOIqxSg/44JlXuxqBk85j68=
-k8s.io/apimachinery v0.33.0-beta.0/go.mod h1:S2OIkExGqJOXYSYcAJwQ9zWcc6BkBUdTJUu4M7z0cvo=
-k8s.io/apiserver v0.33.0-beta.0 h1:EGjNQ4ocOGEq/KaYFuBS6MiUxZL9WmySu+QpMz+sBrk=
-k8s.io/apiserver v0.33.0-beta.0/go.mod h1:6gxw8BX1YZxi2NtOsFIoURP9bVRkP3sNqle0KVXz1cA=
-k8s.io/client-go v0.33.0-beta.0 h1:xRGKK5hU39pb6CFDCDOOlG+LEenB93/RK9hoP4eyAsU=
-k8s.io/client-go v0.33.0-beta.0/go.mod h1:RF6hSu+FncpgHQs1zA1UfGbMq8gxay89r37bCQe+Mj4=
-k8s.io/component-base v0.33.0-beta.0 h1:EEEzTLuzO1Li+YNHcDLQJgxX6AhfxAZqusYRGbIHfhg=
-k8s.io/component-base v0.33.0-beta.0/go.mod h1:J9MYu3hIiNSNAhjiax9ktqplTpXPLP2RLXhzfJj1ahY=
+k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
+k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
+k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
+k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
+k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
+k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
+k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc=
+k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8=
+k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
+k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
+k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk=
+k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 h1:t0huyHnz6HsokckRxAF1bY0cqPFwzINKCL7yltEjZQc=
-k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
diff --git a/pkg/cache/multi_namespace_cache.go b/pkg/cache/multi_namespace_cache.go
index 525d93e0..d7d7b0e7 100644
--- a/pkg/cache/multi_namespace_cache.go
+++ b/pkg/cache/multi_namespace_cache.go
@@ -279,10 +279,7 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
 		return err
 	}
 
-	allItems, err := apimeta.ExtractList(list)
-	if err != nil {
-		return err
-	}
+	allItems := []runtime.Object{}
 
 	limitSet := listOpts.Limit > 0
 
diff --git a/pkg/controller/controllerutil/controllerutil.go b/pkg/controller/controllerutil/controllerutil.go
index 2051a032..0088f88e 100644
--- a/pkg/controller/controllerutil/controllerutil.go
+++ b/pkg/controller/controllerutil/controllerutil.go
@@ -278,7 +278,7 @@ func referSameObject(a, b metav1.OwnerReference) bool {
 	return aGV.Group == bGV.Group && a.Kind == b.Kind && a.Name == b.Name
 }
 
-// OperationResult is the action result of a CreateOrUpdate call.
+// OperationResult is the action result of a CreateOrUpdate or CreateOrPatch call.
 type OperationResult string
 
 const ( // They should complete the sentence "Deployment default/foo has been ..."
@@ -294,13 +294,26 @@ const ( // They should complete the sentence "Deployment default/foo has been ..
 	OperationResultUpdatedStatusOnly OperationResult = "updatedStatusOnly"
 )
 
-// CreateOrUpdate creates or updates the given object in the Kubernetes
-// cluster. The object's desired state must be reconciled with the existing
-// state inside the passed in callback MutateFn.
+// CreateOrUpdate attempts to fetch the given object from the Kubernetes cluster.
+// If the object didn't exist, MutateFn will be called, and it will be created.
+// If the object did exist, MutateFn will be called, and if it changed the
+// object, it will be updated.
+// Otherwise, it will be left unchanged.
+// The executed operation (and an error) will be returned.
 //
-// The MutateFn is called regardless of creating or updating an object.
+// WARNING: If the MutateFn resets a value on obj that has a default value,
+// CreateOrUpdate will *always* perform an update. This is because when the
+// object is fetched from the API server, the value will have taken on the
+// default value, and the check for equality will fail. For example, Deployments
+// must have a Replicas value set. If the MutateFn sets a Deployment's Replicas
+// to nil, then it will never match with the object returned from the API
+// server, which defaults the value to 1.
 //
-// It returns the executed operation and an error.
+// WARNING: CreateOrUpdate assumes that no values have been set on obj aside
+// from the Name/Namespace. Values other than Name and Namespace that existed on
+// obj may be overwritten by the corresponding values in the object returned
+// from the Kubernetes API server. When this happens, the Update will not work
+// as expected.
 //
 // Note: changes made by MutateFn to any sub-resource (status...), will be
 // discarded.
@@ -339,13 +352,26 @@ func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f M
 	return OperationResultUpdated, nil
 }
 
-// CreateOrPatch creates or patches the given object in the Kubernetes
-// cluster. The object's desired state must be reconciled with the before
-// state inside the passed in callback MutateFn.
+// CreateOrPatch attempts to fetch the given object from the Kubernetes cluster.
+// If the object didn't exist, MutateFn will be called, and it will be created.
+// If the object did exist, MutateFn will be called, and if it changed the
+// object, it will be patched.
+// Otherwise, it will be left unchanged.
+// The executed operation (and an error) will be returned.
 //
-// The MutateFn is called regardless of creating or updating an object.
+// WARNING: If the MutateFn resets a value on obj that has a default value,
+// CreateOrPatch will *always* perform a patch. This is because when the
+// object is fetched from the API server, the value will have taken on the
+// default value, and the check for equality will fail.
+// For example, Deployments must have a Replicas value set. If the MutateFn sets
+// a Deployment's Replicas to nil, then it will never match with the object
+// returned from the API server, which defaults the value to 1.
 //
-// It returns the executed operation and an error.
+// WARNING: CreateOrPatch assumes that no values have been set on obj aside
+// from the Name/Namespace. Values other than Name and Namespace that existed on
+// obj may be overwritten by the corresponding values in the object returned
+// from the Kubernetes API server. When this happens, the Patch will not work
+// as expected.
 //
 // Note: changes to any sub-resource other than status will be ignored.
 // Changes to the status sub-resource will only be applied if the object
diff --git a/pkg/log/zap/flags.go b/pkg/log/zap/flags.go
index fb492b14..2c88ad42 100644
--- a/pkg/log/zap/flags.go
+++ b/pkg/log/zap/flags.go
@@ -32,6 +32,7 @@ var levelStrings = map[string]zapcore.Level{
 	"debug": zap.DebugLevel,
 	"info":  zap.InfoLevel,
 	"error": zap.ErrorLevel,
+	"panic": zap.PanicLevel,
 }
 
 var stackLevelStrings = map[string]zapcore.Level{
diff --git a/pkg/log/zap/kube_helpers.go b/pkg/log/zap/kube_helpers.go
index 3b4ebfda..c47fe664 100644
--- a/pkg/log/zap/kube_helpers.go
+++ b/pkg/log/zap/kube_helpers.go
@@ -78,6 +78,7 @@ func (w kubeObjectWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
 func (k *KubeAwareEncoder) Clone() zapcore.Encoder {
 	return &KubeAwareEncoder{
 		Encoder: k.Encoder.Clone(),
+		Verbose: k.Verbose,
 	}
 }
 
diff --git a/pkg/log/zap/zap.go b/pkg/log/zap/zap.go
index 3a114667..607b6680 100644
--- a/pkg/log/zap/zap.go
+++ b/pkg/log/zap/zap.go
@@ -247,7 +247,7 @@ func NewRaw(opts ...Opts) *zap.Logger {
 //     Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn)
 //     Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
 //   - zap-encoder: Zap log encoding (one of 'json' or 'console')
-//   - zap-log-level: Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error',
+//   - zap-log-level: Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', 'panic'
 //     or any integer value > 0 which corresponds to custom debug levels of increasing verbosity").
 //   - zap-stacktrace-level: Zap Level at and above which stacktraces are captured (one of 'info', 'error' or 'panic')
 //   - zap-time-encoding: Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'),
@@ -271,7 +271,7 @@ func (o *Options) BindFlags(fs *flag.FlagSet) {
 		o.Level = fromFlag
 	}
 	fs.Var(&levelVal, "zap-log-level",
-		"Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', "+
+		"Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', 'panic'"+
 			"or any integer value > 0 which corresponds to custom debug levels of increasing verbosity")
 
 	// Set the StrackTrace Level



Test patch:
diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go
index 9efd0487..4509950f 100644
--- a/pkg/cache/cache_test.go
+++ b/pkg/cache/cache_test.go
@@ -1035,13 +1035,15 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
 							Version: "v1",
 							Kind:    "PodList",
 						})
-						Expect(namespacedCache.List(context.Background(), out)).To(Succeed())
-
-						By("verifying the returned pod is from the watched namespace")
-						Expect(out.Items).NotTo(BeEmpty())
-						Expect(out.Items).Should(HaveLen(2))
-						for _, item := range out.Items {
-							Expect(item.GetNamespace()).To(Equal(testNamespaceOne))
+						for range 2 {
+							Expect(namespacedCache.List(context.Background(), out)).To(Succeed())
+
+							By("verifying the returned pod is from the watched namespace")
+							Expect(out.Items).NotTo(BeEmpty())
+							Expect(out.Items).Should(HaveLen(2))
+							for _, item := range out.Items {
+								Expect(item.GetNamespace()).To(Equal(testNamespaceOne))
+							}
 						}
 						By("listing all nodes - should still be able to list a cluster-scoped resource")
 						nodeList := &unstructured.UnstructuredList{}
diff --git a/pkg/log/zap/zap_test.go b/pkg/log/zap/zap_test.go
index f7fad41f..3e80113a 100644
--- a/pkg/log/zap/zap_test.go
+++ b/pkg/log/zap/zap_test.go
@@ -327,6 +327,24 @@ var _ = Describe("Zap log level flag options setup", func() {
 
 			Expect(outRaw).To(BeEmpty())
 		})
+
+		It("Should output only panic logs, otherwise empty logs", func() {
+			args := []string{"--zap-log-level=panic"}
+			fromFlags.BindFlags(&fs)
+			err := fs.Parse(args)
+			Expect(err).ToNot(HaveOccurred())
+
+			logOut := new(bytes.Buffer)
+
+			logger := New(UseFlagOptions(&fromFlags), WriteTo(logOut))
+			logger.V(0).Info(logInfoLevel0)
+			logger.V(1).Info(logDebugLevel1)
+			logger.V(2).Info(logDebugLevel2)
+
+			outRaw := logOut.Bytes()
+
+			Expect(outRaw).To(BeEmpty())
+		})
 	})
 
 	Context("with  zap-log-level  with increased verbosity.", func() {
diff --git a/tools/setup-envtest/go.mod b/tools/setup-envtest/go.mod
index 3463637a..62b88736 100644
--- a/tools/setup-envtest/go.mod
+++ b/tools/setup-envtest/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/spf13/afero v1.12.0
 	github.com/spf13/pflag v1.0.6
 	go.uber.org/zap v1.27.0
-	k8s.io/apimachinery v0.33.0-beta.0
+	k8s.io/apimachinery v0.33.0
 	sigs.k8s.io/yaml v1.4.0
 )
 
@@ -19,9 +19,9 @@ require (
 	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
 	go.uber.org/multierr v1.10.0 // indirect
-	golang.org/x/net v0.33.0 // indirect
-	golang.org/x/sys v0.30.0 // indirect
-	golang.org/x/text v0.22.0 // indirect
+	golang.org/x/net v0.38.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
 	golang.org/x/tools v0.28.0 // indirect
 	google.golang.org/protobuf v1.36.5 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/tools/setup-envtest/go.sum b/tools/setup-envtest/go.sum
index da3f82ed..ed5625af 100644
--- a/tools/setup-envtest/go.sum
+++ b/tools/setup-envtest/go.sum
@@ -29,12 +29,12 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
 go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
 go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
-golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
-golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
 golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
 google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
@@ -43,7 +43,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/apimachinery v0.33.0-beta.0 h1:vLDBChfQwyimk6AbuT7OZOIqxSg/44JlXuxqBk85j68=
-k8s.io/apimachinery v0.33.0-beta.0/go.mod h1:S2OIkExGqJOXYSYcAJwQ9zWcc6BkBUdTJUu4M7z0cvo=
+k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
+k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
 sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
 sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=