@@ -18,6 +18,7 @@ package cache
18
18
19
19
import (
20
20
"context"
21
+ "fmt"
21
22
"os"
22
23
"sort"
23
24
"strconv"
@@ -32,42 +33,46 @@ import (
32
33
"k8s.io/klog/v2"
33
34
)
34
35
35
- var dataConsistencyDetectionEnabled = false
36
+ var dataConsistencyDetectionForWatchListEnabled = false
36
37
37
38
func init () {
38
- dataConsistencyDetectionEnabled , _ = strconv .ParseBool (os .Getenv ("KUBE_WATCHLIST_INCONSISTENCY_DETECTOR" ))
39
+ dataConsistencyDetectionForWatchListEnabled , _ = strconv .ParseBool (os .Getenv ("KUBE_WATCHLIST_INCONSISTENCY_DETECTOR" ))
39
40
}
40
41
41
- // checkWatchListConsistencyIfRequested performs a data consistency check only when
42
+ type retrieveItemsFunc [U any ] func () []U
43
+
44
+ type listFunc [T runtime.Object ] func (ctx context.Context , options metav1.ListOptions ) (T , error )
45
+
46
+ // checkWatchListDataConsistencyIfRequested performs a data consistency check only when
42
47
// the KUBE_WATCHLIST_INCONSISTENCY_DETECTOR environment variable was set during a binary startup.
43
48
//
44
49
// The consistency check is meant to be enforced only in the CI, not in production.
45
50
// The check ensures that data retrieved by the watch-list api call
46
- // is exactly the same as data received by the standard list api call.
51
+ // is exactly the same as data received by the standard list api call against etcd .
47
52
//
48
53
// Note that this function will panic when data inconsistency is detected.
49
54
// This is intentional because we want to catch it in the CI.
50
- func checkWatchListConsistencyIfRequested ( stopCh <- chan struct {} , identity string , lastSyncedResourceVersion string , listerWatcher Lister , store Store ) {
51
- if ! dataConsistencyDetectionEnabled {
55
+ func checkWatchListDataConsistencyIfRequested [ T runtime. Object , U any ]( ctx context. Context , identity string , lastSyncedResourceVersion string , listFn listFunc [ T ], retrieveItemsFn retrieveItemsFunc [ U ] ) {
56
+ if ! dataConsistencyDetectionForWatchListEnabled {
52
57
return
53
58
}
54
- checkWatchListConsistency (stopCh , identity , lastSyncedResourceVersion , listerWatcher , store )
59
+ // for informers we pass an empty ListOptions because
60
+ // listFn might be wrapped for filtering during informer construction.
61
+ checkDataConsistency (ctx , identity , lastSyncedResourceVersion , listFn , metav1.ListOptions {}, retrieveItemsFn )
55
62
}
56
63
57
- // checkWatchListConsistency exists solely for testing purposes.
58
- // we cannot use checkWatchListConsistencyIfRequested because
64
+ // checkDataConsistency exists solely for testing purposes.
65
+ // we cannot use checkWatchListDataConsistencyIfRequested because
59
66
// it is guarded by an environmental variable.
60
67
// we cannot manipulate the environmental variable because
61
68
// it will affect other tests in this package.
62
- func checkWatchListConsistency (stopCh <- chan struct {}, identity string , lastSyncedResourceVersion string , listerWatcher Lister , store Store ) {
63
- klog .Warningf ("%s: data consistency check for the watch-list feature is enabled, this will result in an additional call to the API server." , identity )
64
- opts := metav1.ListOptions {
65
- ResourceVersion : lastSyncedResourceVersion ,
66
- ResourceVersionMatch : metav1 .ResourceVersionMatchExact ,
67
- }
69
+ func checkDataConsistency [T runtime.Object , U any ](ctx context.Context , identity string , lastSyncedResourceVersion string , listFn listFunc [T ], listOptions metav1.ListOptions , retrieveItemsFn retrieveItemsFunc [U ]) {
70
+ klog .Warningf ("data consistency check for %s is enabled, this will result in an additional call to the API server." , identity )
71
+ listOptions .ResourceVersion = lastSyncedResourceVersion
72
+ listOptions .ResourceVersionMatch = metav1 .ResourceVersionMatchExact
68
73
var list runtime.Object
69
- err := wait .PollUntilContextCancel (wait . ContextForChannel ( stopCh ) , time .Second , true , func (_ context.Context ) (done bool , err error ) {
70
- list , err = listerWatcher . List ( opts )
74
+ err := wait .PollUntilContextCancel (ctx , time .Second , true , func (_ context.Context ) (done bool , err error ) {
75
+ list , err = listFn ( ctx , listOptions )
71
76
if err != nil {
72
77
// the consistency check will only be enabled in the CI
73
78
// and LIST calls in general will be retired by the client-go library
@@ -78,7 +83,7 @@ func checkWatchListConsistency(stopCh <-chan struct{}, identity string, lastSync
78
83
return true , nil
79
84
})
80
85
if err != nil {
81
- klog .Errorf ("failed to list data from the server, the watch-list consistency check won't be performed, stopCh was closed, err: %v" , err )
86
+ klog .Errorf ("failed to list data from the server, the data consistency check for %s won't be performed, stopCh was closed, err: %v" , identity , err )
82
87
return
83
88
}
84
89
@@ -88,14 +93,14 @@ func checkWatchListConsistency(stopCh <-chan struct{}, identity string, lastSync
88
93
}
89
94
90
95
listItems := toMetaObjectSliceOrDie (rawListItems )
91
- storeItems := toMetaObjectSliceOrDie (store . List ())
96
+ retrievedItems := toMetaObjectSliceOrDie (retrieveItemsFn ())
92
97
93
98
sort .Sort (byUID (listItems ))
94
- sort .Sort (byUID (storeItems ))
99
+ sort .Sort (byUID (retrievedItems ))
95
100
96
- if ! cmp .Equal (listItems , storeItems ) {
97
- klog .Infof ("%s: data received by the new watch-list api call is different than received by the standard list api call, diff: %v" , identity , cmp .Diff (listItems , storeItems ))
98
- msg := "data inconsistency detected for the watch-list feature , panicking!"
101
+ if ! cmp .Equal (listItems , retrievedItems ) {
102
+ klog .Infof ("previously received data for %s is different than received by the standard list api call against etcd , diff: %v" , identity , cmp .Diff (listItems , retrievedItems ))
103
+ msg := fmt . Sprintf ( "data inconsistency detected for %s , panicking!" , identity )
99
104
panic (msg )
100
105
}
101
106
}
0 commit comments