func (r *ClowdJobInvocationReconciler) SetupWithManager(mgr ctrl.Manager) error {
	r.Recorder = mgr.GetEventRecorderFor("clowdjobinvocation")
	return ctrl.NewControllerManagedBy(mgr).
		For(&crd.ClowdJobInvocation{}).
		Owns(&batchv1.Job{}).
		Complete(r)
}
SetupWithManager registers the CJI with the main manager process
package controllers
import (
	"context"
	"fmt"
	crd "github.com/RedHatInsights/clowder/apis/cloud.redhat.com/v1alpha1"
	"github.com/go-logr/logr"
	batchv1 "k8s.io/api/batch/v1"
	core "k8s.io/api/core/v1"
	k8serr "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/errors"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/iqe"
	jobProvider "github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers/job"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/providers"
	"github.com/RedHatInsights/clowder/controllers/cloud.redhat.com/utils"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
)
type ClowdJobInvocationReconciler struct {
	client.Client
	Log      logr.Logger
	Scheme   *runtime.Scheme
	Recorder record.EventRecorder
}
var IqeClowdJob = providers.NewSingleResourceIdent("cji", "iqe_clowdjob", &batchv1.Job{})
var ClowdJob = providers.NewMultiResourceIdent("cji", "clowdjob", &batchv1.Job{})
var IqeSecret = providers.NewSingleResourceIdent("cji", "iqe_secret", &core.Secret{})
func (r *ClowdJobInvocationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	qualifiedName := fmt.Sprintf("%s:%s", req.Namespace, req.Name)
	log := r.Log.WithValues("jobinvocation", qualifiedName)
	ctx = context.WithValue(ctx, errors.ClowdKey("log"), &log)
	ctx = context.WithValue(ctx, errors.ClowdKey("recorder"), &r.Recorder)
	cji := crd.ClowdJobInvocation{}
	if err := r.Client.Get(ctx, req.NamespacedName, &cji); err != nil {
		if k8serr.IsNotFound(err) {
			return ctrl.Result{}, nil
		}
		r.Log.Error(err, "CJI not found")
		return ctrl.Result{}, err
	}
	cache := providers.NewObjectCache(ctx, r.Client, r.Scheme)
	if cji.Status.Jobs != nil {
		r.Log.Info("jobinvocation", cji.Name, "Warning: deprecated CJI status; please remove this CJI and reinvoke to reset the Completed status")
		cji.Status.JobMap = map[string]crd.JobConditionState{}
		if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
			return ctrl.Result{}, err
		}
		return ctrl.Result{}, nil
	}
	if cji.Status.Completed {
		r.Log.Info("Job has been completed", "jobinvocation", cji.Name)
		r.Recorder.Eventf(&cji, "Normal", "ClowdJobInvocationComplete", "ClowdJobInvocation [%s] has completed all jobs", cji.Name)
		if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
			return ctrl.Result{}, err
		}
		return ctrl.Result{}, nil
	}
	if cji.Status.JobMap != nil {
		if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
			return ctrl.Result{}, err
		}
		return ctrl.Result{}, nil
	} else {
		r.Log.Info("Reconciliation started", "ClowdJobInvocation", fmt.Sprintf("%s:%s", cji.Namespace, cji.Name))
		ctx = context.WithValue(ctx, errors.ClowdKey("obj"), &cji)
		app := crd.ClowdApp{}
		appErr := r.Client.Get(ctx, types.NamespacedName{
			Name:      cji.Spec.AppName,
			Namespace: req.Namespace,
		}, &app)
		if appErr != nil {
			r.Recorder.Eventf(&cji, "Warning", "ClowdAppMissing", "ClowdApp [%s] is missing; Job cannot be invoked", cji.Spec.AppName)
			if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
				return ctrl.Result{}, err
			}
			return ctrl.Result{Requeue: true}, appErr
		}
		if !app.IsReady() {
			r.Recorder.Eventf(&app, "Warning", "ClowdAppNotReady", "ClowdApp [%s] is not ready", cji.Spec.AppName)
			r.Log.Info("App not yet ready, requeue", "jobinvocation", cji.Spec.AppName, "namespace", app.Namespace)
			if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
				return ctrl.Result{}, err
			}
			return ctrl.Result{Requeue: true}, appErr
		}
		env := crd.ClowdEnvironment{}
		envErr := r.Client.Get(ctx, types.NamespacedName{
			Name: app.Spec.EnvName,
		}, &env)
		if envErr != nil {
			r.Recorder.Eventf(&cji, "Warning", "ClowdEnvMissing", "ClowdEnv [%s] is missing; Job cannot be invoked", app.Spec.EnvName)
			if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
				return ctrl.Result{}, err
			}
			return ctrl.Result{Requeue: true}, envErr
		}
		for _, jobName := range cji.Spec.Jobs {
			job, err := getJobFromName(jobName, &app)
			if err != nil {
				r.Recorder.Eventf(&app, "Warning", "JobNameMissing", "ClowdApp [%s] has no job named", jobName)
				r.Log.Info("Missing Job Definition", "jobinvocation", cji.Spec.AppName, "namespace", app.Namespace)
				if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
					return ctrl.Result{}, err
				}
				return ctrl.Result{}, err
			}
			job.Name = fmt.Sprintf("%s-%s", app.Name, jobName)
			r.Log.Info("Invoking job", "jobinvocation", job.Name, "namespace", app.Namespace)
			if err := r.InvokeJob(ctx, &cache, &job, &app, &env, &cji); err != nil {
				r.Log.Error(err, "Job Invocation Failed", "jobinvocation", jobName, "namespace", app.Namespace)
				if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
					return ctrl.Result{}, err
				}
				r.Recorder.Eventf(&cji, "Warning", "JobNotInvoked", "Job [%s] could not be invoked", jobName)
				return ctrl.Result{Requeue: true}, err
			}
		}
		var emptyTesting crd.IqeJobSpec
		if cji.Spec.Testing.Iqe != emptyTesting {
			nn := types.NamespacedName{
				Name:      cji.GenerateJobName(),
				Namespace: cji.Namespace,
			}
			j := batchv1.Job{}
			if err := cache.Create(IqeClowdJob, nn, &j); err != nil {
				r.Log.Error(err, "Iqe Job could not be created via cache", "jobinvocation", nn.Name)
				if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
					return ctrl.Result{}, err
				}
				return ctrl.Result{}, err
			}
			if err := iqe.CreateIqeJobResource(&cache, &cji, &env, &app, nn, ctx, &j, r.Log, r.Client); err != nil {
				r.Log.Error(err, "Iqe Job creation encountered an error", "jobinvocation", nn.Name)
				r.Recorder.Eventf(&cji, "Warning", "IQEJobFailure", "Job [%s] failed to invoke", j.ObjectMeta.Name)
				if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
					return ctrl.Result{}, err
				}
				return ctrl.Result{}, err
			}
			if err := cache.Update(IqeClowdJob, &j); err != nil {
				r.Log.Error(err, "Iqe Job could not update via cache", "jobinvocation", nn.Name)
				if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
					return ctrl.Result{}, err
				}
				return ctrl.Result{}, err
			}
			r.Log.Info("Iqe Job Invoked Successfully", "jobinvocation", nn.Name, "namespace", app.Namespace)
			if cji.Status.JobMap != nil {
				cji.Status.JobMap[nn.Name] = crd.JobInvoked
			} else {
				cji.Status.JobMap = map[string]crd.JobConditionState{nn.Name: crd.JobInvoked}
			}
			r.Recorder.Eventf(&cji, "Normal", "IQEJobInvoked", "Job [%s] was invoked successfully", j.ObjectMeta.Name)
		}
		if cacheErr := cache.ApplyAll(); cacheErr != nil {
			if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
				return ctrl.Result{}, err
			}
			return ctrl.Result{}, cacheErr
		}
		if err := SetClowdJobInvocationConditions(ctx, r.Client, &cji, crd.ReconciliationSuccessful, nil); err != nil {
			return ctrl.Result{}, err
		}
	}
	return ctrl.Result{}, nil
}
func (r *ClowdJobInvocationReconciler) InvokeJob(ctx context.Context, cache *providers.ObjectCache, job *crd.Job, app *crd.ClowdApp, env *crd.ClowdEnvironment, cji *crd.ClowdJobInvocation) error {
	randomString := utils.RandStringLower(7)
	nn := types.NamespacedName{
		Name:      fmt.Sprintf("%s-%s", job.Name, randomString),
		Namespace: cji.Namespace,
	}
	j := batchv1.Job{}
	if err := cache.Create(ClowdJob, nn, &j); err != nil {
		return err
	}
	jobProvider.CreateJobResource(cji, env, app, nn, job, &j)
	if err := cache.Update(ClowdJob, &j); err != nil {
		return err
	}
	r.Log.Info("Job Invoked Successfully", "jobinvocation", job.Name, "namespace", app.Namespace)
	if cji.Status.JobMap != nil {
		cji.Status.JobMap[j.ObjectMeta.Name] = crd.JobInvoked
	} else {
		cji.Status.JobMap = map[string]crd.JobConditionState{j.ObjectMeta.Name: crd.JobInvoked}
	}
	r.Recorder.Eventf(cji, "Normal", "ClowdJobInvoked", "Job [%s] was invoked successfully", j.ObjectMeta.Name)
	return nil
}
func getJobFromName(jobName string, app *crd.ClowdApp) (job crd.Job, err error) {
	for _, j := range app.Spec.Jobs {
		if j.Name == jobName {
			return j, nil
		}
	}
	return crd.Job{}, errors.New(fmt.Sprintf("No such job %s", jobName))
}