func (c *Controller) genericHandler(
	log logrus.FieldLogger,
	expected reflect.Type,
	e event,
	obj interface{},
) {
	if reflect.TypeOf(obj) != expected {
		log.Errorf("expected to get %v but got %T", expected, obj)
		return
	}
	cleanObj(obj)
	c.log.Debugf("generic handler called: %s: %s", e, reflect.TypeOf(obj))
	c.queue.Add(&item{
		obj:   obj.(runtime.Object),
		event: e,
	})
}
genericHandler is used to add an object to the queue.
package controller
import (
	"context"
	"fmt"
	"reflect"
	"regexp"
	"sync"
	"time"
	"github.com/sirupsen/logrus"
	appsv1 "k8s.io/api/apps/v1"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	storagev1 "k8s.io/api/storage/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
	"castai-agent/internal/castai"
	"castai-agent/internal/config"
	"castai-agent/internal/services/providers/types"
	"castai-agent/internal/services/version"
	"castai-agent/pkg/labels"
)
var (
	sensitiveValuePattern = regexp.MustCompile(`(?i)passwd|pass|password|pwd|secret|token|key`)
)
type Controller struct {
	log                  logrus.FieldLogger
	clusterID            string
	castaiclient         castai.Client
	provider             types.Provider
	queue                workqueue.Interface
	interval             time.Duration
	prepDuration         time.Duration
	initialSleepDuration time.Duration
	informers            map[reflect.Type]cache.SharedInformer
	delta   *delta
	deltaMu sync.Mutex
	spotCache   map[string]bool
	spotCacheMu sync.Mutex
	agentVersion *config.AgentVersion
}
func New(
	log logrus.FieldLogger,
	f informers.SharedInformerFactory,
	castaiclient castai.Client,
	provider types.Provider,
	clusterID string,
	interval time.Duration,
	prepDuration time.Duration,
	initialSleepDuration time.Duration,
	v version.Interface,
	agentVersion *config.AgentVersion,
) *Controller {
	typeInformerMap := map[reflect.Type]cache.SharedInformer{
		reflect.TypeOf(&corev1.Node{}):                  f.Core().V1().Nodes().Informer(),
		reflect.TypeOf(&corev1.Pod{}):                   f.Core().V1().Pods().Informer(),
		reflect.TypeOf(&corev1.PersistentVolume{}):      f.Core().V1().PersistentVolumes().Informer(),
		reflect.TypeOf(&corev1.PersistentVolumeClaim{}): f.Core().V1().PersistentVolumeClaims().Informer(),
		reflect.TypeOf(&corev1.ReplicationController{}): f.Core().V1().ReplicationControllers().Informer(),
		reflect.TypeOf(&corev1.Service{}):               f.Core().V1().Services().Informer(),
		reflect.TypeOf(&appsv1.Deployment{}):            f.Apps().V1().Deployments().Informer(),
		reflect.TypeOf(&appsv1.ReplicaSet{}):            f.Apps().V1().ReplicaSets().Informer(),
		reflect.TypeOf(&appsv1.DaemonSet{}):             f.Apps().V1().DaemonSets().Informer(),
		reflect.TypeOf(&appsv1.StatefulSet{}):           f.Apps().V1().StatefulSets().Informer(),
		reflect.TypeOf(&storagev1.StorageClass{}):       f.Storage().V1().StorageClasses().Informer(),
		reflect.TypeOf(&batchv1.Job{}):                  f.Batch().V1().Jobs().Informer(),
	}
	if v.MinorInt() >= 17 {
		typeInformerMap[reflect.TypeOf(&storagev1.CSINode{})] = f.Storage().V1().CSINodes().Informer()
	}
	c := &Controller{
		log:                  log,
		clusterID:            clusterID,
		castaiclient:         castaiclient,
		provider:             provider,
		interval:             interval,
		prepDuration:         prepDuration,
		initialSleepDuration: initialSleepDuration,
		delta:                newDelta(log, clusterID, v.Full()),
		spotCache:            map[string]bool{},
		queue:                workqueue.NewNamed("castai-agent"),
		informers:            typeInformerMap,
		agentVersion:         agentVersion,
	}
	c.registerEventHandlers()
	return c
}
func (c *Controller) registerEventHandlers() {
	for typ, informer := range c.informers {
		typ := typ
		informer := informer
		log := c.log.WithField("informer", typ.String())
		h := c.createEventHandlers(log, typ)
		informer.AddEventHandler(h)
	}
}
func (c *Controller) createEventHandlers(log logrus.FieldLogger, typ reflect.Type) cache.ResourceEventHandler {
	if typ == reflect.TypeOf(&corev1.Node{}) {
		return c.createNodeEventHandlers(log, typ)
	}
	return c.createGenericEventHandlers(log, typ)
}
func (c *Controller) createNodeEventHandlers(log logrus.FieldLogger, typ reflect.Type) cache.ResourceEventHandler {
	return cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			c.deletedUnknownHandler(log, eventAdd, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
				c.nodeAddHandler(log, eventAdd, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
					c.genericHandler(log, typ, e, obj)
				})
			})
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			c.deletedUnknownHandler(log, eventUpdate, newObj, func(log logrus.FieldLogger, e event, obj interface{}) {
				c.nodeAddHandler(log, e, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
					c.genericHandler(log, typ, e, obj)
				})
			})
		},
		DeleteFunc: func(obj interface{}) {
			c.deletedUnknownHandler(log, eventDelete, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
				c.nodeDeleteHandler(log, e, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
					c.genericHandler(log, typ, e, obj)
				})
			})
		},
	}
}
func (c *Controller) createGenericEventHandlers(log logrus.FieldLogger, typ reflect.Type) cache.ResourceEventHandler {
	return cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			c.deletedUnknownHandler(log, eventAdd, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
				c.genericHandler(log, typ, e, obj)
			})
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			c.deletedUnknownHandler(log, eventUpdate, newObj, func(log logrus.FieldLogger, e event, obj interface{}) {
				c.genericHandler(log, typ, e, obj)
			})
		},
		DeleteFunc: func(obj interface{}) {
			c.deletedUnknownHandler(log, eventDelete, obj, func(log logrus.FieldLogger, e event, obj interface{}) {
				c.genericHandler(log, typ, e, obj)
			})
		},
	}
}
type handlerFunc func(log logrus.FieldLogger, event event, obj interface{})
func (c *Controller) deletedUnknownHandler(log logrus.FieldLogger, e event, obj interface{}, next handlerFunc) {
	if deleted, ok := obj.(cache.DeletedFinalStateUnknown); ok {
		next(log, eventDelete, deleted.Obj)
	} else {
		next(log, e, obj)
	}
}
func (c *Controller) nodeAddHandler(log logrus.FieldLogger, e event, obj interface{}, next handlerFunc) {
	node, ok := obj.(*corev1.Node)
	if !ok {
		log.Errorf("expected to get *corev1.Node but got %T", obj)
		return
	}
	c.spotCacheMu.Lock()
	defer c.spotCacheMu.Unlock()
	spot, ok := c.spotCache[node.Name]
	if !ok {
		var err error
		spot, err = c.provider.IsSpot(context.Background(), node)
		if err != nil {
			log.Warnf("failed to determine whether node %q is spot: %v", node.Name, err)
		} else {
			c.spotCache[node.Name] = spot
		}
	}
	if spot {
		node.Labels[labels.CastaiFakeSpot] = "true"
	}
	next(log, e, node)
}
func (c *Controller) nodeDeleteHandler(log logrus.FieldLogger, e event, obj interface{}, next handlerFunc) {
	node, ok := obj.(*corev1.Node)
	if !ok {
		log.Errorf("expected to get *corev1.Node but got %T", obj)
		return
	}
	c.spotCacheMu.Lock()
	delete(c.spotCache, node.Name)
	c.spotCacheMu.Unlock()
	next(log, e, node)
}