Skip to content

Commit dae37c2

Browse files
authored
Merge pull request kubernetes#125335 from p0lyn0mial/upstream-consistency-decector-handles-legacy-case
client-go/consistencydetector: handles the watch cache legacy case
2 parents e67f889 + fe8a2d2 commit dae37c2

File tree

2 files changed

+84
-7
lines changed

2 files changed

+84
-7
lines changed

staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity
4646
return
4747
}
4848
klog.Warningf("data consistency check for %s is enabled, this will result in an additional call to the API server.", identity)
49-
listOptions.ResourceVersion = lastSyncedResourceVersion
50-
listOptions.ResourceVersionMatch = metav1.ResourceVersionMatchExact
49+
50+
retrievedItems := toMetaObjectSliceOrDie(retrieveItemsFn())
51+
listOptions = prepareListCallOptions(lastSyncedResourceVersion, listOptions, len(retrievedItems))
5152
var list runtime.Object
5253
err := wait.PollUntilContextCancel(ctx, time.Second, true, func(_ context.Context) (done bool, err error) {
5354
list, err = listFn(ctx, listOptions)
@@ -69,9 +70,7 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity
6970
if err != nil {
7071
panic(err) // this should never happen
7172
}
72-
7373
listItems := toMetaObjectSliceOrDie(rawListItems)
74-
retrievedItems := toMetaObjectSliceOrDie(retrieveItemsFn())
7574

7675
sort.Sort(byUID(listItems))
7776
sort.Sort(byUID(retrievedItems))
@@ -85,24 +84,49 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity
8584

8685
// canFormAdditionalListCall ensures that we can form a valid LIST requests
8786
// for checking data consistency.
88-
func canFormAdditionalListCall(resourceVersion string, options metav1.ListOptions) bool {
87+
func canFormAdditionalListCall(lastSyncedResourceVersion string, listOptions metav1.ListOptions) bool {
8988
// since we are setting ResourceVersionMatch to metav1.ResourceVersionMatchExact
9089
// we need to make sure that the continuation hasn't been set
9190
// https://github.com/kubernetes/kubernetes/blob/be4afb9ef90b19ccb6f7e595cbdb247e088b2347/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation.go#L38
92-
if len(options.Continue) > 0 {
91+
if len(listOptions.Continue) > 0 {
9392
return false
9493
}
9594

9695
// since we are setting ResourceVersionMatch to metav1.ResourceVersionMatchExact
9796
// we need to make sure that the RV is valid because the validation code forbids RV == "0"
9897
// https://github.com/kubernetes/kubernetes/blob/be4afb9ef90b19ccb6f7e595cbdb247e088b2347/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation.go#L44
99-
if resourceVersion == "0" {
98+
if lastSyncedResourceVersion == "0" {
10099
return false
101100
}
102101

103102
return true
104103
}
105104

105+
// prepareListCallOptions changes the input list options so that
106+
// the list call goes directly to etcd
107+
func prepareListCallOptions(lastSyncedResourceVersion string, listOptions metav1.ListOptions, retrievedItemsCount int) metav1.ListOptions {
108+
// this is our legacy case:
109+
//
110+
// the watch cache skips the Limit if the ResourceVersion was set to "0"
111+
// thus, to compare with data retrieved directly from etcd
112+
// we need to skip the limit to for the list call as well.
113+
//
114+
// note that when the number of retrieved items is less than the request limit,
115+
// it means either the watch cache is disabled, or there is not enough data.
116+
// in both cases, we can use the limit because we will be able to compare
117+
// the data with the items retrieved from etcd.
118+
if listOptions.ResourceVersion == "0" && listOptions.Limit > 0 && int64(retrievedItemsCount) > listOptions.Limit {
119+
listOptions.Limit = 0
120+
}
121+
122+
// set the RV and RVM so that we get the snapshot of data
123+
// directly from etcd.
124+
listOptions.ResourceVersion = lastSyncedResourceVersion
125+
listOptions.ResourceVersionMatch = metav1.ResourceVersionMatchExact
126+
127+
return listOptions
128+
}
129+
106130
type byUID []metav1.Object
107131

108132
func (a byUID) Len() int { return len(a) }

staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,59 @@ func TestDataConsistencyChecker(t *testing.T) {
6060
},
6161
},
6262

63+
{
64+
name: "legacy, the limit is removed from the list options when it wasn't honored by the watch cache",
65+
listResponse: &v1.PodList{
66+
ListMeta: metav1.ListMeta{ResourceVersion: "2"},
67+
Items: []v1.Pod{*makePod("p1", "1"), *makePod("p2", "2"), *makePod("p3", "3")},
68+
},
69+
requestOptions: metav1.ListOptions{ResourceVersion: "0", Limit: 2},
70+
retrievedItems: []*v1.Pod{makePod("p1", "1"), makePod("p2", "2"), makePod("p3", "3")},
71+
expectedListRequests: 1,
72+
expectedRequestOptions: []metav1.ListOptions{
73+
{
74+
ResourceVersion: "2",
75+
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
76+
},
77+
},
78+
},
79+
80+
{
81+
name: "the limit is NOT removed from the list options for non-legacy request",
82+
listResponse: &v1.PodList{
83+
ListMeta: metav1.ListMeta{ResourceVersion: "2"},
84+
Items: []v1.Pod{*makePod("p1", "1"), *makePod("p2", "2"), *makePod("p3", "3")},
85+
},
86+
requestOptions: metav1.ListOptions{ResourceVersion: "2", Limit: 2},
87+
retrievedItems: []*v1.Pod{makePod("p1", "1"), makePod("p2", "2"), makePod("p3", "3")},
88+
expectedListRequests: 1,
89+
expectedRequestOptions: []metav1.ListOptions{
90+
{
91+
Limit: 2,
92+
ResourceVersion: "2",
93+
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
94+
},
95+
},
96+
},
97+
98+
{
99+
name: "legacy, the limit is NOT removed from the list options when the watch cache is disabled",
100+
listResponse: &v1.PodList{
101+
ListMeta: metav1.ListMeta{ResourceVersion: "2"},
102+
Items: []v1.Pod{*makePod("p1", "1"), *makePod("p2", "2"), *makePod("p3", "3")},
103+
},
104+
requestOptions: metav1.ListOptions{ResourceVersion: "0", Limit: 5},
105+
retrievedItems: []*v1.Pod{makePod("p1", "1"), makePod("p2", "2"), makePod("p3", "3")},
106+
expectedListRequests: 1,
107+
expectedRequestOptions: []metav1.ListOptions{
108+
{
109+
Limit: 5,
110+
ResourceVersion: "2",
111+
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
112+
},
113+
},
114+
},
115+
63116
{
64117
name: "data consistency check won't panic when there is no data",
65118
listResponse: &v1.PodList{

0 commit comments

Comments
 (0)