From 26a79e4c0ba88af77392b2a092d8030943d3079b Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 18 Jan 2024 15:32:35 +0200 Subject: [PATCH] kubeadm: special case context errors in GetConfigMapWithShortRetry If some code is about to go over the context deadline, "x/time/rate/rate.go" would return and untyped error with the string "would exceed context deadline". If some code already exceeded the deadline the error would be of type DeadlineExceeded. Ignore such context errors and only store API and connectivity errors. --- cmd/kubeadm/app/util/apiclient/idempotency.go | 9 +++- .../app/util/apiclient/idempotency_test.go | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/cmd/kubeadm/app/util/apiclient/idempotency.go b/cmd/kubeadm/app/util/apiclient/idempotency.go index 828dab9c7d7..8e6535edda7 100644 --- a/cmd/kubeadm/app/util/apiclient/idempotency.go +++ b/cmd/kubeadm/app/util/apiclient/idempotency.go @@ -19,6 +19,7 @@ package apiclient import ( "context" "encoding/json" + "strings" "time" "github.com/pkg/errors" @@ -346,7 +347,13 @@ func GetConfigMapWithShortRetry(client clientset.Interface, namespace, name stri if err == nil { return true, nil } - lastError = err + // If some code is about to go over the context deadline, "x/time/rate/rate.go" would return + // and untyped error with the string "would exceed context deadline". If some code already exceeded + // the deadline the error would be of type DeadlineExceeded. Ignore such context errors and only store + // API and connectivity errors. + if !strings.Contains(err.Error(), "would exceed context deadline") && !errors.Is(err, context.DeadlineExceeded) { + lastError = err + } return false, nil }) if err == nil { diff --git a/cmd/kubeadm/app/util/apiclient/idempotency_test.go b/cmd/kubeadm/app/util/apiclient/idempotency_test.go index 9a9e3f18f64..19c8de2a74e 100644 --- a/cmd/kubeadm/app/util/apiclient/idempotency_test.go +++ b/cmd/kubeadm/app/util/apiclient/idempotency_test.go @@ -237,3 +237,54 @@ func TestMutateConfigMapWithConflict(t *testing.T) { t.Fatalf("ConfigMap mutation with conflict was invalid, has: %q", cm.Data["key"]) } } + +func TestGetConfigMapWithShortRetry(t *testing.T) { + testcases := []struct { + name string + reactorFunc func(core.Action) (bool, runtime.Object, error) + errorCheckFunc func(*testing.T, error) + }{ + { + name: "context deadline exceeded error is handled", + reactorFunc: func(core.Action) (bool, runtime.Object, error) { + return true, nil, context.DeadlineExceeded + }, + errorCheckFunc: func(t *testing.T, err error) { + if err != nil { + t.Errorf("unexpected error: %v", err) + } + }, + }, + { + name: "would exceed context deadline error is handled", + reactorFunc: func(core.Action) (bool, runtime.Object, error) { + return true, nil, errors.New("Wait returned an error: rate: Wait(n=1) would exceed context deadline") + }, + errorCheckFunc: func(t *testing.T, err error) { + if err != nil { + t.Errorf("unexpected error: %v", err) + } + }, + }, + { + name: "API error is not handled", + reactorFunc: func(core.Action) (bool, runtime.Object, error) { + return true, nil, apierrors.NewNotFound(schema.GroupResource{}, "") + }, + errorCheckFunc: func(t *testing.T, err error) { + if !apierrors.IsNotFound(err) { + t.Errorf("expected error: IsNotFound, got: %v", err) + } + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + client := fake.NewSimpleClientset() + client.PrependReactor("get", "configmaps", tc.reactorFunc) + _, err := GetConfigMapWithShortRetry(client, "foo", "bar") + tc.errorCheckFunc(t, err) + }) + } +}