func (r *ClowdEnvironmentReconciler) SetupWithManager(mgr ctrl.Manager) error {
	r.Recorder = mgr.GetEventRecorderFor("env")
	controller := ctrl.NewControllerManagedBy(mgr).
		Owns(&apps.Deployment{}, builder.WithPredicates(ignoreStatusUpdatePredicate(r.Log, "app"))).
		Owns(&core.Service{}, builder.WithPredicates(ignoreStatusUpdatePredicate(r.Log, "app"))).
		Watches(
			&source.Kind{Type: &crd.ClowdApp{}},
			handler.EnqueueRequestsFromMapFunc(r.envToEnqueueUponAppUpdate),
			builder.WithPredicates(ignoreStatusUpdatePredicate(r.Log, "env")),
		).
		For(&crd.ClowdEnvironment{})
	if clowder_config.LoadedConfig.Features.WatchStrimziResources {
		controller.Owns(&strimzi.Kafka{})
		controller.Owns(&strimzi.KafkaConnect{})
	}
	return controller.Complete(r)
}
SetupWithManager sets up with manager
package controllers
import (
	"context"
	"fmt"
	"sort"
	"sync"
	strimzi "github.com/RedHatInsights/strimzi-client-go/apis/kafka.strimzi.io/v1beta2"
	"github.com/go-logr/logr"
	apps "k8s.io/api/apps/v1"
	core "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	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"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/confighash"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/cronjob"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/database"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/dependencies"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/deployment"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/featureflags"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/inmemorydb"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/iqe"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/kafka"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/logging"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/metrics"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/namespace"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/objectstore"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/pullsecrets"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/serviceaccount"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/servicemesh"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/sidecar"
	_ "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/web"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/utils"
	crd "github.com/RedHatInsights/clowder/apis/cloud.redhat.com/v1alpha1"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/clowder_config"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/errors"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers"
	k8serr "k8s.io/apimachinery/pkg/api/errors"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"sigs.k8s.io/controller-runtime/pkg/source"
)
var mu sync.RWMutex
var cEnv = ""
const envFinalizer = "finalizer.env.cloud.redhat.com"
type ClowdEnvironmentReconciler struct {
	client.Client
	Log      logr.Logger
	Scheme   *runtime.Scheme
	Recorder record.EventRecorder
}
func SetEnv(name string) {
	mu.Lock()
	defer mu.Unlock()
	cEnv = name
}
func ReleaseEnv() {
	mu.Lock()
	defer mu.Unlock()
	cEnv = ""
}
func ReadEnv() string {
	mu.RLock()
	defer mu.RUnlock()
	return cEnv
}
func (r *ClowdEnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	log := r.Log.WithValues("env", req.Name).WithValues("id", utils.RandString(5))
	ctx = context.WithValue(ctx, errors.ClowdKey("log"), &log)
	ctx = context.WithValue(ctx, errors.ClowdKey("recorder"), &r.Recorder)
	env := crd.ClowdEnvironment{}
	err := r.Client.Get(ctx, req.NamespacedName, &env)
	if err != nil {
		if k8serr.IsNotFound(err) {
			return ctrl.Result{}, nil
		}
		return ctrl.Result{}, err
	}
	isEnvMarkedForDeletion := env.GetDeletionTimestamp() != nil
	if isEnvMarkedForDeletion {
		if contains(env.GetFinalizers(), envFinalizer) {
			if err := r.finalizeEnvironment(log, &env); err != nil {
				return ctrl.Result{}, err
			}
			controllerutil.RemoveFinalizer(&env, envFinalizer)
			err := r.Update(ctx, &env)
			if err != nil {
				return ctrl.Result{}, err
			}
		}
		return ctrl.Result{}, nil
	}
	if !contains(env.GetFinalizers(), envFinalizer) {
		if err := r.addFinalizer(log, &env); err != nil {
			return ctrl.Result{}, err
		}
	}
	log.Info("Reconciliation started", "env", env.Name)
	if env.Spec.Disabled {
		log.Info("Reconciliation aborted - set to be disabled", "env", env.Name)
		return ctrl.Result{}, nil
	}
	if env.Status.TargetNamespace == "" {
		if env.Spec.TargetNamespace != "" {
			namespace := core.Namespace{}
			namespaceName := types.NamespacedName{
				Name: env.Spec.TargetNamespace,
			}
			err := r.Client.Get(ctx, namespaceName, &namespace)
			if err != nil {
				r.Recorder.Eventf(&env, "Warning", "NamespaceMissing", "Requested Target Namespace [%s] is missing", env.Spec.TargetNamespace)
				SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, err)
				return ctrl.Result{Requeue: true}, err
			}
			env.Status.TargetNamespace = env.Spec.TargetNamespace
		} else {
			env.Status.TargetNamespace = env.GenerateTargetNamespace()
			namespace := &core.Namespace{}
			namespace.SetName(env.Status.TargetNamespace)
			err := r.Client.Create(ctx, namespace)
			if err != nil {
				SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, err)
				return ctrl.Result{Requeue: true}, err
			}
		}
		err := r.Client.Status().Update(ctx, &env)
		if err != nil {
			SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, err)
			return ctrl.Result{Requeue: true}, err
		}
	}
	ctx = context.WithValue(ctx, errors.ClowdKey("obj"), &env)
	cache := providers.NewObjectCache(ctx, r.Client, scheme)
	provider := providers.Provider{
		Ctx:    ctx,
		Client: r.Client,
		Env:    &env,
		Cache:  &cache,
		Log:    log,
	}
	var requeue = false
	SetEnv(env.Name)
	defer ReleaseEnv()
	provErr := runProvidersForEnv(log, provider)
	if provErr != nil {
		if non_fatal := errors.HandleError(ctx, provErr); !non_fatal {
			SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, provErr)
			return ctrl.Result{}, provErr
		} else {
			requeue = true
		}
	}
	cacheErr := cache.ApplyAll()
	if cacheErr != nil {
		if non_fatal := errors.HandleError(ctx, cacheErr); !non_fatal {
			SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, cacheErr)
			return ctrl.Result{}, cacheErr
		} else {
			requeue = true
		}
	}
	if err == nil {
		if _, ok := managedEnvironments[env.Name]; !ok {
			managedEnvironments[env.Name] = true
		}
		managedEnvsMetric.Set(float64(len(managedEnvironments)))
	}
	err = r.setAppInfo(provider)
	if err != nil {
		SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, err)
		return ctrl.Result{Requeue: true}, err
	}
	if statusErr := SetEnvResourceStatus(ctx, r.Client, &env); statusErr != nil {
		SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, err)
		return ctrl.Result{}, err
	}
	setPrometheusStatus(&env)
	env.Status.Ready = env.IsReady()
	env.Status.Generation = env.Generation
	if err := r.Client.Status().Update(ctx, &env); err != nil {
		SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationFailed, err)
		return ctrl.Result{}, err
	}
	if !requeue {
		r.Recorder.Eventf(&env, "Normal", "SuccessfulReconciliation", "Environment reconciled [%s]", env.GetClowdName())
		log.Info("Reconciliation successful", "env", env.Name)
		err := cache.Reconcile(&env)
		if err != nil {
			return ctrl.Result{Requeue: requeue}, nil
		}
		SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationSuccessful, err)
	} else {
		var err error
		if provErr != nil {
			err = provErr
		} else if cacheErr != nil {
			err = cacheErr
		}
		log.Info("Reconciliation partially successful", "env", fmt.Sprintf("%s:%s", env.Namespace, env.Name))
		r.Recorder.Eventf(&env, "Warning", "SuccessfulPartialReconciliation", "Environment requeued [%s]", env.GetClowdName())
		SetClowdEnvConditions(ctx, r.Client, &env, crd.ReconciliationPartiallySuccessful, err)
	}
	return ctrl.Result{Requeue: requeue}, nil
}
func setPrometheusStatus(env *crd.ClowdEnvironment) {
	var hostname string
	if env.Spec.Providers.Metrics.Mode == "app-interface" {
		hostname = env.Spec.Providers.Metrics.Prometheus.AppInterfaceHostname
	} else {
		hostname = fmt.Sprintf("prometheus-operated.%s.svc.local", env.Status.TargetNamespace)
	}
	env.Status.Prometheus = crd.PrometheusStatus{Hostname: hostname}
}
func runProvidersForEnv(log logr.Logger, provider providers.Provider) error {
	for _, provAcc := range providers.ProvidersRegistration.Registry {
		log.Info("running provider:", "name", provAcc.Name, "order", provAcc.Order)
		if _, err := provAcc.SetupProvider(&provider); err != nil {
			return errors.Wrap(fmt.Sprintf("getprov: %s", provAcc.Name), err)
		}
		log.Info("running provider: complete", "name", provAcc.Name, "order", provAcc.Order)
	}
	return nil
}