Repository: Azure/ARO-RP
Pull request #2542 (2022-11-15 15:51:26)

Migrates rest of the E2Es to use `Eventually` instead of `wait.Poll*`
### Which issue this PR addresses:

Part of [WI 13667437](https://msazure.visualstudio.com/AzureRedHatOpenShift/_workitems/edit/13667437/).

### What this PR does / why we need it:

This helps with troubleshooting of E2Es as it makes easier to understand what assertion within the wait code failed and why.

This is basically the same as https://github.com/Azure/ARO-RP/pull/2534, but for rest of E2Es.


### Test plan for issue:

Run E2E

### Is there any documentation that needs to be updated for this PR?

No, just E2E refactoring


Patch:
diff --git a/pkg/util/ready/ready.go b/pkg/util/ready/ready.go
index 65b1c9175..cb1540b3b 100644
--- a/pkg/util/ready/ready.go
+++ b/pkg/util/ready/ready.go
@@ -170,22 +170,6 @@ func StatefulSetIsReady(s *appsv1.StatefulSet) bool {
 		s.Generation == s.Status.ObservedGeneration
 }
 
-// CheckStatefulSetIsReady returns a function which polls a StatefulSet and
-// returns its readiness
-func CheckStatefulSetIsReady(ctx context.Context, cli appsv1client.StatefulSetInterface, name string) func() (bool, error) {
-	return func() (bool, error) {
-		s, err := cli.Get(ctx, name, metav1.GetOptions{})
-		switch {
-		case kerrors.IsNotFound(err):
-			return false, nil
-		case err != nil:
-			return false, err
-		}
-
-		return StatefulSetIsReady(s), nil
-	}
-}
-
 // MachineConfigPoolIsReady returns true if a MachineConfigPool is considered
 // ready
 func MachineConfigPoolIsReady(s *mcv1.MachineConfigPool) bool {


Test patch:
diff --git a/pkg/util/ready/ready_test.go b/pkg/util/ready/ready_test.go
index a96eb4787..ddcf1ca64 100644
--- a/pkg/util/ready/ready_test.go
+++ b/pkg/util/ready/ready_test.go
@@ -565,57 +565,6 @@ func TestCheckDeploymentIsReadyError(t *testing.T) {
 	}
 }
 
-func TestCheckStatefulSetIsReady(t *testing.T) {
-	ctx := context.Background()
-
-	statefulset := &appsv1.StatefulSet{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "statefulset-not-found",
-			Namespace: "default",
-		},
-	}
-	clientset := fake.NewSimpleClientset()
-	_, err := clientset.AppsV1().StatefulSets("default").Create(ctx, statefulset, metav1.CreateOptions{})
-	if err != nil {
-		t.Fatalf("error creating statefulset: %v", err)
-	}
-	_, err = CheckStatefulSetIsReady(ctx, clientset.AppsV1().StatefulSets("default"), statefulset.ObjectMeta.Name)()
-
-	if err != nil {
-		t.Fatalf("check statefulsets is not ready: %v", err)
-	}
-}
-
-func TestCheckStatefulSetIsReadyNotFound(t *testing.T) {
-	ctx := context.Background()
-	statefulset := &appsv1.StatefulSet{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "statefulset-not-found",
-			Namespace: "default",
-		},
-	}
-	clientset := fake.NewSimpleClientset()
-	ok, _ := CheckStatefulSetIsReady(ctx, clientset.AppsV1().StatefulSets("default"), statefulset.ObjectMeta.Name)()
-
-	if ok {
-		t.Fatalf("check statefulsets is found")
-	}
-}
-
-func TestCheckStatefulSetIsReadyError(t *testing.T) {
-	ctx := context.Background()
-
-	clientset := fake.NewSimpleClientset()
-	clientset.Fake.PrependReactor("get", "statefulsets", func(action ktesting.Action) (bool, kruntime.Object, error) {
-		return true, &appsv1.StatefulSet{}, errors.New("error getting deployment")
-	})
-	_, err := CheckStatefulSetIsReady(ctx, clientset.AppsV1().StatefulSets("default"), "")()
-
-	if err == nil {
-		t.Fatalf("check statefulsets error is: %v", err)
-	}
-}
-
 func TestCheckMachineConfigPoolIsReady(t *testing.T) {
 	ctx := context.Background()
 
diff --git a/test/e2e/adminapi.go b/test/e2e/adminapi.go
index afc8e0abc..f18eff91c 100644
--- a/test/e2e/adminapi.go
+++ b/test/e2e/adminapi.go
@@ -84,24 +84,24 @@ func adminRequest(ctx context.Context, method, path string, params url.Values, i
 }
 
 // adminGetCluster returns admin representation of an ARO cluster
-func adminGetCluster(ctx context.Context, resourceID string) *admin.OpenShiftCluster {
+func adminGetCluster(g Gomega, ctx context.Context, resourceID string) *admin.OpenShiftCluster {
 	var oc admin.OpenShiftCluster
 	resp, err := adminRequest(ctx, http.MethodGet, resourceID, nil, nil, &oc)
-	Expect(err).NotTo(HaveOccurred())
-	Expect(resp.StatusCode).To(Equal(http.StatusOK))
+	g.Expect(err).NotTo(HaveOccurred())
+	g.Expect(resp.StatusCode).To(Equal(http.StatusOK))
 	return &oc
 }
 
 // adminListClusters returns a list of ARO clusters in admin representation.
 // It handles pagination: function returns all the clusters from all pages.
-func adminListClusters(ctx context.Context, path string) []*admin.OpenShiftCluster {
+func adminListClusters(g Gomega, ctx context.Context, path string) []*admin.OpenShiftCluster {
 	ocs := make([]*admin.OpenShiftCluster, 0)
 	params := url.Values{}
 	for {
 		var list admin.OpenShiftClusterList
 		resp, err := adminRequest(ctx, http.MethodGet, path, params, nil, &list)
-		Expect(err).NotTo(HaveOccurred())
-		Expect(resp.StatusCode).To(Equal(http.StatusOK))
+		g.Expect(err).NotTo(HaveOccurred())
+		g.Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 		ocs = append(ocs, list.OpenShiftClusters...)
 
@@ -109,14 +109,14 @@ func adminListClusters(ctx context.Context, path string) []*admin.OpenShiftClust
 			break
 		}
 
-		params = nextParams(list.NextLink)
+		params = nextParams(g, list.NextLink)
 	}
 	return ocs
 }
 
-func nextParams(nextLink string) url.Values {
+func nextParams(g Gomega, nextLink string) url.Values {
 	url, err := url.Parse(nextLink)
-	Expect(err).NotTo(HaveOccurred())
+	g.Expect(err).NotTo(HaveOccurred())
 
 	return url.Query()
 }
diff --git a/test/e2e/adminapi_cluster_get.go b/test/e2e/adminapi_cluster_get.go
index 6e619b72b..6aa6c9562 100644
--- a/test/e2e/adminapi_cluster_get.go
+++ b/test/e2e/adminapi_cluster_get.go
@@ -17,7 +17,7 @@ var _ = Describe("[Admin API] Get cluster action", func() {
 		resourceID := resourceIDFromEnv()
 
 		By("requesting the cluster document via RP admin API")
-		oc := adminGetCluster(ctx, resourceID)
+		oc := adminGetCluster(Default, ctx, resourceID)
 
 		By("checking that we received the expected cluster")
 		Expect(oc.ID).To(Equal(resourceID))
diff --git a/test/e2e/adminapi_cluster_getlogs.go b/test/e2e/adminapi_cluster_getlogs.go
index 23ca8b04c..6f26a4a0f 100644
--- a/test/e2e/adminapi_cluster_getlogs.go
+++ b/test/e2e/adminapi_cluster_getlogs.go
@@ -5,19 +5,16 @@ package e2e
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"net/http"
 	"net/url"
 	"strings"
-	"time"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/wait"
 )
 
 var _ = Describe("[Admin API] Kubernetes get pod logs action", func() {
@@ -59,24 +56,12 @@ func testGetPodLogsOK(ctx context.Context, containerName, podName, namespace str
 	}()
 
 	By("waiting for the pod to successfully terminate")
-	err = wait.PollInfinite(time.Second*5, func() (done bool, err error) {
+	Eventually(func(g Gomega, ctx context.Context) {
 		pod, err = clients.Kubernetes.CoreV1().Pods(namespace).Get(ctx, pod.Name, metav1.GetOptions{})
-		Expect(err).NotTo(HaveOccurred())
+		g.Expect(err).NotTo(HaveOccurred())
 
-		switch pod.Status.Phase {
-		case corev1.PodSucceeded:
-			return true, nil
-		case corev1.PodPending:
-			if pod.CreationTimestamp.Time.Add(5*time.Minute).Unix() < time.Now().Unix() {
-				return false, errors.New("pod was pending for more than 5min")
-			}
-			return false, nil
-		case corev1.PodFailed:
-			return true, errors.New(pod.Status.Message)
-		}
-		return false, nil
-	})
-	Expect(err).NotTo(HaveOccurred())
+		g.Expect(pod.Status.Phase).To(Equal(corev1.PodSucceeded))
+	}).WithContext(ctx).Should(Succeed())
 
 	By("requesting logs via RP admin API")
 	params := url.Values{
diff --git a/test/e2e/adminapi_cluster_list.go b/test/e2e/adminapi_cluster_list.go
index ce9ec357d..c4ee6139b 100644
--- a/test/e2e/adminapi_cluster_list.go
+++ b/test/e2e/adminapi_cluster_list.go
@@ -19,27 +19,27 @@ var _ = Describe("[Admin API] List clusters action", func() {
 	It("must return list of clusters with admin fields", func(ctx context.Context) {
 		resourceID := resourceIDFromEnv()
 
-		testAdminClustersList(ctx, "/admin/providers/Microsoft.RedHatOpenShift/openShiftClusters", resourceID)
+		testAdminClustersList(Default, ctx, "/admin/providers/Microsoft.RedHatOpenShift/openShiftClusters", resourceID)
 	})
 
 	It("must return list of clusters with admin fields by subscription", func(ctx context.Context) {
 		resourceID := resourceIDFromEnv()
 
 		path := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.RedHatOpenShift/openShiftClusters", _env.SubscriptionID())
-		testAdminClustersList(ctx, path, resourceID)
+		testAdminClustersList(Default, ctx, path, resourceID)
 	})
 
 	It("must return list of clusters with admin fields by resource group", func(ctx context.Context) {
 		resourceID := resourceIDFromEnv()
 
 		path := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.RedHatOpenShift/openShiftClusters", _env.SubscriptionID(), vnetResourceGroup)
-		testAdminClustersList(ctx, path, resourceID)
+		testAdminClustersList(Default, ctx, path, resourceID)
 	})
 })
 
-func testAdminClustersList(ctx context.Context, path, wantResourceID string) {
+func testAdminClustersList(g Gomega, ctx context.Context, path, wantResourceID string) {
 	By("listing the cluster documents via RP admin API")
-	ocs := adminListClusters(ctx, path)
+	ocs := adminListClusters(g, ctx, path)
 
 	By("verifying that we received the expected cluster")
 	var oc *admin.OpenShiftCluster
@@ -48,12 +48,12 @@ func testAdminClustersList(ctx context.Context, path, wantResourceID string) {
 			oc = ocs[i]
 		}
 	}
-	Expect(oc).ToNot(BeNil())
-	Expect(oc.ID).To(Equal(wantResourceID))
+	g.Expect(oc).ToNot(BeNil())
+	g.Expect(oc.ID).To(Equal(wantResourceID))
 
 	By("checking that fields available only in Admin API have values")
 	// Note: some fields will have empty values
 	// on successfully provisioned cluster (oc.Properties.Install, for example)
-	Expect(oc.Properties.StorageSuffix).ToNot(BeEmpty())
-	Expect(oc.Properties.InfraID).ToNot(BeEmpty())
+	g.Expect(oc.Properties.StorageSuffix).ToNot(BeEmpty())
+	g.Expect(oc.Properties.InfraID).ToNot(BeEmpty())
 }
diff --git a/test/e2e/adminapi_cluster_update.go b/test/e2e/adminapi_cluster_update.go
index 694a66477..838e53f73 100644
--- a/test/e2e/adminapi_cluster_update.go
+++ b/test/e2e/adminapi_cluster_update.go
@@ -7,13 +7,10 @@ import (
 	"context"
 	"encoding/json"
 	"net/http"
-	"time"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
-	"k8s.io/apimachinery/pkg/util/wait"
-
 	"github.com/Azure/ARO-RP/pkg/api/admin"
 )
 
@@ -34,10 +31,10 @@ var _ = Describe("[Admin API] Cluster admin update action", func() {
 		Expect(oc.Properties.LastProvisioningState).To(Equal(admin.ProvisioningStateSucceeded))
 
 		By("waiting for the update to complete")
-		err = wait.PollImmediate(10*time.Second, 30*time.Minute, func() (bool, error) {
-			oc = adminGetCluster(ctx, resourceID)
-			return oc.Properties.ProvisioningState == admin.ProvisioningStateSucceeded, nil
-		})
-		Expect(err).NotTo(HaveOccurred())
+		Eventually(func(g Gomega, ctx context.Context) {
+			oc = adminGetCluster(g, ctx, resourceID)
+
+			g.Expect(oc.Properties.ProvisioningState).To(Equal(admin.ProvisioningStateSucceeded))
+		}).WithContext(ctx).Should(Succeed())
 	})
 })
diff --git a/test/e2e/adminapi_kubernetesobjects.go b/test/e2e/adminapi_kubernetesobjects.go
index 0dfc37b7e..aad192ed8 100644
--- a/test/e2e/adminapi_kubernetesobjects.go
+++ b/test/e2e/adminapi_kubernetesobjects.go
@@ -7,7 +7,6 @@ import (
 	"context"
 	"net/http"
 	"net/url"
-	"time"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -15,7 +14,6 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/wait"
 
 	"github.com/Azure/ARO-RP/pkg/api"
 )
@@ -79,17 +77,10 @@ var _ = Describe("[Admin API] Kubernetes objects action", func() {
 				// To avoid flakes, we need it to be completely deleted before we can use it again
 				// in a separate run or in a separate It block
 				By("waiting for the test customer namespace to be deleted")
-				err = wait.PollImmediate(10*time.Second, 5*time.Minute, func() (bool, error) {
+				Eventually(func(g Gomega, ctx context.Context) {
 					_, err := clients.Kubernetes.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
-					if kerrors.IsNotFound(err) {
-						return true, nil
-					}
-					if err != nil {
-						log.Warn(err)
-					}
-					return false, nil // swallow error
-				})
-				Expect(err).NotTo(HaveOccurred())
+					g.Expect(kerrors.IsNotFound(err)).To(BeTrue(), "expect Namespace to be deleted")
+				}).WithContext(ctx).Should(Succeed())
 			}()
 
 			testConfigMapCreateOrUpdateForbidden(ctx, "creating", objName, namespace)
@@ -268,17 +259,10 @@ func testConfigMapDeleteOK(ctx context.Context, objName, namespace string) {
 	// To avoid flakes, we need it to be completely deleted before we can use it again
 	// in a separate run or in a separate It block
 	By("waiting for the configmap to be deleted")
-	err = wait.PollImmediate(10*time.Second, time.Minute, func() (bool, error) {
+	Eventually(func(g Gomega, ctx context.Context) {
 		_, err = clients.Kubernetes.CoreV1().ConfigMaps(namespace).Get(ctx, objName, metav1.GetOptions{})
-		if kerrors.IsNotFound(err) {
-			return true, nil
-		}
-		if err != nil {
-			log.Warn(err)
-		}
-		return false, nil // swallow error
-	})
-	Expect(err).NotTo(HaveOccurred())
+		g.Expect(kerrors.IsNotFound(err)).To(BeTrue(), "expect ConfigMap to be deleted")
+	}).WithContext(ctx).Should(Succeed())
 }
 
 func testConfigMapCreateOrUpdateForbidden(ctx context.Context, operation, objName, namespace string) {
@@ -336,17 +320,10 @@ func testPodForceDeleteOK(ctx context.Context, objName, namespace string) {
 	// To avoid flakes, we need it to be completely deleted before we can use it again
 	// in a separate run or in a separate It block
 	By("waiting for the pod to be deleted")
-	err = wait.PollImmediate(10*time.Second, time.Minute, func() (bool, error) {
+	Eventually(func(g Gomega, ctx context.Context) {
 		_, err = clients.Kubernetes.CoreV1().Pods(namespace).Get(ctx, objName, metav1.GetOptions{})
-		if kerrors.IsNotFound(err) {
-			return true, nil
-		}
-		if err != nil {
-			log.Warn(err)
-		}
-		return false, nil // swallow error
-	})
-	Expect(err).NotTo(HaveOccurred())
+		g.Expect(kerrors.IsNotFound(err)).To(BeTrue(), "expect Pod to be deleted")
+	}).WithContext(ctx).Should(Succeed())
 }
 
 func mockSecret(name, namespace string) corev1.Secret {
diff --git a/test/e2e/adminapi_redeployvm.go b/test/e2e/adminapi_redeployvm.go
index cf454b7a5..173ea204a 100644
--- a/test/e2e/adminapi_redeployvm.go
+++ b/test/e2e/adminapi_redeployvm.go
@@ -6,7 +6,6 @@ package e2e
 import (
 	"bufio"
 	"context"
-	"fmt"
 	"net/http"
 	"net/url"
 	"strings"
@@ -18,7 +17,6 @@ import (
 	mgmtcompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/wait"
 
 	"github.com/Azure/ARO-RP/pkg/util/ready"
 	"github.com/Azure/ARO-RP/pkg/util/stringutils"
@@ -47,55 +45,39 @@ var _ = Describe("[Admin API] VM redeploy action", func() {
 		log.Infof("selected vm: %s", *vm.Name)
 
 		By("saving the current uptime")
-		oldUptime, err := getNodeUptime(ctx, *vm.Name)
+		oldUptime, err := getNodeUptime(Default, ctx, *vm.Name)
 		Expect(err).NotTo(HaveOccurred())
 
-		By("verifying redeploy action completes without error")
+		By("triggering VM redeployment via RP Admin API")
 		resp, err := adminRequest(ctx, http.MethodPost, "/admin"+resourceID+"/redeployvm", url.Values{"vmName": []string{*vm.Name}}, nil, nil)
 		Expect(err).NotTo(HaveOccurred())
 		Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 		By("waiting for the redeployed VM to report Running power state in Azure")
-		// we can pollimmediate without fear of false positive because we have
-		// already waited on the redeploy future
-		err = wait.PollImmediate(1*time.Minute, 10*time.Minute, func() (bool, error) {
+		Eventually(func(g Gomega, ctx context.Context) {
 			restartedVm, err := clients.VirtualMachines.Get(ctx, clusterResourceGroup, *vm.Name, mgmtcompute.InstanceView)
-			if err != nil {
-				log.Info(fmt.Sprintf("Failed to get restarted vm: %v", err))
-				return false, nil // swallow err, retry
-			}
-			for _, status := range *restartedVm.InstanceView.Statuses {
-				if *status.Code == "PowerState/running" {
-					return true, nil
-				}
-			}
-			return false, nil
-		})
-		Expect(err).NotTo(HaveOccurred())
+			g.Expect(err).NotTo(HaveOccurred())
+
+			g.Expect(*restartedVm.InstanceView.Statuses).To(ContainElement(HaveField("Code", HaveValue(Equal("PowerState/running")))))
+		}).WithContext(ctx).WithTimeout(10 * time.Minute).WithPolling(time.Minute).Should(Succeed())
 
 		By("waiting for the redeployed node to eventually become Ready in OpenShift")
 		// wait 1 minute - this will guarantee we pass the minimum (default) threshold of Node heartbeats (40 seconds)
-		err = wait.Poll(1*time.Minute, 10*time.Minute, func() (bool, error) {
+		Eventually(func(g Gomega, ctx context.Context) {
 			node, err := clients.Kubernetes.CoreV1().Nodes().Get(ctx, *vm.Name, metav1.GetOptions{})
-			if err != nil {
-				log.Warn(err)
-				return false, nil // swallow error, retry
-			}
-			if ready.NodeIsReady(node) {
-				return true, nil
-			}
-			return false, nil
-		})
-		Expect(err).NotTo(HaveOccurred())
+			g.Expect(err).NotTo(HaveOccurred())
+
+			g.Expect(ready.NodeIsReady(node)).To(BeTrue())
+		}).WithContext(ctx).WithTimeout(10 * time.Minute).WithPolling(time.Minute).Should(Succeed())
 
 		By("getting system uptime again and making sure it is newer")
-		newUptime, err := getNodeUptime(ctx, *vm.Name)
+		newUptime, err := getNodeUptime(Default, ctx, *vm.Name)
 		Expect(err).NotTo(HaveOccurred())
-		Expect(oldUptime.Before(newUptime)).To(BeTrue())
+		Expect(newUptime).To(BeTemporally(">", oldUptime))
 	})
 })
 
-func getNodeUptime(ctx context.Context, node string) (time.Time, error) {
+func getNodeUptime(g Gomega, ctx context.Context, node string) (time.Time, error) {
 	// container kernel = node kernel = `uptime` in a Pod reflects the Node as well
 	namespace := "default"
 	name := node
@@ -121,7 +103,7 @@ func getNodeUptime(ctx context.Context, node string) (time.Time, error) {
 		},
 	}
 
-	// Create
+	By("creating uptime pod")
 	_, err := clients.Kubernetes.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
 	if err != nil {
 		return time.Time{}, err
@@ -129,28 +111,22 @@ func getNodeUptime(ctx context.Context, node string) (time.Time, error) {
 
 	// Defer Delete
 	defer func() {
+		By("deleting uptime pod")
 		err := clients.Kubernetes.CoreV1().Pods(namespace).Delete(ctx, name, metav1.DeleteOptions{})
 		if err != nil {
 			log.Error("Could not delete test Pod")
 		}
 	}()
 
-	// Wait for Completion
-	err = wait.PollImmediate(5*time.Second, 3*time.Minute, func() (bool, error) {
+	By("waiting for uptime pod to move into the Succeeded phase")
+	g.Eventually(func(g Gomega, ctx context.Context) {
 		p, err := clients.Kubernetes.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{})
-		if err != nil {
-			return false, err
-		}
-		if p.Status.Phase == "Succeeded" {
-			return true, nil
-		}
-		return false, nil // retry
-	})
-	if err != nil {
-		return time.Time{}, err
-	}
+		g.Expect(err).NotTo(HaveOccurred())
+
+		g.Expect(p.Status.Phase).To(Equal(corev1.PodSucceeded))
+	}).WithContext(ctx).Should(Succeed())
 
-	// Logs (uptime)
+	By("getting logs")
 	req := clients.Kubernetes.CoreV1().Pods(namespace).GetLogs(name, &corev1.PodLogOptions{})
 	stream, err := req.Stream(ctx)
 	if err != nil {
diff --git a/test/e2e/cluster.go b/test/e2e/cluster.go
index f642ec9eb..ae6b72fbc 100644
--- a/test/e2e/cluster.go
+++ b/test/e2e/cluster.go
@@ -5,7 +5,6 @@ package e2e
 
 import (
 	"context"
-	"time"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -15,7 +14,6 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/kubernetes"
 
 	"github.com/Azure/ARO-RP/pkg/util/ready"
@@ -58,8 +56,12 @@ var _ = Describe("Cluster", func() {
 		Expect(err).NotTo(HaveOccurred())
 
 		By("verifying the stateful set is ready")
-		err = wait.PollImmediate(10*time.Second, 15*time.Minute, ready.CheckStatefulSetIsReady(ctx, clients.Kubernetes.AppsV1().StatefulSets(testNamespace), "busybox"))
-		Expect(err).NotTo(HaveOccurred())
+		Eventually(func(g Gomega, ctx context.Context) {
+			s, err := clients.Kubernetes.AppsV1().StatefulSets(testNamespace).Get(ctx, "busybox", metav1.GetOptions{})
+			g.Expect(err).NotTo(HaveOccurred())
+
+			g.Expect(ready.StatefulSetIsReady(s)).To(BeTrue(), "expect stateful to be ready")
+		}).WithContext(ctx).Should(Succeed())
 	})
 
 	It("can create load balancer services", func(ctx context.Context) {
@@ -102,8 +104,12 @@ var _ = Describe("Cluster", func() {
 		Expect(err).NotTo(HaveOccurred())
 
 		By("verifying the deployment is ready")
-		err = wait.PollImmediate(10*time.Second, 5*time.Minute, ready.CheckDeploymentIsReady(ctx, clients.Kubernetes.AppsV1().Deployments(testNamespace), deployName))
-		Expect(err).NotTo(HaveOccurred())
+		Eventually(func(g Gomega, ctx context.Context) {
+			s, err := clients.Kubernetes.AppsV1().Deployments(testNamespace).Get(ctx, deployName, metav1.GetOptions{})
+			g.Expect(err).NotTo(HaveOccurred())
+
+			g.Expect(ready.DeploymentIsReady(s)).To(BeTrue(), "expect stateful to be ready")
+		}).WithContext(ctx).Should(Succeed())
 	})
 })