func (c *CrdController) HasSynced() bool {
	return c.Informer.HasSynced()
}
HasSynced indicates when the controller is synced up with the K8s.
package controller
import (
	"fmt"
	"time"
	"github.com/contiv/vpp/plugins/crd/handler"
	"go.ligato.io/cn-infra/v2/logging"
	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	k8sCache "k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
)
const (
	maxRetries = 5
)
var serverStartTime time.Time
type CrdController struct {
	Deps
	Spec CrdSpec
	queue workqueue.RateLimitingInterface
}
type Deps struct {
	Log          logging.Logger
	APIClient    *apiextcs.Clientset
	EventHandler handler.Handler
	Informer     k8sCache.SharedIndexInformer
}
type CrdSpec struct {
	TypeName   string
	Group      string
	Version    string
	Plural     string
	Validation *apiextv1beta1.CustomResourceValidation
}
type Event struct {
	key         string
	eventType   string
	resource    interface{}
	oldResource interface{}
}
func (c *CrdController) Init() error {
	var (
		event Event
		err   error
	)
	c.Log.Infof("%s-Controller: initializing...", c.Spec.TypeName)
	err = c.EventHandler.Init()
	if err != nil {
		c.Log.Error(err)
		return err
	}
	err = c.createCRD(c.Spec.Plural+"."+c.Spec.Group,
		c.Spec.Group,
		c.Spec.Version,
		c.Spec.Plural,
		c.Spec.TypeName)
	if err != nil {
		c.Log.Errorf("Error initializing CRD: %v", err)
		return err
	}
	c.queue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
	c.Informer.AddEventHandler(k8sCache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			event.key, err = k8sCache.MetaNamespaceKeyFunc(obj)
			event.eventType = "create"
			event.resource = obj
			c.Log.Infof("Add %s resource with key: %s", c.Spec.TypeName, event.key)
			if err == nil {
				c.queue.Add(event)
			}
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			event.key, err = k8sCache.MetaNamespaceKeyFunc(newObj)
			event.resource = newObj
			event.oldResource = oldObj
			event.eventType = "update"
			c.Log.Infof("Update %s resource with key: %s", c.Spec.TypeName, event.key)
			if err == nil {
				c.queue.Add(event)
			}
		},
		DeleteFunc: func(obj interface{}) {
			event.key, err = k8sCache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			event.eventType = "delete"
			event.resource = obj
			c.Log.Infof("Delete %s resource with key: %s", c.Spec.TypeName, event.key)
			if err == nil {
				c.queue.Add(event)
			}
		},
	})
	return nil
}
func (c *CrdController) Run(ctx <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()
	c.Log.Infof("%s-Controller: Starting...", c.Spec.TypeName)
	go c.Informer.Run(ctx)
	if !k8sCache.WaitForCacheSync(ctx, c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Error syncing cache"))
		return
	}
	c.Log.Info("Controller.Run: cache sync complete")
	wait.Until(c.runWorker, time.Second, ctx)
}