func (gr *Reconciler) WithDefaulter(d func(interface{})) *Reconciler {
	gr.applyDefaults = d
	return gr
}
WithDefaulter - callback for error handling
package genericreconciler
import (
	"context"
	"fmt"
	apierror "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	urt "k8s.io/apimachinery/pkg/util/runtime"
	"log"
	"reflect"
	"sigs.k8s.io/controller-reconciler/pkg/reconciler"
	rmanager "sigs.k8s.io/controller-reconciler/pkg/reconciler/manager"
	"sigs.k8s.io/controller-reconciler/pkg/reconciler/manager/k8s"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/manager"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"
	"time"
)
const (
	DefaultReconcilePeriod      = 3 * time.Minute
	FinalizeReconcilePeriod     = 30 * time.Second
	CRGetFailureReconcilePeriod = 30 * time.Second
)
func handleErrorArr(info string, name string, e error, errs []error) []error {
	handleError(info, name, e)
	return append(errs, e)
}
func handleError(info string, name string, e error) error {
	urt.HandleError(fmt.Errorf("Failed: [%s] %s. %s", name, info, e.Error()))
	return e
}
func (gr *Reconciler) itemMgr(i reconciler.Object) (rmanager.Manager, error) {
	m := gr.rsrcMgr.Get(i.Type)
	if m == nil {
		return m, fmt.Errorf("Resource Manager not registered for Type: %s", i.Type)
	}
	return m, nil
}
func (gr *Reconciler) ReconcileResource(namespacedname types.NamespacedName) (reconcile.Result, error) {
	var p time.Duration
	period := DefaultReconcilePeriod
	expected := []reconciler.Object{}
	resource := gr.resource.(runtime.Object).DeepCopyObject()
	nname := reflect.TypeOf(resource).String() + "/" + namespacedname.String()
	rm := gr.rsrcMgr.Get("k8s")
	err := k8s.Get(rm, namespacedname, resource.(runtime.Object))
	if err != nil {
		if apierror.IsNotFound(err) {
			urt.HandleError(fmt.Errorf("not found %s. %s", nname, err.Error()))
			return reconcile.Result{}, nil
		}
		return reconcile.Result{RequeueAfter: CRGetFailureReconcilePeriod}, err
	}
	errkind := ""
	if gr.validate != nil {
		log.Printf("%s Validating spec\n", nname)
		err = gr.validate(resource)
	}
	if err == nil {
		if gr.applyDefaults != nil {
			log.Printf("%s Applying defaults\n", nname)
			gr.applyDefaults(resource)
		}
		o := resource.(metav1.Object)
		for _, h := range gr.using {
			if o.GetDeletionTimestamp() == nil {
				p, err = gr.reconcileUsing(h, resource, nname, expected)
			} else {
				err = gr.finalizeUsing(h, resource, nname, expected)
				p = FinalizeReconcilePeriod
			}
			if p != 0 && p < period {
				period = p
			}
			if err != nil {
				break
			}
		}
	} else {
		errkind = "validate"
	}
	if err != nil {
		urt.HandleError(fmt.Errorf("error reconciling %s. %s", nname, err.Error()))
		if gr.errorHandler != nil {
			gr.errorHandler(resource, err, errkind)
		}
	}
	err = rm.Update(reconciler.Object{Obj: &k8s.Object{Obj: resource.(metav1.Object)}})
	if err != nil {
		urt.HandleError(fmt.Errorf("error updating %s. %s", nname, err.Error()))
	}
	return reconcile.Result{RequeueAfter: period}, err
}
func (gr *Reconciler) observe(observables []reconciler.Observable) ([]reconciler.Object, error) {
	seen := []reconciler.Object{}
	for _, m := range gr.rsrcMgr.All() {
		o, err := m.Observe(observables...)
		if err != nil {
			return []reconciler.Object{}, err
		}
		seen = append(seen, o...)
	}
	return seen, nil
}
func (gr *Reconciler) observeAndMutate(h Handler, resource runtime.Object, crname string, aggregated []reconciler.Object) ([]reconciler.Object, []reconciler.Object, []reconciler.Object, string, error) {
	var err error
	var expected, observed, dependent []reconciler.Object
	stage := "dependent resources"
	labels := getLabels(resource, reflect.TypeOf(h).String())
	dependent, err = gr.observe(getAllObservables(&gr.rsrcMgr, dependentResources(h, resource), labels))
	if err == nil && dependent != nil {
		stage = "observing resources"
		observed, err = gr.observe(h.Observables(resource, labels, dependent))
		if err == nil && observed != nil {
			stage = "gathering expected resources"
			expected, err = h.Objects(resource, labels, observed, dependent, aggregated)
		}
	}
	if err != nil {
		observed = []reconciler.Object{}
		expected = []reconciler.Object{}
	}
	if expected == nil {
		expected = []reconciler.Object{}
	}
	if observed == nil {
		observed = []reconciler.Object{}
	}
	return expected, observed, dependent, stage, err
}
func (gr *Reconciler) finalizeUsing(h Handler, resource runtime.Object, crname string, aggregated []reconciler.Object) error {
	cname := crname + "(using:" + reflect.TypeOf(h).String() + ")"
	log.Printf("%s  { finalizing\n", cname)
	defer log.Printf("%s  } finalizing\n", cname)
	expected, observed, dependent, stage, err := gr.observeAndMutate(h, resource, crname, aggregated)
	if err != nil {
		handleError(stage, crname, err)
	}
	aggregated = append(aggregated, expected...)
	stage = "finalizing resource"
	err = finalize(h, resource, observed, dependent)
	if err == nil {
		stage = "finalizing deletion of objects"
		for _, o := range observed {
			oRsrcName := o.Obj.GetName()
			if o.Delete {
				if rm, e := gr.itemMgr(o); e != nil {
					err = e
					break
				} else if e := rm.Delete(o); e != nil {
					err = e
					break
				} else {
					log.Printf("%s   -delete: %s\n", cname, oRsrcName)
				}
			}
		}
	}
	if err != nil {
		handleError(stage, crname, err)
	}
	return err
}
func (gr *Reconciler) ownerRef(resource runtime.Object) *metav1.OwnerReference {
	return metav1.NewControllerRef(resource.(metav1.Object), schema.GroupVersionKind{
		Group:   gr.gv.Group,
		Version: gr.gv.Version,
		Kind:    reflect.TypeOf(gr.resource).String(),
	})
}
func (gr *Reconciler) reconcileUsing(h Handler, resource runtime.Object, crname string, aggregated []reconciler.Object) (time.Duration, error) {
	errs := []error{}
	var reconciled []reconciler.Object
	cname := crname + "(cmpnt:" + reflect.TypeOf(h).String() + ")"
	log.Printf("%s  { reconciling component\n", cname)
	defer log.Printf("%s  } reconciling component\n", cname)
	expected, observed, _, stage, err := gr.observeAndMutate(h, resource, crname, aggregated)
	if err != nil {
		errs = handleErrorArr(stage, crname, err, errs)
	} else {
		aggregated = append(aggregated, expected...)
		log.Printf("%s  Expected Resources:\n", cname)
		for _, e := range expected {
			log.Printf("%s   exp: %s %s\n", cname, e.Type, e.Obj.GetName())
		}
		log.Printf("%s  Observed Resources:\n", cname)
		for _, e := range observed {
			log.Printf("%s   obs: %s\n", cname, e.Obj.GetName())
		}
		log.Printf("%s  Reconciling Resources:\n", cname)
	}
	for _, e := range expected {
		seen := false
		eRsrcName := e.Obj.GetName()
		for _, o := range observed {
			if e.Type != o.Type || !e.Obj.IsSameAs(o.Obj) {
				continue
			}
			reconciled = append(reconciled, o)
			seen = true
			if e.Lifecycle == reconciler.LifecycleReferred {
				log.Printf("%s   notmanaged: %s\n", cname, eRsrcName)
				break
			}
			rm, err := gr.itemMgr(e)
			if err != nil {
				errs = handleErrorArr("update", eRsrcName, err, errs)
				break
			}
			canupdate := e.Lifecycle != reconciler.LifecycleNoUpdate
			compDiffers := differs(h, e, o)
			rmDiffers := rm.SpecDiffers(&e, &o)
			refchange := e.Obj.SetOwnerReferences(gr.ownerRef(resource))
			if canupdate && rmDiffers && compDiffers || refchange {
				if err := rm.Update(e); err != nil {
					errs = handleErrorArr("update", eRsrcName, err, errs)
				} else {
					log.Printf("%s   update: %s\n", cname, eRsrcName)
				}
			} else {
				log.Printf("%s   nochange: %s\n", cname, eRsrcName)
			}
			break
		}
		if !seen {
			if e.Lifecycle != reconciler.LifecycleReferred {
				e.Obj.SetOwnerReferences(gr.ownerRef(resource))
				if rm, err := gr.itemMgr(e); err != nil {
					errs = handleErrorArr("Create", cname, err, errs)
				} else if err := rm.Create(e); err != nil {
					errs = handleErrorArr("Create", cname, err, errs)
				} else {
					log.Printf("%s   +create: %s\n", cname, eRsrcName)
					reconciled = append(reconciled, e)
				}
			} else {
				err := fmt.Errorf("missing resource not managed by %s: %s", cname, eRsrcName)
				errs = handleErrorArr("missing resource", cname, err, errs)
			}
		}
	}
	for _, o := range observed {
		seen := false
		oRsrcName := o.Obj.GetName()
		if o.Lifecycle == reconciler.LifecycleDecorate {
			if o.Update {
				if rm, err := gr.itemMgr(o); err != nil {
					errs = handleErrorArr("decorate", oRsrcName, err, errs)
				} else if err := rm.Update(o); err != nil {
					errs = handleErrorArr("update", oRsrcName, err, errs)
				} else {
					log.Printf("%s   decorate: %s\n", cname, oRsrcName)
				}
			}
			continue
		}
		for _, e := range expected {
			if e.Type == o.Type && e.Obj.IsSameAs(o.Obj) {
				seen = true
				break
			}
		}
		if !seen {
			if rm, err := gr.itemMgr(o); err != nil {
				errs = handleErrorArr("delete", oRsrcName, err, errs)
			} else if err := rm.Delete(o); err != nil {
				errs = handleErrorArr("delete", oRsrcName, err, errs)
			} else {
				log.Printf("%s   -delete: %s\n", cname, oRsrcName)
			}
		}
	}
	err = utilerrors.NewAggregate(errs)
	period := updateStatus(h, resource, reconciled, err)
	return period, err
}
func (gr *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
	r, err := gr.ReconcileResource(request.NamespacedName)
	if err != nil {
		fmt.Printf("err: %s", err.Error())
	}
	return r, err
}
func WithManager(m manager.Manager) *Reconciler {
	gr := new(Reconciler)
	gr.manager = m
	return gr
}
func (gr *Reconciler) For(resource runtime.Object, gv schema.GroupVersion) *Reconciler {
	gr.resource = resource
	gr.gv = gv
	return gr
}
func (gr *Reconciler) Using(h Handler) *Reconciler {
	gr.using = append(gr.using, h)
	return gr
}
func (gr *Reconciler) WithResourceManager(getter func() (string, rmanager.Manager, error)) *Reconciler {
	mgrtype, mgr, err := getter()
	if err == nil {
		gr.rsrcMgr.Add(mgrtype, mgr)
	}
	return gr
}
func (gr *Reconciler) Build() *Reconciler {
	km := k8s.NewRsrcManager(context.TODO(), gr.manager.GetClient(), gr.manager.GetScheme())
	gr.rsrcMgr.Add(k8s.Type, km)
	return gr
}
func (gr *Reconciler) Controller(r reconcile.Reconciler) error {
	if r == nil {
		r = gr
	}
	return ctrl.NewControllerManagedBy(gr.manager).
		For(gr.resource).
		Complete(r)
}
func (gr *Reconciler) WithErrorHandler(eh func(interface{}, error, string)) *Reconciler {
	gr.errorHandler = eh
	return gr
}
func (gr *Reconciler) WithValidator(v func(interface{}) error) *Reconciler {
	gr.validate = v
	return gr
}