code: func NewCRDRegistrationController(crdinformer crdinformers.CustomResourceDefinitionInformer, apiServiceRegistration AutoAPIServiceRegistration) *crdRegistrationController {
  	c := &crdRegistrationController{
  		crdLister:              crdinformer.Lister(),
  		crdSynced:              crdinformer.Informer().HasSynced,
  		apiServiceRegistration: apiServiceRegistration,
  		syncedInitialSet:       make(chan struct{}),
  		queue:                  workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "crd_autoregistration_controller"),
  	}
  	c.syncHandler = c.handleVersionUpdate
  
  	crdinformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  		AddFunc: func(obj interface{}) {
  			cast := obj.(*apiextensions.CustomResourceDefinition)
  			c.enqueueCRD(cast)
  		},
  		UpdateFunc: func(oldObj, newObj interface{}) {
  			// Enqueue both old and new object to make sure we remove and add appropriate API services.
  			// The working queue will resolve any duplicates and only changes will stay in the queue.
  			c.enqueueCRD(oldObj.(*apiextensions.CustomResourceDefinition))
  			c.enqueueCRD(newObj.(*apiextensions.CustomResourceDefinition))
  		},
  		DeleteFunc: func(obj interface{}) {
  			cast, ok := obj.(*apiextensions.CustomResourceDefinition)
  			if !ok {
  				tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
  				if !ok {
  					klog.V(2).Infof("Couldn't get object from tombstone %#v", obj)
  					return
  				}
  				cast, ok = tombstone.Obj.(*apiextensions.CustomResourceDefinition)
  				if !ok {
  					klog.V(2).Infof("Tombstone contained unexpected object: %#v", obj)
  					return
  				}
  			}
  			c.enqueueCRD(cast)
  		},
  	})
  
  	return c
  }
docs: // NewCRDRegistrationController returns a controller which will register CRD GroupVersions with the auto APIService registration
  // controller so they automatically stay in sync.