func (r *ProgressiveSyncReconciler) requestsForSecretChange(o client.Object) []reconcile.Request {
	var requests []reconcile.Request
	var prList syncv1alpha1.ProgressiveSyncList
	var appList argov1alpha1.ApplicationList
	requestsMap := make(map[types.NamespacedName]bool)
	ctx := context.Background()
	s, ok := o.(*corev1.Secret)
	if !ok {
		err := fmt.Errorf("expected secret, got %T", o)
		r.Log.Error(err, "failed to convert object to secret")
		return nil
	}
	r.Log.Info("received secret event", "name", s.Name, "Namespace", s.Namespace)
	if !utils.IsArgoCDCluster(s.GetLabels()) {
		return nil
	}
	if err := r.List(ctx, &prList); err != nil {
		r.Log.Error(err, "failed to list ProgressiveSync")
		return nil
	}
	if err := r.List(ctx, &appList); err != nil {
		r.Log.Error(err, "failed to list Application")
		return nil
	}
	for _, pr := range prList.Items {
		for _, app := range appList.Items {
			if app.Spec.Destination.Server == string(s.Data["server"]) && pr.Owns(app.GetOwnerReferences()) {
				namespacedName := types.NamespacedName{Name: pr.Name, Namespace: pr.Namespace}
				if _, ok := requestsMap[namespacedName]; !ok {
					requestsMap[namespacedName] = true
					requests = append(requests, reconcile.Request{NamespacedName: namespacedName})
				}
			}
		}
	}
	return requests
}
requestsForSecretChange returns a reconcile request when a Secret changes
package controllers
import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strings"
	"time"
	"k8s.io/apimachinery/pkg/util/json"
	syncv1alpha1 "github.com/Skyscanner/applicationset-progressive-sync/api/v1alpha1"
	"github.com/Skyscanner/applicationset-progressive-sync/internal/consts"
	"github.com/Skyscanner/applicationset-progressive-sync/internal/scheduler"
	"github.com/Skyscanner/applicationset-progressive-sync/internal/utils"
	applicationset "github.com/argoproj-labs/applicationset/api/v1alpha1"
	applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
	argov1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
	"github.com/go-logr/logr"
	corev1 "k8s.io/api/core/v1"
	apimeta "k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/util/retry"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/builder"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"sigs.k8s.io/controller-runtime/pkg/source"
)
const RequeueDelayOnError = time.Minute * 5
type ProgressiveSyncReconciler struct {
	client.Client
	Log             logr.Logger
	Scheme          *runtime.Scheme
	ArgoCDAppClient utils.ArgoCDAppClient
	StateManager    utils.ProgressiveSyncStateManager
	ArgoNamespace   string
}
func (r *ProgressiveSyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := r.Log.WithValues("progressivesync", req.NamespacedName)
	log.Info("reconciliation loop started")
	var ps syncv1alpha1.ProgressiveSync
	if err := r.Get(ctx, req.NamespacedName, &ps); err != nil {
		log.Error(err, "unable to fetch progressivesync object")
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}
	log = log.WithValues("applicationset", ps.Spec.SourceRef.Name)
	if !ps.ObjectMeta.DeletionTimestamp.IsZero() {
		controllerutil.RemoveFinalizer(&ps, syncv1alpha1.ProgressiveSyncFinalizer)
		if err := r.Update(ctx, &ps); err != nil {
			log.Error(err, "failed to update object when removing finalizer")
			return ctrl.Result{}, err
		}
		return ctrl.Result{}, nil
	}
	if !controllerutil.ContainsFinalizer(&ps, syncv1alpha1.ProgressiveSyncFinalizer) {
		controllerutil.AddFinalizer(&ps, syncv1alpha1.ProgressiveSyncFinalizer)
		if err := r.Update(ctx, &ps); err != nil {
			log.Error(err, "failed to update object when adding finalizer")
			return ctrl.Result{}, err
		}
		return ctrl.Result{Requeue: true}, nil
	}
	pss, _ := r.StateManager.Get(ps.Name)
	newHash, err := r.calculateHashedSpec(ctx, &ps)
	if err != nil {
		log.Error(err, "Failed to generate the hash value from the ApplicationSet spec")
		return ctrl.Result{}, err
	}
	currentHash := pss.GetHashedSpec()
	if currentHash != newHash {
		pss.SetHashedSpec(newHash)
		log.Info("Successfully updated the hash value of the ApplicationSet spec", "service", ps.Name)
	} else {
		log.Info("Hash value is the same as nothing has changed in the ApplicationSet spec", "service", ps.Name)
	}
	latest := ps
	var result reconcile.Result
	var reconcileErr error
	for _, stage := range ps.Spec.Stages {
		log = log.WithValues("stage", stage.Name)
		latest, result, reconcileErr = r.reconcileStage(ctx, latest, stage, pss)
		if err := r.updateStatusWithRetry(ctx, &latest); err != nil {
			return ctrl.Result{}, err
		}
		if result.Requeue || reconcileErr != nil {
			log.Info("requeuing stage")
			return result, reconcileErr
		}
	}
	completed := latest.NewStatusCondition(syncv1alpha1.CompletedCondition, metav1.ConditionTrue, syncv1alpha1.StagesCompleteReason, "All stages completed")
	apimeta.SetStatusCondition(latest.GetStatusConditions(), completed)
	if err := r.updateStatusWithRetry(ctx, &latest); err != nil {
		log.Error(err, "failed to update object status")
		return ctrl.Result{}, err
	}
	log.Info("sync completed")
	return ctrl.Result{}, nil
}
func (r *ProgressiveSyncReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&syncv1alpha1.ProgressiveSync{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
		Watches(
			&source.Kind{Type: &argov1alpha1.Application{}},
			handler.EnqueueRequestsFromMapFunc(r.requestsForApplicationChange)).
		Watches(
			&source.Kind{Type: &corev1.Secret{}}, handler.EnqueueRequestsFromMapFunc(r.requestsForSecretChange),
			builder.WithPredicates(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.LabelChangedPredicate{}))).
		Complete(r)
}
func (r *ProgressiveSyncReconciler) requestsForApplicationChange(o client.Object) []reconcile.Request {
	var requests []reconcile.Request
	var list syncv1alpha1.ProgressiveSyncList
	ctx := context.Background()
	app, ok := o.(*argov1alpha1.Application)
	if !ok {
		err := fmt.Errorf("expected application, got %T", o)
		r.Log.Error(err, "failed to convert object to application")
		return nil
	}
	if err := r.List(ctx, &list); err != nil {
		r.Log.Error(err, "failed to list ProgressiveSync")
		return nil
	}
	for _, pr := range list.Items {
		if pr.Owns(app.GetOwnerReferences()) {
			requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{
				Namespace: pr.Namespace,
				Name:      pr.Name,
			}})
			r.Log.Info("application changed", "app", fmt.Sprintf("%s/%s", app.Namespace, app.Name), "sync.status", app.Status.Sync.Status, "health.status", app.Status.Health.Status)
		}
	}
	return requests
}