Skip to content

Create waiter package with predicate-based generic API for e2e polling #518

@oleg-kushniriov

Description

@oleg-kushniriov

What you would like to be added?

Create a dedicated e2e/waiter package that provides a single, generic WaitFor function driven by user-supplied predicates.

API sketch:

w := waiter.New(
waiter.WithTimeout(4 * time.Minute),
waiter.WithInterval(5 * time.Second),
waiter.WithLogger(logger),
)

// Generic WaitFor — caller supplies fetch + predicate
pods, err := waiter.WaitFor(ctx, w, func(ctx context.Context) (*v1.PodList, error) {
return clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{})
}, func(pods *v1.PodList) bool {
return len(pods.Items) == expectedCount
})

The Waiter struct holds defaults (timeout, interval, logger). The WaitFor[T] function is generic — it takes a fetch function func(ctx) (T, error) and a predicate func(T) bool, polls until the predicate returns true, and returns the final T value. This replaces all specialized wait functions with one composable primitive.

Currently, wait/poll logic is scattered across multiple packages with inconsistent signatures:

  • utils.WaitForPods, WaitForPodCount, WaitForPodCountAndPhases, WaitForPodsInNamespace, WaitForPodsWithClientset — 6 pod wait variants differing only in client type or condition checked
  • utils.WaitForPodCliqueScalingGroup, WaitForPodClique, DeletePodCliqueSetAndWait — Grove resource waiters
  • k8s.PollForCondition — low-level polling primitive
  • k8s/pods.PodManager.WaitFor* — manager-level waiters that duplicate utils
  • ~50 inline PollForCondition closures across test files for ad-hoc conditions

The new package should:

  1. Expose a Waiter struct configured via With... options (WithTimeout, WithInterval, WithLogger)
  2. Provide a single generic WaitFor[T](ctx, w, fetchFn, predicateFn) (T, error) as the core primitive
  3. Optionally provide common predicate helpers (e.g. CountEquals(n), AllRunning()) for readability
  4. Replace the current mix of free functions, manager methods, and inline poll closures

Why is this needed?

Every wait function today takes ctx, a client (sometimes restConfig, sometimes clientset, sometimes dynamicClient), namespace, timeout, and interval as individual arguments — 7-10 parameter signatures with easy-to-miss ordering bugs. The same condition (e.g. "N pods running") is implemented as both a dedicated function in utils and as inline closures in tests. A generic
predicate-based waiter eliminates all specialized variants: callers compose what they need from fetch + predicate. One place to add logging on timeout, backoff, or diagnostics. Tests become shorter and more expressive.

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No fields configured for Task.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions