diff --git a/operator/internal/controller/utils/reconciler.go b/operator/internal/controller/utils/reconciler.go index 47921ea1f..baa141c9d 100644 --- a/operator/internal/controller/utils/reconciler.go +++ b/operator/internal/controller/utils/reconciler.go @@ -45,11 +45,15 @@ func GetPodCliqueSet(ctx context.Context, cl client.Client, logger logr.Logger, return grovectrl.ContinueReconcile() } -// GetPodClique gets the latest PodClique object. It will usually hit the informer cache. If the object is not found, it will log a message and return DoNotRequeue. +// GetPodClique gets the latest PodClique object. It will usually hit the informer cache. +// When ignoreNotFound is true the caller signals that a NotFound response is expected (e.g. +// during a normal cascade-delete of the parent PodCliqueSet). In that case the log is emitted +// at V(1) (verbose/debug) to avoid spamming info logs at scale. Unexpected NotFound responses +// (ignoreNotFound=false) are still propagated as errors so the calling reconciler can surface them. func GetPodClique(ctx context.Context, cl client.Client, logger logr.Logger, objectKey client.ObjectKey, pclq *v1alpha1.PodClique, ignoreNotFound bool) grovectrl.ReconcileStepResult { if err := cl.Get(ctx, objectKey, pclq); err != nil { if ignoreNotFound && apierrors.IsNotFound(err) { - logger.Info("PodClique not found", "objectKey", objectKey) + logger.V(1).Info("PodClique not found (expected during cascade-delete)", "objectKey", objectKey) return grovectrl.DoNotRequeue() } return grovectrl.ReconcileWithErrors("error getting PodClique", err) diff --git a/operator/internal/controller/utils/reconciler_test.go b/operator/internal/controller/utils/reconciler_test.go index 213206b29..92b80ac81 100644 --- a/operator/internal/controller/utils/reconciler_test.go +++ b/operator/internal/controller/utils/reconciler_test.go @@ -192,6 +192,26 @@ func TestGetPodClique(t *testing.T) { } } +// TestGetPodClique_IgnoreNotFoundFalse verifies that when ignoreNotFound is false and the +// PodClique does not exist, GetPodClique propagates the error rather than returning DoNotRequeue. +// This path covers unexpected missing PodCliques (not cascade-delete) and should surface the error. +func TestGetPodClique_IgnoreNotFoundFalse(t *testing.T) { + ctx := context.Background() + logger := logr.Discard() + + scheme := runtime.NewScheme() + require.NoError(t, grovecorev1alpha1.AddToScheme(scheme)) + fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() + + objectKey := types.NamespacedName{Name: "missing-pclq", Namespace: "default"} + pclq := &grovecorev1alpha1.PodClique{} + result := GetPodClique(ctx, fakeClient, logger, objectKey, pclq, false) + + // Must not silently swallow the error or return DoNotRequeue — the caller + // should be aware the object is missing unexpectedly. + assert.True(t, result.NeedsRequeue() || result.HasErrors(), "expected error or requeue on unexpected NotFound with ignoreNotFound=false") +} + // TestGetPodCliqueScalingGroup tests the GetPodCliqueScalingGroup function func TestGetPodCliqueScalingGroup(t *testing.T) { tests := []struct {