Merge pull request #128759 from omerap12/PollUntilContextCancel-apiextensions-apiserver

chore: update deprecated polling methods in apiextensions-apiserver
This commit is contained in:
Kubernetes Prow Robot 2024-12-16 12:24:53 +01:00 committed by GitHub
commit ec1e7fea17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 74 additions and 63 deletions

View File

@ -17,6 +17,7 @@ limitations under the License.
package apiserver package apiserver
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
@ -258,14 +259,14 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
// we don't want to report healthy until we can handle all CRDs that have already been registered. Waiting for the informer // we don't want to report healthy until we can handle all CRDs that have already been registered. Waiting for the informer
// to sync makes sure that the lister will be valid before we begin. There may still be races for CRDs added after startup, // to sync makes sure that the lister will be valid before we begin. There may still be races for CRDs added after startup,
// but we won't go healthy until we can handle the ones already present. // but we won't go healthy until we can handle the ones already present.
s.GenericAPIServer.AddPostStartHookOrDie("crd-informer-synced", func(context genericapiserver.PostStartHookContext) error { s.GenericAPIServer.AddPostStartHookOrDie("crd-informer-synced", func(ctx genericapiserver.PostStartHookContext) error {
return wait.PollImmediateUntil(100*time.Millisecond, func() (bool, error) { return wait.PollUntilContextCancel(ctx.Context, 100*time.Millisecond, true, func(ctx context.Context) (bool, error) {
if s.Informers.Apiextensions().V1().CustomResourceDefinitions().Informer().HasSynced() { if s.Informers.Apiextensions().V1().CustomResourceDefinitions().Informer().HasSynced() {
close(hasCRDInformerSyncedSignal) close(hasCRDInformerSyncedSignal)
return true, nil return true, nil
} }
return false, nil return false, nil
}, context.Done()) })
}) })
return s, nil return s, nil

View File

@ -17,6 +17,8 @@ limitations under the License.
package apiserver package apiserver
import ( import (
"context"
"errors"
"fmt" "fmt"
"sort" "sort"
"time" "time"
@ -297,7 +299,7 @@ func (c *DiscoveryController) Run(stopCh <-chan struct{}, synchedCh chan<- struc
} }
// initially sync all group versions to make sure we serve complete discovery // initially sync all group versions to make sure we serve complete discovery
if err := wait.PollImmediateUntil(time.Second, func() (bool, error) { if err := wait.PollUntilContextCancel(context.Background(), time.Second, true, func(ctx context.Context) (bool, error) {
crds, err := c.crdLister.List(labels.Everything()) crds, err := c.crdLister.List(labels.Everything())
if err != nil { if err != nil {
utilruntime.HandleError(fmt.Errorf("failed to initially list CRDs: %v", err)) utilruntime.HandleError(fmt.Errorf("failed to initially list CRDs: %v", err))
@ -313,10 +315,11 @@ func (c *DiscoveryController) Run(stopCh <-chan struct{}, synchedCh chan<- struc
} }
} }
return true, nil return true, nil
}, stopCh); err == wait.ErrWaitTimeout { }); err != nil {
utilruntime.HandleError(fmt.Errorf("timed out waiting for discovery endpoint to initialize")) if errors.Is(err, context.DeadlineExceeded) {
return utilruntime.HandleError(fmt.Errorf("timed out waiting for initial discovery sync"))
} else if err != nil { return
}
panic(fmt.Errorf("unexpected error: %v", err)) panic(fmt.Errorf("unexpected error: %v", err))
} }
close(synchedCh) close(synchedCh)

View File

@ -235,7 +235,7 @@ func (c *CRDFinalizer) deleteInstances(crd *apiextensionsv1.CustomResourceDefini
// now we need to wait until all the resources are deleted. Start with a simple poll before we do anything fancy. // now we need to wait until all the resources are deleted. Start with a simple poll before we do anything fancy.
// TODO not all servers are synchronized on caches. It is possible for a stale one to still be creating things. // TODO not all servers are synchronized on caches. It is possible for a stale one to still be creating things.
// Once we have a mechanism for servers to indicate their states, we should check that for concurrence. // Once we have a mechanism for servers to indicate their states, we should check that for concurrence.
err = wait.PollImmediate(5*time.Second, 1*time.Minute, func() (bool, error) { err = wait.PollUntilContextTimeout(ctx, 5*time.Second, 1*time.Minute, true, func(ctx context.Context) (bool, error) {
listObj, err := crClient.List(ctx, nil) listObj, err := crClient.List(ctx, nil)
if err != nil { if err != nil {
return false, err return false, err

View File

@ -72,10 +72,10 @@ func TestAPIApproval(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
approvedKubeAPI, err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), approvedKubeAPI.Name, metav1.GetOptions{}) approvedKubeAPI, getErr := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, approvedKubeAPI.Name, metav1.GetOptions{})
if err != nil { if getErr != nil {
return false, err return false, getErr
} }
if approvedKubeAPIApproved := findCRDCondition(approvedKubeAPI, apiextensionsv1.KubernetesAPIApprovalPolicyConformant); approvedKubeAPIApproved == nil || approvedKubeAPIApproved.Status != apiextensionsv1.ConditionTrue { if approvedKubeAPIApproved := findCRDCondition(approvedKubeAPI, apiextensionsv1.KubernetesAPIApprovalPolicyConformant); approvedKubeAPIApproved == nil || approvedKubeAPIApproved.Status != apiextensionsv1.ConditionTrue {
t.Log(approvedKubeAPIApproved) t.Log(approvedKubeAPIApproved)

View File

@ -219,13 +219,13 @@ func testWebhookConverter(t *testing.T, watchCache bool) {
defer ctc.removeConversionWebhook(t) defer ctc.removeConversionWebhook(t)
// wait until new webhook is called the first time // wait until new webhook is called the first time
if err := wait.PollImmediate(time.Millisecond*100, wait.ForeverTestTimeout, func() (bool, error) { if err := wait.PollUntilContextTimeout(context.Background(), time.Millisecond*100, wait.ForeverTestTimeout, true, func(ctx context.Context) (done bool, err error) {
_, err := ctc.versionedClient(marker.GetNamespace(), "v1alpha1").Get(context.TODO(), marker.GetName(), metav1.GetOptions{}) _, getErr := ctc.versionedClient(marker.GetNamespace(), "v1alpha1").Get(ctx, marker.GetName(), metav1.GetOptions{})
select { select {
case <-upCh: case <-upCh:
return true, nil return true, nil
default: default:
t.Logf("Waiting for webhook to become effective, getting marker object: %v", err) t.Logf("Waiting for webhook to become effective, getting marker object: %v", getErr)
return false, nil return false, nil
} }
}); err != nil { }); err != nil {

View File

@ -17,6 +17,7 @@ limitations under the License.
package conversion package conversion
import ( import (
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
@ -63,9 +64,9 @@ func StartConversionWebhookServer(handler http.Handler) (func(), *apiextensionsv
} }
// StartTLS returns immediately, there is a small chance of a race to avoid. // StartTLS returns immediately, there is a small chance of a race to avoid.
if err := wait.PollImmediate(time.Millisecond*100, wait.ForeverTestTimeout, func() (bool, error) { if err := wait.PollUntilContextTimeout(context.Background(), time.Millisecond*100, wait.ForeverTestTimeout, true, func(ctx context.Context) (done bool, err error) {
_, err := webhookServer.Client().Get(webhookServer.URL) // even a 404 is fine _, getErr := webhookServer.Client().Get(webhookServer.URL) // even a 404 is fine
return err == nil, nil return getErr == nil, nil
}); err != nil { }); err != nil {
webhookServer.Close() webhookServer.Close()
return nil, nil, err return nil, nil, err

View File

@ -316,8 +316,8 @@ func testDefaulting(t *testing.T, watchCache bool) {
addDefault("v1beta2", "c", "C") addDefault("v1beta2", "c", "C")
t.Logf("wait until GET sees 'c' in both status and spec") t.Logf("wait until GET sees 'c' in both status and spec")
if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { if err := wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
obj, err := fooClient.Get(context.TODO(), foo.GetName(), metav1.GetOptions{}) obj, err := fooClient.Get(ctx, foo.GetName(), metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
@ -333,7 +333,7 @@ func testDefaulting(t *testing.T, watchCache bool) {
mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"spec", "c"}, {"status", "a"}, {"status", "b"}, {"status", "c"}}) mustExist(foo.Object, [][]string{{"spec", "a"}, {"spec", "b"}, {"spec", "c"}, {"status", "a"}, {"status", "b"}, {"status", "c"}})
t.Logf("wait until GET sees 'c' in both status and spec of cached get") t.Logf("wait until GET sees 'c' in both status and spec of cached get")
if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { if err := wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
obj, err := fooClient.Get(context.TODO(), foo.GetName(), metav1.GetOptions{ResourceVersion: "0"}) obj, err := fooClient.Get(context.TODO(), foo.GetName(), metav1.GetOptions{ResourceVersion: "0"})
if err != nil { if err != nil {
return false, err return false, err
@ -409,8 +409,8 @@ func testDefaulting(t *testing.T, watchCache bool) {
t.Logf("Add 'c' default to the REST version, remove it from the storage version, and wait until GET no longer sees it in both status and spec") t.Logf("Add 'c' default to the REST version, remove it from the storage version, and wait until GET no longer sees it in both status and spec")
addDefault("v1beta1", "c", "C") addDefault("v1beta1", "c", "C")
removeDefault("v1beta2", "c") removeDefault("v1beta2", "c")
if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { if err := wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
obj, err := fooClient.Get(context.TODO(), foo.GetName(), metav1.GetOptions{}) obj, err := fooClient.Get(ctx, foo.GetName(), metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
@ -434,8 +434,8 @@ func testDefaulting(t *testing.T, watchCache bool) {
removeDefault("v1beta1", "a") removeDefault("v1beta1", "a")
removeDefault("v1beta1", "b") removeDefault("v1beta1", "b")
removeDefault("v1beta1", "c") removeDefault("v1beta1", "c")
if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { if err := wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
obj, err := fooClient.Get(context.TODO(), foo.GetName(), metav1.GetOptions{}) obj, err := fooClient.Get(ctx, foo.GetName(), metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -347,7 +347,7 @@ func existsInDiscoveryV1(crd *apiextensionsv1.CustomResourceDefinition, apiExten
func waitForCRDReadyWatchUnsafe(crd *apiextensionsv1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) (*apiextensionsv1.CustomResourceDefinition, error) { func waitForCRDReadyWatchUnsafe(crd *apiextensionsv1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) (*apiextensionsv1.CustomResourceDefinition, error) {
// wait until all resources appears in discovery // wait until all resources appears in discovery
for _, version := range servedV1Versions(crd) { for _, version := range servedV1Versions(crd) {
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) { err := wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
return existsInDiscoveryV1(crd, apiExtensionsClient, version) return existsInDiscoveryV1(crd, apiExtensionsClient, version)
}) })
if err != nil { if err != nil {
@ -375,7 +375,7 @@ func waitForCRDReady(crd *apiextensionsv1.CustomResourceDefinition, apiExtension
// For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error. // For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error.
// This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen // This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen
// before like the created RV could be too old to watch. // before like the created RV could be too old to watch.
err = wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
return isWatchCachePrimed(v1CRD, dynamicClientSet) return isWatchCachePrimed(v1CRD, dynamicClientSet)
}) })
if err != nil { if err != nil {
@ -396,7 +396,7 @@ func CreateNewV1CustomResourceDefinitionWatchUnsafe(v1CRD *apiextensionsv1.Custo
// wait until all resources appears in discovery // wait until all resources appears in discovery
for _, version := range servedV1Versions(v1CRD) { for _, version := range servedV1Versions(v1CRD) {
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) { err := wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
return existsInDiscoveryV1(v1CRD, apiExtensionsClient, version) return existsInDiscoveryV1(v1CRD, apiExtensionsClient, version)
}) })
if err != nil { if err != nil {
@ -424,7 +424,7 @@ func CreateNewV1CustomResourceDefinition(v1CRD *apiextensionsv1.CustomResourceDe
// For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error. // For this test, we'll actually cycle, "list/watch/create/delete" until we get an RV from list that observes the create and not an error.
// This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen // This way all the tests that are checking for watches don't have to worry about RV too old problems because crazy things *could* happen
// before like the created RV could be too old to watch. // before like the created RV could be too old to watch.
err = wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
return isWatchCachePrimed(v1CRD, dynamicClientSet) return isWatchCachePrimed(v1CRD, dynamicClientSet)
}) })
if err != nil { if err != nil {
@ -518,7 +518,7 @@ func DeleteV1CustomResourceDefinition(crd *apiextensionsv1.CustomResourceDefinit
return err return err
} }
for _, version := range servedV1Versions(crd) { for _, version := range servedV1Versions(crd) {
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) { err := wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
exists, err := existsInDiscoveryV1(crd, apiExtensionsClient, version) exists, err := existsInDiscoveryV1(crd, apiExtensionsClient, version)
return !exists, err return !exists, err
}) })
@ -540,7 +540,7 @@ func DeleteV1CustomResourceDefinitions(deleteListOpts metav1.ListOptions, apiExt
} }
for _, crd := range list.Items { for _, crd := range list.Items {
for _, version := range servedV1Versions(&crd) { for _, version := range servedV1Versions(&crd) {
err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) { err := wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, 30*time.Second, true, func(ctx context.Context) (bool, error) {
exists, err := existsInDiscoveryV1(&crd, apiExtensionsClient, version) exists, err := existsInDiscoveryV1(&crd, apiExtensionsClient, version)
return !exists, err return !exists, err
}) })

View File

@ -209,16 +209,16 @@ func TestListTypes(t *testing.T) {
} }
t.Logf("Updating again with invalid values, eventually successfully due to ratcheting logic") t.Logf("Updating again with invalid values, eventually successfully due to ratcheting logic")
err = wait.PollImmediate(time.Millisecond*100, wait.ForeverTestTimeout, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), time.Millisecond*100, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
_, err = fooClient.Update(context.TODO(), modifiedInstance, metav1.UpdateOptions{}) _, updateErr := fooClient.Update(ctx, modifiedInstance, metav1.UpdateOptions{})
if err == nil { if updateErr == nil {
return true, err return true, nil
} }
if errors.IsInvalid(err) { if errors.IsInvalid(updateErr) {
// wait until modifiedInstance becomes valid again // wait until modifiedInstance becomes valid again
return false, nil return false, nil
} }
return false, err return false, updateErr
}) })
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)

View File

@ -18,6 +18,7 @@ package integration
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"testing" "testing"
@ -777,15 +778,16 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
} }
// CR is now accepted // CR is now accepted
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 500*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (done bool, err error) {
_, err := noxuResourceClient.Create(context.TODO(), instanceToCreate, metav1.CreateOptions{}) _, createErr := noxuResourceClient.Create(ctx, instanceToCreate, metav1.CreateOptions{})
if _, isStatus := err.(*apierrors.StatusError); isStatus { var statusErr *apierrors.StatusError
if apierrors.IsInvalid(err) { if errors.As(createErr, &statusErr) {
if apierrors.IsInvalid(createErr) {
return false, nil return false, nil
} }
} }
if err != nil { if createErr != nil {
return false, err return false, createErr
} }
return true, nil return true, nil
}) })
@ -925,8 +927,8 @@ spec:
// wait for condition with violations // wait for condition with violations
t.Log("Waiting for NonStructuralSchema condition") t.Log("Waiting for NonStructuralSchema condition")
var cond *apiextensionsv1.CustomResourceDefinitionCondition var cond *apiextensionsv1.CustomResourceDefinitionCondition
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (done bool, err error) {
obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
@ -963,8 +965,8 @@ spec:
// wait for condition to go away // wait for condition to go away
t.Log("Wait for condition to disappear") t.Log("Wait for condition to disappear")
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (done bool, err error) {
obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
@ -1529,8 +1531,8 @@ properties:
if len(tst.expectedViolations) == 0 { if len(tst.expectedViolations) == 0 {
// wait for condition to not appear // wait for condition to not appear
var cond *apiextensionsv1.CustomResourceDefinitionCondition var cond *apiextensionsv1.CustomResourceDefinitionCondition
err := wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (done bool, err error) {
obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), betaCRD.Name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, betaCRD.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }
@ -1540,7 +1542,7 @@ properties:
} }
return true, nil return true, nil
}) })
if err != wait.ErrWaitTimeout { if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("expected no NonStructuralSchema condition, but got one: %v", cond) t.Fatalf("expected no NonStructuralSchema condition, but got one: %v", cond)
} }
return return
@ -1548,8 +1550,8 @@ properties:
// wait for condition to appear with the given violations // wait for condition to appear with the given violations
var cond *apiextensionsv1.CustomResourceDefinitionCondition var cond *apiextensionsv1.CustomResourceDefinitionCondition
err = wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (done bool, err error) {
obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), betaCRD.Name, metav1.GetOptions{}) obj, err := apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, betaCRD.Name, metav1.GetOptions{})
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -18,6 +18,7 @@ package integration
import ( import (
"context" "context"
stderrors "errors"
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
@ -25,6 +26,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/test/integration/fixtures" "k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
@ -88,18 +90,19 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) {
{ {
t.Logf("patch of handler version v1beta1 (non-storage version) should succeed") t.Logf("patch of handler version v1beta1 (non-storage version) should succeed")
i := 0 i := 0
err = wait.PollImmediate(time.Millisecond*100, wait.ForeverTestTimeout, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), time.Millisecond*100, wait.ForeverTestTimeout, true, func(ctx context.Context) (done bool, err error) {
patch := []byte(fmt.Sprintf(`{"i": %d}`, i)) patch := []byte(fmt.Sprintf(`{"i": %d}`, i))
i++ i++
_, err := noxuNamespacedResourceClientV1beta1.Patch(context.TODO(), "foo", types.MergePatchType, patch, metav1.PatchOptions{}) _, patchErr := noxuNamespacedResourceClientV1beta1.Patch(ctx, "foo", types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil { if patchErr != nil {
// work around "grpc: the client connection is closing" error // work around "grpc: the client connection is closing" error
// TODO: fix the grpc error // TODO: fix the grpc error
if err, ok := err.(*errors.StatusError); ok && err.Status().Code == http.StatusInternalServerError { var statusErr *errors.StatusError
if stderrors.As(patchErr, &statusErr) && statusErr.Status().Code == http.StatusInternalServerError {
return false, nil return false, nil
} }
return false, err return false, patchErr
} }
return true, nil return true, nil
}) })
@ -111,20 +114,21 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) {
t.Logf("patch of handler version v1beta2 (storage version) should fail") t.Logf("patch of handler version v1beta2 (storage version) should fail")
i := 0 i := 0
noxuNamespacedResourceClientV1beta2 := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, "v1beta2") // use the storage version v1beta2 noxuNamespacedResourceClientV1beta2 := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, "v1beta2") // use the storage version v1beta2
err = wait.PollImmediate(time.Millisecond*100, wait.ForeverTestTimeout, func() (bool, error) { err = wait.PollUntilContextTimeout(context.Background(), time.Millisecond*100, wait.ForeverTestTimeout, true, func(ctx context.Context) (done bool, err error) {
patch := []byte(fmt.Sprintf(`{"i": %d}`, i)) patch := []byte(fmt.Sprintf(`{"i": %d}`, i))
i++ i++
_, err := noxuNamespacedResourceClientV1beta2.Patch(context.TODO(), "foo", types.MergePatchType, patch, metav1.PatchOptions{}) _, patchErr := noxuNamespacedResourceClientV1beta2.Patch(ctx, "foo", types.MergePatchType, patch, metav1.PatchOptions{})
assert.Error(t, err) require.Error(t, patchErr)
// work around "grpc: the client connection is closing" error // work around "grpc: the client connection is closing" error
// TODO: fix the grpc error // TODO: fix the grpc error
if err, ok := err.(*errors.StatusError); ok && err.Status().Code == http.StatusInternalServerError { var statusErr *errors.StatusError
if stderrors.As(patchErr, &statusErr) && statusErr.Status().Code == http.StatusInternalServerError {
return false, nil return false, nil
} }
assert.ErrorContains(t, err, "apiVersion") assert.ErrorContains(t, patchErr, "apiVersion")
return true, nil return true, nil
}) })
assert.NoError(t, err) assert.NoError(t, err)