@@ -24,6 +24,7 @@ import (
24
24
"k8s.io/api/core/v1"
25
25
discovery "k8s.io/api/discovery/v1beta1"
26
26
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
+ "k8s.io/apimachinery/pkg/runtime"
27
28
"k8s.io/apimachinery/pkg/types"
28
29
utilpointer "k8s.io/utils/pointer"
29
30
)
@@ -152,11 +153,13 @@ func TestEndpointsMapFromESC(t *testing.T) {
152
153
t .Run (name , func (t * testing.T ) {
153
154
esCache := NewEndpointSliceCache (tc .hostname , nil , nil , nil )
154
155
156
+ cmc := newCacheMutationCheck (tc .endpointSlices )
155
157
for _ , endpointSlice := range tc .endpointSlices {
156
158
esCache .updatePending (endpointSlice , false )
157
159
}
158
160
159
161
compareEndpointsMapsStr (t , esCache .getEndpointsMap (tc .namespacedName , esCache .trackerByServiceMap [tc .namespacedName ].pending ), tc .expectedMap )
162
+ cmc .Check (t )
160
163
})
161
164
}
162
165
}
@@ -315,6 +318,8 @@ func TestEsInfoChanged(t *testing.T) {
315
318
316
319
for name , tc := range testCases {
317
320
t .Run (name , func (t * testing.T ) {
321
+ cmc := newCacheMutationCheck ([]* discovery.EndpointSlice {tc .initialSlice })
322
+
318
323
if tc .initialSlice != nil {
319
324
tc .cache .updatePending (tc .initialSlice , false )
320
325
tc .cache .checkoutChanges ()
@@ -331,6 +336,8 @@ func TestEsInfoChanged(t *testing.T) {
331
336
if tc .expectChanged != changed {
332
337
t .Errorf ("Expected esInfoChanged() to return %t, got %t" , tc .expectChanged , changed )
333
338
}
339
+
340
+ cmc .Check (t )
334
341
})
335
342
}
336
343
}
@@ -378,3 +385,45 @@ func generateEndpointSliceWithOffset(serviceName, namespace string, sliceNum, of
378
385
func generateEndpointSlice (serviceName , namespace string , sliceNum , numEndpoints , unreadyMod int , hosts []string , portNums []* int32 ) * discovery.EndpointSlice {
379
386
return generateEndpointSliceWithOffset (serviceName , namespace , sliceNum , sliceNum , numEndpoints , unreadyMod , hosts , portNums )
380
387
}
388
+
389
+ // cacheMutationCheck helps ensure that cached objects have not been changed
390
+ // in any way throughout a test run.
391
+ type cacheMutationCheck struct {
392
+ objects []cacheObject
393
+ }
394
+
395
+ // cacheObject stores a reference to an original object as well as a deep copy
396
+ // of that object to track any mutations in the original object.
397
+ type cacheObject struct {
398
+ original runtime.Object
399
+ deepCopy runtime.Object
400
+ }
401
+
402
+ // newCacheMutationCheck initializes a cacheMutationCheck with EndpointSlices.
403
+ func newCacheMutationCheck (endpointSlices []* discovery.EndpointSlice ) cacheMutationCheck {
404
+ cmc := cacheMutationCheck {}
405
+ for _ , endpointSlice := range endpointSlices {
406
+ cmc .Add (endpointSlice )
407
+ }
408
+ return cmc
409
+ }
410
+
411
+ // Add appends a runtime.Object and a deep copy of that object into the
412
+ // cacheMutationCheck.
413
+ func (cmc * cacheMutationCheck ) Add (o runtime.Object ) {
414
+ cmc .objects = append (cmc .objects , cacheObject {
415
+ original : o ,
416
+ deepCopy : o .DeepCopyObject (),
417
+ })
418
+ }
419
+
420
+ // Check verifies that no objects in the cacheMutationCheck have been mutated.
421
+ func (cmc * cacheMutationCheck ) Check (t * testing.T ) {
422
+ for _ , o := range cmc .objects {
423
+ if ! reflect .DeepEqual (o .original , o .deepCopy ) {
424
+ // Cached objects can't be safely mutated and instead should be deep
425
+ // copied before changed in any way.
426
+ t .Errorf ("Cached object was unexpectedly mutated. Original: %+v, Mutated: %+v" , o .deepCopy , o .original )
427
+ }
428
+ }
429
+ }
0 commit comments