@@ -4,10 +4,12 @@ import (
4
4
"context"
5
5
"errors"
6
6
"fmt"
7
+ "strings"
7
8
8
9
corev1 "k8s.io/api/core/v1"
9
10
discoveryv1 "k8s.io/api/discovery/v1"
10
- apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11
+ apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
12
+ kerrors "k8s.io/apimachinery/pkg/api/errors"
11
13
"k8s.io/apimachinery/pkg/api/meta"
12
14
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13
15
"k8s.io/apimachinery/pkg/fields"
@@ -25,10 +27,12 @@ import (
25
27
apiv1 "sigs.k8s.io/gateway-api/apis/v1"
26
28
apiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
27
29
apiv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
30
+ "sigs.k8s.io/gateway-api/pkg/consts"
28
31
29
32
gloov1 "github.com/solo-io/gloo/projects/controller/pkg/api/v1/kube/apis/gloo.solo.io/v1"
30
33
sologatewayv1 "github.com/solo-io/gloo/projects/gateway/pkg/api/v1/kube/apis/gateway.solo.io/v1"
31
34
"github.com/solo-io/gloo/projects/gateway2/api/v1alpha1"
35
+ "github.com/solo-io/gloo/projects/gateway2/crds"
32
36
"github.com/solo-io/gloo/projects/gateway2/deployer"
33
37
"github.com/solo-io/gloo/projects/gateway2/extensions"
34
38
"github.com/solo-io/gloo/projects/gateway2/query"
@@ -68,14 +72,15 @@ func NewBaseGatewayController(ctx context.Context, cfg GatewayConfig) error {
68
72
controllerBuilder := & controllerBuilder {
69
73
cfg : cfg ,
70
74
reconciler : & controllerReconciler {
71
- cli : cfg .Mgr .GetClient (),
72
- scheme : cfg .Mgr .GetScheme (),
73
- kick : cfg .Kick ,
75
+ controllerName : cfg .ControllerName ,
76
+ cli : cfg .Mgr .GetClient (),
77
+ scheme : cfg .Mgr .GetScheme (),
78
+ kick : cfg .Kick ,
74
79
},
75
80
}
76
81
77
82
return run (ctx ,
78
- controllerBuilder .watchCustomResourceDefinitions ,
83
+ controllerBuilder .watchCRDs ,
79
84
controllerBuilder .watchGwClass ,
80
85
controllerBuilder .watchGw ,
81
86
controllerBuilder .watchHttpRoute ,
@@ -292,21 +297,24 @@ func (c *controllerBuilder) watchGwClass(_ context.Context) error {
292
297
Complete (reconcile .Func (c .reconciler .ReconcileGatewayClasses ))
293
298
}
294
299
295
- func (c * controllerBuilder ) watchCustomResourceDefinitions (_ context.Context ) error {
300
+ // watchCustomResourceDefinitions sets up a controller to watch for changes to specific Gateway API
301
+ // CRDs and triggers GatewayClass reconciliation if generation or annotations change.
302
+ func (c * controllerBuilder ) watchCRDs (_ context.Context ) error {
296
303
return ctrl .NewControllerManagedBy (c .cfg .Mgr ).
297
- WithEventFilter (predicate .And (
304
+ For (& apiextv1.CustomResourceDefinition {}).
305
+ WithEventFilter (predicate .NewPredicateFuncs (func (object client.Object ) bool {
306
+ crd , ok := object .(* apiextv1.CustomResourceDefinition )
307
+ if ! ok {
308
+ return false
309
+ }
310
+ // Check if the CRD is one we care about
311
+ return c .cfg .CRDs .Has (crd .Name )
312
+ })).
313
+ WithEventFilter (predicate .Or (
314
+ predicate.AnnotationChangedPredicate {},
298
315
predicate.GenerationChangedPredicate {},
299
- predicate .NewPredicateFuncs (func (object client.Object ) bool {
300
- crd , ok := object .(* apiextensionsv1.CustomResourceDefinition )
301
- if ! ok {
302
- return false
303
- }
304
- // Check if the CRD is one we care about
305
- return c .cfg .CRDs .Has (crd .Name )
306
- }),
307
316
)).
308
- For (& apiextensionsv1.CustomResourceDefinition {}).
309
- Complete (reconcile .Func (c .reconciler .ReconcileCustomResourceDefinitions ))
317
+ Complete (reconcile .Func (c .reconciler .ReconcileCRDs ))
310
318
}
311
319
312
320
func (c * controllerBuilder ) watchHttpRoute (_ context.Context ) error {
@@ -412,9 +420,10 @@ func (c *controllerBuilder) watchSecrets(ctx context.Context) error {
412
420
}
413
421
414
422
type controllerReconciler struct {
415
- cli client.Client
416
- scheme * runtime.Scheme
417
- kick func (ctx context.Context )
423
+ controllerName string
424
+ cli client.Client
425
+ scheme * runtime.Scheme
426
+ kick func (ctx context.Context )
418
427
}
419
428
420
429
func (r * controllerReconciler ) ReconcileHttpListenerOptions (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
@@ -491,12 +500,6 @@ func (r *controllerReconciler) ReconcileNamespaces(ctx context.Context, req ctrl
491
500
return ctrl.Result {}, nil
492
501
}
493
502
494
- func (r * controllerReconciler ) ReconcileCustomResourceDefinitions (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
495
- // For now, simply trigger the main reconciliation loop
496
- r .kick (ctx )
497
- return ctrl.Result {}, nil
498
- }
499
-
500
503
func (r * controllerReconciler ) ReconcileHttpRoutes (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
501
504
// TODO: consider finding impacted gateways and queue them
502
505
// TODO: consider enabling this
@@ -525,8 +528,41 @@ func (r *controllerReconciler) ReconcileReferenceGrants(ctx context.Context, req
525
528
return ctrl.Result {}, nil
526
529
}
527
530
531
+ func (r * controllerReconciler ) ReconcileCRDs (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
532
+ log := log .FromContext (ctx ).WithValues ("CustomResourceDefinition" , req .Name )
533
+
534
+ log .Info ("reconciling CustomResourceDefinition" )
535
+
536
+ var gatewayClassList apiv1.GatewayClassList
537
+ if err := r .cli .List (ctx , & gatewayClassList ); err != nil {
538
+ log .Error (err , "Failed to list GatewayClasses" )
539
+ return ctrl.Result {}, err
540
+ }
541
+
542
+ for _ , gatewayClass := range gatewayClassList .Items {
543
+ if gatewayClass .Spec .ControllerName == apiv1 .GatewayController (r .controllerName ) {
544
+ req := ctrl.Request {
545
+ NamespacedName : client.ObjectKey {
546
+ Name : gatewayClass .Name ,
547
+ },
548
+ }
549
+
550
+ if _ , err := r .ReconcileGatewayClasses (ctx , req ); err != nil {
551
+ log .Error (err , "Failed to reconcile GatewayClass" , "name" , gatewayClass .Name )
552
+ return ctrl.Result {}, err
553
+ }
554
+ }
555
+ }
556
+
557
+ r .kick (ctx )
558
+
559
+ log .Info ("Reconciled CustomResourceDefinition" )
560
+
561
+ return ctrl.Result {}, nil
562
+ }
563
+
528
564
func (r * controllerReconciler ) ReconcileGatewayClasses (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
529
- log := log .FromContext (ctx ).WithValues ("gwclass " , req .NamespacedName )
565
+ log := log .FromContext (ctx ).WithValues ("GatewayClass " , req .Name )
530
566
531
567
gwclass := & apiv1.GatewayClass {}
532
568
if err := r .cli .Get (ctx , req .NamespacedName , gwclass ); err != nil {
@@ -537,31 +573,74 @@ func (r *controllerReconciler) ReconcileGatewayClasses(ctx context.Context, req
537
573
return ctrl.Result {}, client .IgnoreNotFound (err )
538
574
}
539
575
540
- log .Info ("reconciling gateway class " )
576
+ log .Info ("Reconciling GatewayClass " )
541
577
542
- // mark it as accepted:
578
+ // Initialize the status conditions. No need to set LastTransitionTime since it's handled by SetStatusCondition.
579
+ msg := "Gateway API CRDs are a supported version"
543
580
acceptedCondition := metav1.Condition {
544
581
Type : string (apiv1 .GatewayClassConditionStatusAccepted ),
545
582
Status : metav1 .ConditionTrue ,
546
583
Reason : string (apiv1 .GatewayClassReasonAccepted ),
584
+ Message : msg ,
547
585
ObservedGeneration : gwclass .Generation ,
548
- // no need to set LastTransitionTime, it will be set automatically by SetStatusCondition
549
586
}
550
- meta .SetStatusCondition (& gwclass .Status .Conditions , acceptedCondition )
551
-
552
- // TODO: This should actually check the version of the CRDs in the cluster to be 100% sure
553
- supportedVersionCondition := metav1.Condition {
587
+ supportedCondition := metav1.Condition {
554
588
Type : string (apiv1 .GatewayClassConditionStatusSupportedVersion ),
555
589
Status : metav1 .ConditionTrue ,
556
- ObservedGeneration : gwclass .Generation ,
557
590
Reason : string (apiv1 .GatewayClassReasonSupportedVersion ),
591
+ Message : msg ,
592
+ ObservedGeneration : gwclass .Generation ,
593
+ }
594
+
595
+ // Check CRD versions
596
+ supported , err := r .checkCRDVersions (ctx )
597
+ if err != nil {
598
+ log .Error (err , "Failed to check CRD versions" )
599
+ return ctrl.Result {}, err
600
+ }
601
+
602
+ if ! supported {
603
+ // Update the values of status conditions
604
+ msg = fmt .Sprintf ("Unsupported Gateway API CRDs detected. Supported versions are: %s" ,
605
+ strings .Join (wellknown .SupportedVersions , ", " ))
606
+ acceptedCondition .Status = metav1 .ConditionFalse
607
+ acceptedCondition .Reason = string (apiv1 .GatewayClassReasonUnsupportedVersion )
608
+ acceptedCondition .Message = msg
609
+ supportedCondition .Status = metav1 .ConditionFalse
610
+ supportedCondition .Reason = string (apiv1 .GatewayClassReasonUnsupportedVersion )
611
+ supportedCondition .Message = msg
558
612
}
559
- meta .SetStatusCondition (& gwclass .Status .Conditions , supportedVersionCondition )
613
+
614
+ // Set the status conditions
615
+ meta .SetStatusCondition (& gwclass .Status .Conditions , acceptedCondition )
616
+ meta .SetStatusCondition (& gwclass .Status .Conditions , supportedCondition )
560
617
561
618
if err := r .cli .Status ().Update (ctx , gwclass ); err != nil {
619
+ log .Error (err , "Failed to update GatewayClass status" )
562
620
return ctrl.Result {}, err
563
621
}
564
- log .Info ("updated gateway class status" )
622
+
623
+ log .Info ("Reconciled GatewayClass" )
565
624
566
625
return ctrl.Result {}, nil
567
626
}
627
+
628
+ // checkCRDVersions checks that the "gateway.networking.k8s.io/bundle-version" annotation key is set
629
+ // to a supported version for each required Gateway API CRD.
630
+ func (r * controllerReconciler ) checkCRDVersions (ctx context.Context ) (bool , error ) {
631
+ for _ , crdName := range crds .Required {
632
+ crd := & apiextv1.CustomResourceDefinition {}
633
+ if err := r .cli .Get (ctx , client.ObjectKey {Name : crdName }, crd ); err != nil {
634
+ if kerrors .IsNotFound (err ) {
635
+ return false , nil
636
+ }
637
+ return false , err
638
+ }
639
+
640
+ bundleVersion , exists := crd .Annotations [consts .BundleVersionAnnotation ]
641
+ if ! exists || ! crds .IsSupportedVersion (bundleVersion ) {
642
+ return false , nil
643
+ }
644
+ }
645
+ return true , nil
646
+ }
0 commit comments