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) + }) + } +}