@@ -22,6 +22,7 @@ import (
2222 "fmt"
2323 "time"
2424
25+ apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
2526 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2627 apierrors "k8s.io/apimachinery/pkg/api/errors"
2728 "k8s.io/apimachinery/pkg/api/meta"
@@ -39,6 +40,7 @@ import (
3940 "github.com/kcp-dev/kcp/pkg/informer"
4041 "github.com/kcp-dev/kcp/pkg/logging"
4142 "github.com/kcp-dev/kcp/pkg/reconciler/apis/apibinding"
43+ "github.com/kcp-dev/kcp/pkg/tombstone"
4244 builtinschemas "github.com/kcp-dev/kcp/pkg/virtual/apiexport/schemas/builtin"
4345 apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
4446 apisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2"
@@ -135,23 +137,28 @@ func NewController(
135137 },
136138 }
137139
138- objOrTombstone := func (obj any ) any {
139- if tombstone , ok := obj .(cache.DeletedFinalStateUnknown ); ok {
140- obj = tombstone .Obj
141- }
142- return obj
143- }
144-
145140 _ , _ = logicalClusterInformer .Informer ().AddEventHandler (cache.ResourceEventHandlerFuncs {
146141 AddFunc : func (obj interface {}) {
147- c .enqueueLogicalCluster (nil , objOrTombstone ( obj ).( * corev1alpha1.LogicalCluster ), opCreate )
142+ c .enqueueLogicalCluster (nil , tombstone. Obj [ * corev1alpha1.LogicalCluster ]( obj ), opCreate )
148143 },
149144 UpdateFunc : func (oldObj , newObj interface {}) {
150- c .enqueueLogicalCluster (objOrTombstone ( oldObj ).( * corev1alpha1.LogicalCluster ),
151- objOrTombstone ( newObj ).( * corev1alpha1.LogicalCluster ), opUpdate )
145+ c .enqueueLogicalCluster (tombstone. Obj [ * corev1alpha1.LogicalCluster ]( oldObj ),
146+ tombstone. Obj [ * corev1alpha1.LogicalCluster ]( newObj ), opUpdate )
152147 },
153148 DeleteFunc : func (obj interface {}) {
154- c .enqueueLogicalCluster (objOrTombstone (obj ).(* corev1alpha1.LogicalCluster ), nil , opDelete )
149+ c .enqueueLogicalCluster (tombstone.Obj [* corev1alpha1.LogicalCluster ](obj ), nil , opDelete )
150+ },
151+ })
152+
153+ _ , _ = apiBindingInformer .Informer ().AddEventHandler (cache.ResourceEventHandlerFuncs {
154+ UpdateFunc : func (oldObj , newObj interface {}) {
155+ c .enqueueAPIBindingUpdate (tombstone.Obj [* apisv1alpha2.APIBinding ](newObj ))
156+ },
157+ })
158+
159+ _ , _ = crdInformer .Informer ().AddEventHandler (cache.ResourceEventHandlerFuncs {
160+ UpdateFunc : func (oldObj , newObj interface {}) {
161+ c .enqueueCRDUpdate (tombstone.Obj [* apiextensionsv1.CustomResourceDefinition ](newObj ))
155162 },
156163 })
157164
@@ -204,6 +211,115 @@ func diffResourceBindingsAnn(oldAnn, newAnn apibinding.ResourceBindingsAnnotatio
204211 return
205212}
206213
214+ func (c * Controller ) enqueueCRDUpdate (crd * apiextensionsv1.CustomResourceDefinition ) {
215+ if ! apiextensionshelpers .IsCRDConditionTrue (crd , apiextensionsv1 .Established ) {
216+ // The CRD is not ready yet. Nothing to do, we'll get notified on the next update event.
217+ return
218+ }
219+
220+ gr := schema.GroupResource {
221+ Group : crd .Spec .Group ,
222+ Resource : crd .Status .AcceptedNames .Plural ,
223+ }
224+
225+ lc , err := c .getLogicalCluster (logicalcluster .From (crd ), corev1alpha1 .LogicalClusterName )
226+ if err != nil {
227+ utilruntime .HandleError (err )
228+ return
229+ }
230+
231+ boundResourcesAnn , err := apibinding .GetResourceBindings (lc )
232+ if err != nil {
233+ utilruntime .HandleError (err )
234+ return
235+ }
236+
237+ if _ , hasCRD := boundResourcesAnn [gr .String ()]; ! hasCRD {
238+ // The CRD is not listed in the resources lock yet.
239+ return
240+ }
241+
242+ // Update the GR by removing and adding it back again.
243+ resourceLock := apibinding.ResourceBindingsAnnotation {
244+ gr .String (): apibinding.ExpirableLock {
245+ Lock : apibinding.Lock {
246+ CRD : true ,
247+ },
248+ },
249+ }
250+ it := queueItem {
251+ ClusterName : logicalcluster .From (crd ),
252+ ClusterResourceName : corev1alpha1 .LogicalClusterName ,
253+ Op : opUpdate ,
254+
255+ ToRemove : resourceLock ,
256+ ToAdd : resourceLock ,
257+ }
258+
259+ keyBytes , err := json .Marshal (& it )
260+ if err != nil {
261+ utilruntime .HandleError (err )
262+ return
263+ }
264+ key := string (keyBytes )
265+
266+ logging .WithQueueKey (logging .WithReconciler (klog .Background (), ControllerName ), key ).WithName (crd .Name ).
267+ V (4 ).Info ("queueing ResourceBindingsAnnotation patch because of CRD" )
268+ c .queue .Add (key )
269+ }
270+
271+ func (c * Controller ) enqueueAPIBindingUpdate (apiBinding * apisv1alpha2.APIBinding ) {
272+ lc , err := c .getLogicalCluster (logicalcluster .From (apiBinding ), corev1alpha1 .LogicalClusterName )
273+ if err != nil {
274+ utilruntime .HandleError (err )
275+ return
276+ }
277+ boundResourcesAnn , err := apibinding .GetResourceBindings (lc )
278+ if err != nil {
279+ utilruntime .HandleError (err )
280+ return
281+ }
282+
283+ it := queueItem {
284+ ClusterName : logicalcluster .From (apiBinding ),
285+ ClusterResourceName : corev1alpha1 .LogicalClusterName ,
286+ Op : opUpdate ,
287+ ToAdd : make (apibinding.ResourceBindingsAnnotation ),
288+ ToRemove : make (apibinding.ResourceBindingsAnnotation ),
289+ }
290+
291+ for _ , boundRes := range apiBinding .Status .BoundResources {
292+ gr := schema.GroupResource {
293+ Group : boundRes .Group ,
294+ Resource : boundRes .Resource ,
295+ }
296+ key := gr .String ()
297+ if _ , hasBinding := boundResourcesAnn [key ]; ! hasBinding {
298+ continue
299+ }
300+
301+ // Update the GR by removing and adding it back again.
302+ resourceLock := apibinding.ExpirableLock {
303+ Lock : apibinding.Lock {
304+ Name : apiBinding .Name ,
305+ },
306+ }
307+ it .ToRemove [key ] = resourceLock
308+ it .ToAdd [key ] = resourceLock
309+ }
310+
311+ keyBytes , err := json .Marshal (& it )
312+ if err != nil {
313+ utilruntime .HandleError (err )
314+ return
315+ }
316+ key := string (keyBytes )
317+
318+ logging .WithQueueKey (logging .WithReconciler (klog .Background (), ControllerName ), key ).WithName (apiBinding .Name ).
319+ V (4 ).Info ("queueing ResourceBindingsAnnotation patch because of APIBinding" )
320+ c .queue .Add (key )
321+ }
322+
207323func (c * Controller ) enqueueLogicalCluster (oldObj * corev1alpha1.LogicalCluster , newObj * corev1alpha1.LogicalCluster , op ctrlOp ) {
208324 oldBoundResourcesAnnStr := getResourceBindingsAnnJSON (oldObj )
209325 newBoundResourcesAnnStr := getResourceBindingsAnnJSON (newObj )
@@ -307,16 +423,20 @@ func (c *Controller) gatherGVKRsForCRD(crd *apiextensionsv1.CustomResourceDefini
307423 if crd == nil {
308424 return nil
309425 }
310- gvkrs := make ([]typeMeta , len (crd .Status .StoredVersions ))
311- for i , crdVersion := range crd .Status .StoredVersions {
312- gvkrs [i ] = newTypeMeta (
426+ gvkrs := make ([]typeMeta , 0 , len (crd .Spec .Versions ))
427+ for _ , version := range crd .Spec .Versions {
428+ if ! version .Served {
429+ continue
430+ }
431+
432+ gvkrs = append (gvkrs , newTypeMeta (
313433 crd .Spec .Group ,
314- crdVersion ,
434+ version . Name ,
315435 crd .Status .AcceptedNames .Kind ,
316436 crd .Status .AcceptedNames .Singular ,
317437 crd .Status .AcceptedNames .Plural ,
318438 resourceScopeToRESTScope (crd .Spec .Scope ),
319- )
439+ ))
320440 }
321441 return gvkrs
322442}
@@ -341,6 +461,10 @@ func (c *Controller) gatherGVKRsForAPIBinding(apiBinding *apisv1alpha2.APIBindin
341461 }
342462
343463 for _ , schVersion := range sch .Spec .Versions {
464+ if ! schVersion .Served {
465+ continue
466+ }
467+
344468 gvkrs = append (gvkrs , newTypeMeta (
345469 sch .Spec .Group ,
346470 schVersion .Name ,
0 commit comments