mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Merge pull request #125335 from p0lyn0mial/upstream-consistency-decector-handles-legacy-case
client-go/consistencydetector: handles the watch cache legacy case
This commit is contained in:
commit
dae37c2164
@ -46,8 +46,9 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
klog.Warningf("data consistency check for %s is enabled, this will result in an additional call to the API server.", identity)
|
klog.Warningf("data consistency check for %s is enabled, this will result in an additional call to the API server.", identity)
|
||||||
listOptions.ResourceVersion = lastSyncedResourceVersion
|
|
||||||
listOptions.ResourceVersionMatch = metav1.ResourceVersionMatchExact
|
retrievedItems := toMetaObjectSliceOrDie(retrieveItemsFn())
|
||||||
|
listOptions = prepareListCallOptions(lastSyncedResourceVersion, listOptions, len(retrievedItems))
|
||||||
var list runtime.Object
|
var list runtime.Object
|
||||||
err := wait.PollUntilContextCancel(ctx, time.Second, true, func(_ context.Context) (done bool, err error) {
|
err := wait.PollUntilContextCancel(ctx, time.Second, true, func(_ context.Context) (done bool, err error) {
|
||||||
list, err = listFn(ctx, listOptions)
|
list, err = listFn(ctx, listOptions)
|
||||||
@ -69,9 +70,7 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // this should never happen
|
panic(err) // this should never happen
|
||||||
}
|
}
|
||||||
|
|
||||||
listItems := toMetaObjectSliceOrDie(rawListItems)
|
listItems := toMetaObjectSliceOrDie(rawListItems)
|
||||||
retrievedItems := toMetaObjectSliceOrDie(retrieveItemsFn())
|
|
||||||
|
|
||||||
sort.Sort(byUID(listItems))
|
sort.Sort(byUID(listItems))
|
||||||
sort.Sort(byUID(retrievedItems))
|
sort.Sort(byUID(retrievedItems))
|
||||||
@ -85,24 +84,49 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity
|
|||||||
|
|
||||||
// canFormAdditionalListCall ensures that we can form a valid LIST requests
|
// canFormAdditionalListCall ensures that we can form a valid LIST requests
|
||||||
// for checking data consistency.
|
// for checking data consistency.
|
||||||
func canFormAdditionalListCall(resourceVersion string, options metav1.ListOptions) bool {
|
func canFormAdditionalListCall(lastSyncedResourceVersion string, listOptions metav1.ListOptions) bool {
|
||||||
// since we are setting ResourceVersionMatch to metav1.ResourceVersionMatchExact
|
// since we are setting ResourceVersionMatch to metav1.ResourceVersionMatchExact
|
||||||
// we need to make sure that the continuation hasn't been set
|
// we need to make sure that the continuation hasn't been set
|
||||||
// https://github.com/kubernetes/kubernetes/blob/be4afb9ef90b19ccb6f7e595cbdb247e088b2347/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation.go#L38
|
// https://github.com/kubernetes/kubernetes/blob/be4afb9ef90b19ccb6f7e595cbdb247e088b2347/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation.go#L38
|
||||||
if len(options.Continue) > 0 {
|
if len(listOptions.Continue) > 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// since we are setting ResourceVersionMatch to metav1.ResourceVersionMatchExact
|
// since we are setting ResourceVersionMatch to metav1.ResourceVersionMatchExact
|
||||||
// we need to make sure that the RV is valid because the validation code forbids RV == "0"
|
// we need to make sure that the RV is valid because the validation code forbids RV == "0"
|
||||||
// https://github.com/kubernetes/kubernetes/blob/be4afb9ef90b19ccb6f7e595cbdb247e088b2347/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation.go#L44
|
// https://github.com/kubernetes/kubernetes/blob/be4afb9ef90b19ccb6f7e595cbdb247e088b2347/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation.go#L44
|
||||||
if resourceVersion == "0" {
|
if lastSyncedResourceVersion == "0" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepareListCallOptions changes the input list options so that
|
||||||
|
// the list call goes directly to etcd
|
||||||
|
func prepareListCallOptions(lastSyncedResourceVersion string, listOptions metav1.ListOptions, retrievedItemsCount int) metav1.ListOptions {
|
||||||
|
// this is our legacy case:
|
||||||
|
//
|
||||||
|
// the watch cache skips the Limit if the ResourceVersion was set to "0"
|
||||||
|
// thus, to compare with data retrieved directly from etcd
|
||||||
|
// we need to skip the limit to for the list call as well.
|
||||||
|
//
|
||||||
|
// note that when the number of retrieved items is less than the request limit,
|
||||||
|
// it means either the watch cache is disabled, or there is not enough data.
|
||||||
|
// in both cases, we can use the limit because we will be able to compare
|
||||||
|
// the data with the items retrieved from etcd.
|
||||||
|
if listOptions.ResourceVersion == "0" && listOptions.Limit > 0 && int64(retrievedItemsCount) > listOptions.Limit {
|
||||||
|
listOptions.Limit = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the RV and RVM so that we get the snapshot of data
|
||||||
|
// directly from etcd.
|
||||||
|
listOptions.ResourceVersion = lastSyncedResourceVersion
|
||||||
|
listOptions.ResourceVersionMatch = metav1.ResourceVersionMatchExact
|
||||||
|
|
||||||
|
return listOptions
|
||||||
|
}
|
||||||
|
|
||||||
type byUID []metav1.Object
|
type byUID []metav1.Object
|
||||||
|
|
||||||
func (a byUID) Len() int { return len(a) }
|
func (a byUID) Len() int { return len(a) }
|
||||||
|
@ -60,6 +60,59 @@ func TestDataConsistencyChecker(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "legacy, the limit is removed from the list options when it wasn't honored by the watch cache",
|
||||||
|
listResponse: &v1.PodList{
|
||||||
|
ListMeta: metav1.ListMeta{ResourceVersion: "2"},
|
||||||
|
Items: []v1.Pod{*makePod("p1", "1"), *makePod("p2", "2"), *makePod("p3", "3")},
|
||||||
|
},
|
||||||
|
requestOptions: metav1.ListOptions{ResourceVersion: "0", Limit: 2},
|
||||||
|
retrievedItems: []*v1.Pod{makePod("p1", "1"), makePod("p2", "2"), makePod("p3", "3")},
|
||||||
|
expectedListRequests: 1,
|
||||||
|
expectedRequestOptions: []metav1.ListOptions{
|
||||||
|
{
|
||||||
|
ResourceVersion: "2",
|
||||||
|
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "the limit is NOT removed from the list options for non-legacy request",
|
||||||
|
listResponse: &v1.PodList{
|
||||||
|
ListMeta: metav1.ListMeta{ResourceVersion: "2"},
|
||||||
|
Items: []v1.Pod{*makePod("p1", "1"), *makePod("p2", "2"), *makePod("p3", "3")},
|
||||||
|
},
|
||||||
|
requestOptions: metav1.ListOptions{ResourceVersion: "2", Limit: 2},
|
||||||
|
retrievedItems: []*v1.Pod{makePod("p1", "1"), makePod("p2", "2"), makePod("p3", "3")},
|
||||||
|
expectedListRequests: 1,
|
||||||
|
expectedRequestOptions: []metav1.ListOptions{
|
||||||
|
{
|
||||||
|
Limit: 2,
|
||||||
|
ResourceVersion: "2",
|
||||||
|
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "legacy, the limit is NOT removed from the list options when the watch cache is disabled",
|
||||||
|
listResponse: &v1.PodList{
|
||||||
|
ListMeta: metav1.ListMeta{ResourceVersion: "2"},
|
||||||
|
Items: []v1.Pod{*makePod("p1", "1"), *makePod("p2", "2"), *makePod("p3", "3")},
|
||||||
|
},
|
||||||
|
requestOptions: metav1.ListOptions{ResourceVersion: "0", Limit: 5},
|
||||||
|
retrievedItems: []*v1.Pod{makePod("p1", "1"), makePod("p2", "2"), makePod("p3", "3")},
|
||||||
|
expectedListRequests: 1,
|
||||||
|
expectedRequestOptions: []metav1.ListOptions{
|
||||||
|
{
|
||||||
|
Limit: 5,
|
||||||
|
ResourceVersion: "2",
|
||||||
|
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "data consistency check won't panic when there is no data",
|
name: "data consistency check won't panic when there is no data",
|
||||||
listResponse: &v1.PodList{
|
listResponse: &v1.PodList{
|
||||||
|
Loading…
Reference in New Issue
Block a user