func CopyMutatedSpecFields(to *reconciler.Object, from *reconciler.Object) {
	e := to.Obj.(*Object)
	o := from.Obj.(*Object)
	e.Obj.SetOwnerReferences(o.Obj.GetOwnerReferences())
	e.Obj.SetResourceVersion(o.Obj.GetResourceVersion())
	switch e.Obj.(type) {
	case *corev1.Service:
		e.Obj.(*corev1.Service).Spec.ClusterIP = o.Obj.(*corev1.Service).Spec.ClusterIP
	case *policyv1.PodDisruptionBudget:
	case *corev1.PersistentVolumeClaim:
		e.Obj.(*corev1.PersistentVolumeClaim).Spec.StorageClassName = o.Obj.(*corev1.PersistentVolumeClaim).Spec.StorageClassName
	}
}
CopyMutatedSpecFields - copy known mutated fields from observed to expected
package k8s
import (
	"bufio"
	"bytes"
	"context"
	"errors"
	"fmt"
	corev1 "k8s.io/api/core/v1"
	policyv1 "k8s.io/api/policy/v1beta1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/apimachinery/pkg/util/yaml"
	"log"
	"os"
	"reflect"
	"sigs.k8s.io/controller-reconciler/pkg/reconciler"
	"sigs.k8s.io/controller-reconciler/pkg/reconciler/manager"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"strings"
	"text/template"
)
const (
	Type = "k8s"
)
var Codecs serializer.CodecFactory
type RsrcManager struct {
	name   string
	client client.Client
	scheme *runtime.Scheme
	codecs serializer.CodecFactory
}
type FileResource struct {
	Path    string
	ObjList metav1.ListInterface
}
func Getter(ctx context.Context, c client.Client, v *runtime.Scheme) func() (string, manager.Manager, error) {
	return func() (string, manager.Manager, error) {
		rm := &RsrcManager{}
		rm.WithClient(c).WithName(Type + "Mgr").WithScheme(v)
		return Type, rm, nil
	}
}
func NewRsrcManager(ctx context.Context, c client.Client, v *runtime.Scheme) *RsrcManager {
	rm := &RsrcManager{}
	rm.WithClient(c).WithName(Type + "Mgr").WithScheme(v).WithCodec(nil)
	return rm
}
func (rm *RsrcManager) WithCodec(codec *serializer.CodecFactory) *RsrcManager {
	if codec != nil {
		rm.codecs = *codec
		Codecs = *codec
	} else {
		rm.codecs = serializer.NewCodecFactory(rm.scheme)
		Codecs = rm.codecs
	}
	return rm
}
func (rm *RsrcManager) WithName(v string) *RsrcManager {
	rm.name = v
	return rm
}
func (rm *RsrcManager) WithClient(v client.Client) *RsrcManager {
	rm.client = v
	return rm
}
func (rm *RsrcManager) WithScheme(v *runtime.Scheme) *RsrcManager {
	rm.scheme = v
	return rm
}
func (rm *RsrcManager) GetScheme() *runtime.Scheme {
	return rm.scheme
}
type Object struct {
	Obj metav1.Object
	ObjList metav1.ListInterface
}
func isReferringSameObject(a, b metav1.OwnerReference) bool {
	aGV, err := schema.ParseGroupVersion(a.APIVersion)
	if err != nil {
		return false
	}
	bGV, err := schema.ParseGroupVersion(b.APIVersion)
	if err != nil {
		return false
	}
	return aGV == bGV && a.Kind == b.Kind && a.Name == b.Name
}
func (o *Object) SetLabels(labels map[string]string) {
	o.Obj.SetLabels(labels)
}
func (o *Object) SetOwnerReferences(ref *metav1.OwnerReference) bool {
	if ref == nil {
		return false
	}
	objRefs := o.Obj.GetOwnerReferences()
	for _, r := range objRefs {
		if isReferringSameObject(*ref, r) {
			return false
		}
	}
	objRefs = append(objRefs, *ref)
	o.Obj.SetOwnerReferences(objRefs)
	return true
}
func (o *Object) IsSameAs(a interface{}) bool {
	same := false
	e := a.(*Object)
	oNamespace := o.Obj.GetNamespace()
	oName := o.Obj.GetName()
	oKind := reflect.TypeOf(o.Obj).String()
	if (e.Obj.GetName() == oName) &&
		(e.Obj.GetNamespace() == oNamespace) &&
		(reflect.TypeOf(e.Obj).String() == oKind) {
		same = true
	}
	return same
}
func (o *Object) GetName() string {
	eNamespace := o.Obj.GetNamespace()
	eName := o.Obj.GetName()
	eKind := reflect.TypeOf(o.Obj).String()
	return eNamespace + "/" + eKind + "/" + eName
}
type Observable struct {
	ObjList metav1.ListInterface
	Obj metav1.Object
	Labels map[string]string
	Type metav1.TypeMeta
}
type LocalObjectReference struct {
	corev1.LocalObjectReference `json:",inline"`
}
func (o *Object) AsReconcilerObject() *reconciler.Object {
	return &reconciler.Object{
		Obj:       o,
		Lifecycle: reconciler.LifecycleManaged,
		Type:      Type,
	}
}
func itemFromReader(name string, b *bufio.Reader, data interface{}, list metav1.ListInterface) (*reconciler.Object, error) {
	var exdoc bytes.Buffer
	r := yaml.NewYAMLReader(b)
	doc, err := r.Read()
	if err == nil {
		tmpl, e := template.New("tmpl").Parse(string(doc))
		err = e
		if err == nil {
			err = tmpl.Execute(&exdoc, data)
			if err == nil {
				d := Codecs.UniversalDeserializer()
				obj, _, e := d.Decode(exdoc.Bytes(), nil, nil)
				err = e
				if err == nil {
					return &reconciler.Object{
						Type:      Type,
						Lifecycle: reconciler.LifecycleManaged,
						Obj: &Object{
							Obj:     obj.DeepCopyObject().(metav1.Object),
							ObjList: list,
						},
					}, nil
				}
				log.Printf("   >>>ERR decoding : %s\n", exdoc.String())
			}
		}
	}
	return nil, errors.New(name + ":" + err.Error())
}
func ObjectFromString(name, spec string, values interface{}, list metav1.ListInterface) (*reconciler.Object, error) {
	return itemFromReader(name, bufio.NewReader(strings.NewReader(spec)), values, list)
}
func ObjectFromFile(path string, values interface{}, list metav1.ListInterface) (*reconciler.Object, error) {
	f, err := os.Open(path)
	if err == nil {
		return itemFromReader(path, bufio.NewReader(f), values, list)
	}
	return nil, err
}
func ObjectsFromFiles(values interface{}, fileResources []FileResource) ([]reconciler.Object, error) {
	items := []reconciler.Object{}
	for _, fr := range fileResources {
		o, err := ObjectFromFile(fr.Path, values, fr.ObjList)
		if err != nil {
			return nil, err
		}
		items = append(items, *o)
	}
	return items, nil
}
func NewObservable(list metav1.ListInterface, labels map[string]string) reconciler.Observable {
	return reconciler.Observable{
		Type: Type,
		Obj: Observable{
			ObjList: list,
			Labels:  labels,
		},
	}
}
func (rm *RsrcManager) ObservablesFromObjects(bag []reconciler.Object, labels map[string]string) []reconciler.Observable {
	var gk schema.GroupKind
	var observables []reconciler.Observable
	gkmap := map[schema.GroupKind]struct{}{}
	for _, item := range bag {
		if item.Type != Type {
			continue
		}
		obj, ok := item.Obj.(*Object)
		if !ok {
			continue
		}
		if obj.ObjList != nil {
			ro := obj.Obj.(runtime.Object)
			kinds, _, err := rm.scheme.ObjectKinds(ro)
			if err == nil {
				if len(kinds) != 1 {
					err = fmt.Errorf("Expected exactly 1 kind for Object %T, but found %s kinds", ro, kinds)
				}
			}
			if err == nil {
				gk = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
			} else {
				gk = ro.GetObjectKind().GroupVersionKind().GroupKind()
			}
			if _, ok := gkmap[gk]; !ok {
				gkmap[gk] = struct{}{}
				observable := reconciler.Observable{
					Type: Type,
					Obj: Observable{
						ObjList: obj.ObjList,
						Labels:  labels,
					},
				}
				observables = append(observables, observable)
			}
		} else {
			observable := reconciler.Observable{
				Type: Type,
				Obj: Observable{
					Obj: obj.Obj,
				},
			}
			observables = append(observables, observable)
		}
	}
	return observables
}
func (s *LocalObjectReference) Validate(fp *field.Path, sfield string, errs field.ErrorList, required bool) field.ErrorList {
	fp = fp.Child(sfield)
	if s == nil {
		if required {
			errs = append(errs, field.Required(fp, "Required "+sfield+" missing"))
		}
		return errs
	}
	if s.Name == "" {
		errs = append(errs, field.Required(fp.Child("name"), "name is required"))
	}
	return errs
}
func FilterObservable(observable []reconciler.Observable, list metav1.ListInterface) []reconciler.Observable {
	var filtered []reconciler.Observable
	ltype := reflect.TypeOf(list).String()
	for i := range observable {
		otype := ""
		o, ok := observable[i].Obj.(Observable)
		if ok {
			otype = reflect.TypeOf(o.ObjList).String()
		}
		if ltype != otype {
			filtered = append(filtered, observable[i])
		}
	}
	return filtered
}
func ReferredItem(obj metav1.Object, name, namespace string) reconciler.Object {
	obj.SetName(name)
	obj.SetNamespace(namespace)
	return reconciler.Object{
		Lifecycle: reconciler.LifecycleReferred,
		Type:      Type,
		Obj:       &Object{Obj: obj},
	}
}
func GetItem(b []reconciler.Object, inobj metav1.Object, name, namespace string) metav1.Object {
	inobj.SetName(name)
	inobj.SetNamespace(namespace)
	for _, item := range reconciler.ObjectsByType(b, Type) {
		obj := item.Obj.(*Object)
		otype := reflect.TypeOf(obj.Obj).String()
		intype := reflect.TypeOf(inobj).String()
		if otype == intype && obj.Obj.GetName() == inobj.GetName() && obj.Obj.GetNamespace() == inobj.GetNamespace() {
			return obj.Obj
		}
	}
	return nil
}
func IsSameKind(o *reconciler.Object, inobj metav1.Object) bool {
	same := false
	if o.Type == Type {
		obj := o.Obj.(*Object)
		okind := reflect.TypeOf(obj.Obj).String()
		inkind := reflect.TypeOf(inobj).String()
		if okind == inkind {
			same = true
		}
	}
	return same
}
func Objs(b []reconciler.Object) []metav1.Object {
	var objs []metav1.Object
	for _, item := range reconciler.ObjectsByType(b, Type) {
		o := item.Obj.(*Object)
		objs = append(objs, o.Obj)
	}
	return objs
}