@@ -14,6 +14,7 @@ import (
1414 "github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
1515 "github.com/fluxcd/cli-utils/pkg/kstatus/status"
1616 "github.com/fluxcd/cli-utils/pkg/object"
17+ "github.com/fluxcd/pkg/apis/kustomize"
1718 "github.com/fluxcd/pkg/apis/meta"
1819 "github.com/fluxcd/pkg/runtime/cel"
1920 runtimeClient "github.com/fluxcd/pkg/runtime/client"
@@ -314,7 +315,9 @@ func (r *ResourceSetReconciler) checkDependencies(ctx context.Context,
314315 }
315316
316317 if dep .Ready {
317- if dep .ReadyExpr != "" {
318+ switch {
319+ // Custom CEL ready expression.
320+ case dep .ReadyExpr != "" :
318321 isReady , err := exprs [i ].EvaluateBoolean (ctx , depObj .UnstructuredContent ())
319322 if err != nil {
320323 return err
@@ -323,7 +326,23 @@ func (r *ResourceSetReconciler) checkDependencies(ctx context.Context,
323326 if ! isReady {
324327 return fmt .Errorf ("dependency %s/%s not ready: expression '%s'" , dep .APIVersion , ssautil .FmtObjMetadata (depMd ), dep .ReadyExpr )
325328 }
326- } else {
329+ // Built-in CEL ready expression for ResourceSet and ResourceSetInputProvider.
330+ case dep .Kind == fluxcdv1 .ResourceSetKind || dep .Kind == fluxcdv1 .ResourceSetInputProviderKind :
331+ expr , err := cel .NewExpression (fluxcdv1 .HealthCheckExpr )
332+ if err != nil {
333+ return err
334+ }
335+
336+ isReady , err := expr .EvaluateBoolean (ctx , depObj .UnstructuredContent ())
337+ if err != nil {
338+ return err
339+ }
340+
341+ if ! isReady {
342+ return fmt .Errorf ("dependency %s/%s not ready" , dep .APIVersion , ssautil .FmtObjMetadata (depMd ))
343+ }
344+ // Default status check using kstatus.
345+ default :
327346 stat , err := status .Compute (depObj )
328347 if err != nil {
329348 return fmt .Errorf ("dependency %s/%s not ready: %w" , dep .APIVersion , ssautil .FmtObjMetadata (depMd ), err )
@@ -432,15 +451,10 @@ func (r *ResourceSetReconciler) apply(ctx context.Context,
432451 }
433452
434453 // Configure the Kubernetes client for impersonation.
435- var impersonatorOpts []runtimeClient.ImpersonatorOption
436- if r .DefaultServiceAccount != "" || obj .Spec .ServiceAccountName != "" {
437- impersonatorOpts = append (impersonatorOpts ,
438- runtimeClient .WithServiceAccount (r .DefaultServiceAccount , obj .Spec .ServiceAccountName , obj .GetNamespace ()))
439- }
440- if r .ClusterReader != nil {
441- impersonatorOpts = append (impersonatorOpts , runtimeClient .WithPolling (r .ClusterReader ))
454+ impersonation , err := r .makeImpersonator (ctx , obj )
455+ if err != nil {
456+ return "" , err
442457 }
443- impersonation := runtimeClient .NewImpersonator (r .Client , impersonatorOpts ... )
444458
445459 // Create the Kubernetes client that runs under impersonation.
446460 kubeClient , statusPoller , err := impersonation .GetClient (ctx )
@@ -795,6 +809,45 @@ func (r *ResourceSetReconciler) patch(ctx context.Context,
795809 return nil
796810}
797811
812+ // makeImpersonator creates an impersonator for the ResourceSet.
813+ // It configures service account impersonation and custom health check readers.
814+ func (r * ResourceSetReconciler ) makeImpersonator (ctx context.Context , obj * fluxcdv1.ResourceSet ) (* runtimeClient.Impersonator , error ) {
815+ var impersonatorOpts []runtimeClient.ImpersonatorOption
816+
817+ // Configure service account for impersonation.
818+ if r .DefaultServiceAccount != "" || obj .Spec .ServiceAccountName != "" {
819+ impersonatorOpts = append (impersonatorOpts ,
820+ runtimeClient .WithServiceAccount (r .DefaultServiceAccount , obj .Spec .ServiceAccountName , obj .GetNamespace ()))
821+ }
822+
823+ // Configure the kstatus poller with custom health checks for
824+ // Flux Operator owned resources.
825+ if r .ClusterReader != nil {
826+ kinds := []string {fluxcdv1 .FluxInstanceKind , fluxcdv1 .ResourceSetKind , fluxcdv1 .ResourceSetInputProviderKind }
827+ healthChecks := make ([]kustomize.CustomHealthCheck , 0 , len (kinds ))
828+ for _ , kind := range kinds {
829+ healthChecks = append (healthChecks , kustomize.CustomHealthCheck {
830+ APIVersion : fluxcdv1 .GroupVersion .String (),
831+ Kind : kind ,
832+ HealthCheckExpressions : kustomize.HealthCheckExpressions {
833+ Current : fluxcdv1 .HealthCheckExpr ,
834+ },
835+ })
836+ }
837+
838+ statusReaders , err := cel .PollerWithCustomHealthChecks (ctx , healthChecks )
839+ if err != nil {
840+ return nil , fmt .Errorf ("failed to create custom health check readers: %w" , err )
841+ }
842+
843+ impersonatorOpts = append (impersonatorOpts ,
844+ runtimeClient .WithPolling (r .ClusterReader , statusReaders ... ),
845+ )
846+ }
847+
848+ return runtimeClient .NewImpersonator (r .Client , impersonatorOpts ... ), nil
849+ }
850+
798851func (r * ResourceSetReconciler ) recordMetrics (obj * fluxcdv1.ResourceSet ) error {
799852 if ! obj .ObjectMeta .DeletionTimestamp .IsZero () {
800853 reporter .DeleteMetricsFor (fluxcdv1 .ResourceSetKind , obj .GetName (), obj .GetNamespace ())
0 commit comments