From 7685b5191246c689ffb9656a6c2cf73fc59e14e9 Mon Sep 17 00:00:00 2001 From: xuzhenglun Date: Mon, 16 Jan 2023 11:36:28 +0800 Subject: [PATCH] Fix bug in reflector not detecting "Too large resource version" error before 1.17.0 Kubernetes-commit: 11e5e92dc65c1caaeb248a60d90ccc8e29eb9ff0 --- tools/cache/reflector.go | 7 +++++++ tools/cache/reflector_test.go | 13 ++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tools/cache/reflector.go b/tools/cache/reflector.go index 9aaaac61..b320b7a1 100644 --- a/tools/cache/reflector.go +++ b/tools/cache/reflector.go @@ -23,6 +23,7 @@ import ( "io" "math/rand" "reflect" + "strings" "sync" "time" @@ -693,5 +694,11 @@ func isTooLargeResourceVersionError(err error) bool { return true } } + + // Matches the message returned by api server before 1.17.0 + if strings.Contains(apierr.Status().Message, "Too large resource version") { + return true + } + return false } diff --git a/tools/cache/reflector_test.go b/tools/cache/reflector_test.go index fe2b052b..d772a5d2 100644 --- a/tools/cache/reflector_test.go +++ b/tools/cache/reflector_test.go @@ -919,6 +919,7 @@ func TestReflectorFullListIfTooLarge(t *testing.T) { stopCh := make(chan struct{}) s := NewStore(MetaNamespaceKeyFunc) listCallRVs := []string{} + version := 30 lw := &testLW{ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { @@ -929,6 +930,7 @@ func TestReflectorFullListIfTooLarge(t *testing.T) { }, ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { listCallRVs = append(listCallRVs, options.ResourceVersion) + resourceVersion := strconv.Itoa(version) switch options.ResourceVersion { // initial list @@ -944,9 +946,14 @@ func TestReflectorFullListIfTooLarge(t *testing.T) { err := apierrors.NewTimeoutError("too large resource version", 1) err.ErrStatus.Details.Causes = []metav1.StatusCause{{Message: "Too large resource version"}} return nil, err + // relist after the initial list (covers the error format used in api server before 1.17.0) + case "40": + err := apierrors.NewTimeoutError("Too large resource version", 1) + return nil, err // relist from etcd after "too large" error case "": - return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "30"}}, nil + version += 10 + return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: resourceVersion}}, nil default: return nil, fmt.Errorf("unexpected List call: %s", options.ResourceVersion) } @@ -965,7 +972,7 @@ func TestReflectorFullListIfTooLarge(t *testing.T) { // may be synced to a different version and they will never converge. // TODO: We should use etcd progress-notify feature to avoid this behavior but until this is // done we simply try to relist from now to avoid continuous errors on relists. - for i := 1; i <= 2; i++ { + for i := 1; i <= 3; i++ { // relist twice to cover the two variants of TooLargeResourceVersion api errors stopCh = make(chan struct{}) if err := r.ListAndWatch(stopCh); err != nil { @@ -973,7 +980,7 @@ func TestReflectorFullListIfTooLarge(t *testing.T) { } } - expectedRVs := []string{"0", "20", "", "30", ""} + expectedRVs := []string{"0", "20", "", "30", "", "40", ""} if !reflect.DeepEqual(listCallRVs, expectedRVs) { t.Errorf("Expected series of list calls with resource version of %#v but got: %#v", expectedRVs, listCallRVs) }