@@ -24,9 +24,11 @@ import (
2424
2525 corev1 "k8s.io/api/core/v1"
2626
27+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2728 "k8s.io/apimachinery/pkg/api/errors"
2829 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930 "k8s.io/apimachinery/pkg/types"
31+ "k8s.io/apimachinery/pkg/util/sets"
3032 utilclock "k8s.io/utils/clock"
3133
3234 gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -50,13 +52,27 @@ const (
5052
5153 ingressesEqualConditionMessage = "desired and current number of IngressControllers are equal"
5254
55+ // gatewaysResourceName is the name of the Gateway API gateways CRD.
56+ gatewaysResourceName = "gateways.gateway.networking.k8s.io"
57+ // gatewayclassesResourceName is the name of the Gateway API
58+ // gatewayclasses CRD.
59+ gatewayclassesResourceName = "gatewayclasses.gateway.networking.k8s.io"
60+ // istiosResourceName is the name of the Sail Operator istios CRD.
61+ istiosResourceName = "istios.sailoperator.io"
62+
5363 controllerName = "status_controller"
5464)
5565
56- var log = logf .Logger .WithName (controllerName )
66+ var (
67+ log = logf .Logger .WithName (controllerName )
68+
69+ // clock is to enable unit testing
70+ clock utilclock.Clock = utilclock.RealClock {}
5771
58- // clock is to enable unit testing
59- var clock utilclock.Clock = utilclock.RealClock {}
72+ // relatedObjectsCRDs is a set of names of CRDs that we add to
73+ // relatedObjects if they exist.
74+ relatedObjectsCRDs = sets .New [string ](gatewaysResourceName , gatewayclassesResourceName , istiosResourceName )
75+ )
6076
6177// New creates the status controller. This is the controller that handles all
6278// the logic for creating the ClusterOperator operator and updating its status.
@@ -99,15 +115,15 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
99115 return nil , err
100116 }
101117
102- // If the "GatewayAPI" controller featuregate is enabled, watch
103- // subscriptions so that this controller can update status when the OSSM
104- // subscription is created or updated. Note that the subscriptions
105- // resource only exists if the "OperatorLifecycleManager" capability is
106- // enabled, so we cannot watch it if the capability is not enabled.
107- // Additionally, the default catalog only exists if the "marketplace"
108- // capability is enabled, so we cannot install OSSM without that
109- // capability.
110- if config .GatewayAPIEnabled && config .MarketplaceEnabled && config .OperatorLifecycleManagerEnabled {
118+ // If the "GatewayAPI" and "GatewayAPIController" featuregates are
119+ // enabled, watch subscriptions so that this controller can update
120+ // status when the OSSM subscription is created or updated. Note that
121+ // the subscriptions resource only exists if the
122+ // "OperatorLifecycleManager" capability is enabled, so we cannot watch
123+ // it if the capability is not enabled. Additionally, the default
124+ // catalog only exists if the "marketplace" capability is enabled, so we
125+ // cannot install OSSM without that capability.
126+ if config .GatewayAPIEnabled && config .GatewayAPIControllerEnabled && config . MarketplaceEnabled && config .OperatorLifecycleManagerEnabled {
111127 if err := c .Watch (source .Kind [client.Object ](operatorCache , & operatorsv1alpha1.Subscription {}, handler .EnqueueRequestsFromMapFunc (toDefaultIngressController ), predicate.Funcs {
112128 CreateFunc : func (e event.CreateEvent ) bool {
113129 return e .Object .GetNamespace () == operatorcontroller .OpenshiftOperatorNamespace
@@ -124,6 +140,22 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
124140 })); err != nil {
125141 return nil , err
126142 }
143+ if err := c .Watch (source .Kind [client.Object ](operatorCache , & apiextensionsv1.CustomResourceDefinition {}, handler .EnqueueRequestsFromMapFunc (toDefaultIngressController ), predicate.Funcs {
144+ CreateFunc : func (e event.CreateEvent ) bool {
145+ return relatedObjectsCRDs .Has (e .Object .GetName ())
146+ },
147+ UpdateFunc : func (e event.UpdateEvent ) bool {
148+ return false
149+ },
150+ DeleteFunc : func (e event.DeleteEvent ) bool {
151+ return relatedObjectsCRDs .Has (e .Object .GetName ())
152+ },
153+ GenericFunc : func (e event.GenericEvent ) bool {
154+ return false
155+ },
156+ })); err != nil {
157+ return nil , err
158+ }
127159 }
128160
129161 return c , nil
@@ -133,6 +165,9 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
133165type Config struct {
134166 // GatewayAPIEnabled indicates that the "GatewayAPI" featuregate is enabled.
135167 GatewayAPIEnabled bool
168+ // GatewayAPIControllerEnabled indicates that the "GatewayAPIController"
169+ // featuregate is enabled.
170+ GatewayAPIControllerEnabled bool
136171 // MarketplaceEnabled indicates whether the "marketplace" capability is
137172 // enabled.
138173 MarketplaceEnabled bool
@@ -183,7 +218,7 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
183218 }
184219 oldStatus := co .Status .DeepCopy ()
185220
186- state , err := r .getOperatorState (ingressNamespace , canaryNamespace , co )
221+ state , err := r .getOperatorState (ctx , ingressNamespace , canaryNamespace , co )
187222 if err != nil {
188223 return reconcile.Result {}, fmt .Errorf ("failed to get operator state: %v" , err )
189224 }
@@ -220,11 +255,7 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
220255 Name : state .CanaryNamespace .Name ,
221256 })
222257 }
223- if r .config .GatewayAPIEnabled {
224- related = append (related , configv1.ObjectReference {
225- Group : gatewayapiv1 .GroupName ,
226- Resource : "gatewayclasses" ,
227- })
258+ if r .config .GatewayAPIEnabled && r .config .GatewayAPIControllerEnabled {
228259 if state .haveOSSMSubscription {
229260 subscriptionName := operatorcontroller .ServiceMeshOperatorSubscriptionName ()
230261 related = append (related , configv1.ObjectReference {
@@ -233,18 +264,25 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
233264 Namespace : subscriptionName .Namespace ,
234265 Name : subscriptionName .Name ,
235266 })
236- if state .IngressNamespace != nil {
237- related = append (related , configv1.ObjectReference {
238- Group : sailv1 .GroupVersion .Group ,
239- Resource : "istios" ,
240- Namespace : state .IngressNamespace .Name ,
241- })
242- related = append (related , configv1.ObjectReference {
243- Group : gatewayapiv1 .GroupName ,
244- Resource : "gateways" ,
245- Namespace : state .IngressNamespace .Name ,
246- })
247- }
267+ }
268+ if state .haveIstiosResource {
269+ related = append (related , configv1.ObjectReference {
270+ Group : sailv1 .GroupVersion .Group ,
271+ Resource : "istios" ,
272+ })
273+ }
274+ if state .haveGatewayclassesResource {
275+ related = append (related , configv1.ObjectReference {
276+ Group : gatewayapiv1 .GroupName ,
277+ Resource : "gatewayclasses" ,
278+ })
279+ }
280+ if state .haveGatewaysResource {
281+ related = append (related , configv1.ObjectReference {
282+ Group : gatewayapiv1 .GroupName ,
283+ Resource : "gateways" ,
284+ Namespace : "" , // Include all namespaces.
285+ })
248286 }
249287 }
250288
@@ -317,17 +355,26 @@ type operatorState struct {
317355 IngressControllers []operatorv1.IngressController
318356 DNSRecords []iov1.DNSRecord
319357
320- haveOSSMSubscription bool
321358 unmanagedGatewayAPICRDNames string
359+ // haveOSSMSubscription means that the subscription for OSSM 3 exists.
360+ haveOSSMSubscription bool
361+ // haveIstiosResource means that the "istios.sailproject.io" CRD exists.
362+ haveIstiosResource bool
363+ // haveGatewaysResource means that the
364+ // "gateways.gateway.networking.k8s.io" CRD exists.
365+ haveGatewaysResource bool
366+ // haveGatewayclassesResource means that the
367+ // "gatewayclasses.gateway.networking.k8s.io" CRD exists.
368+ haveGatewayclassesResource bool
322369}
323370
324371// getOperatorState gets and returns the resources necessary to compute the
325372// operator's current state.
326- func (r * reconciler ) getOperatorState (ingressNamespace , canaryNamespace string , co * configv1.ClusterOperator ) (operatorState , error ) {
373+ func (r * reconciler ) getOperatorState (ctx context. Context , ingressNamespace , canaryNamespace string , co * configv1.ClusterOperator ) (operatorState , error ) {
327374 state := operatorState {}
328375
329376 ns := & corev1.Namespace {ObjectMeta : metav1.ObjectMeta {Name : ingressNamespace }}
330- if err := r .client .Get (context . TODO () , types.NamespacedName {Name : ingressNamespace }, ns ); err != nil {
377+ if err := r .client .Get (ctx , types.NamespacedName {Name : ingressNamespace }, ns ); err != nil {
331378 if ! errors .IsNotFound (err ) {
332379 return state , fmt .Errorf ("failed to get namespace %q: %v" , ingressNamespace , err )
333380 }
@@ -336,7 +383,7 @@ func (r *reconciler) getOperatorState(ingressNamespace, canaryNamespace string,
336383 }
337384
338385 ns = & corev1.Namespace {ObjectMeta : metav1.ObjectMeta {Name : canaryNamespace }}
339- if err := r .client .Get (context . TODO () , types.NamespacedName {Name : canaryNamespace }, ns ); err != nil {
386+ if err := r .client .Get (ctx , types.NamespacedName {Name : canaryNamespace }, ns ); err != nil {
340387 if ! errors .IsNotFound (err ) {
341388 return state , fmt .Errorf ("failed to get namespace %q: %v" , canaryNamespace , err )
342389 }
@@ -345,32 +392,61 @@ func (r *reconciler) getOperatorState(ingressNamespace, canaryNamespace string,
345392 }
346393
347394 ingressList := & operatorv1.IngressControllerList {}
348- if err := r .cache .List (context . TODO () , ingressList , client .InNamespace (r .config .Namespace )); err != nil {
395+ if err := r .cache .List (ctx , ingressList , client .InNamespace (r .config .Namespace )); err != nil {
349396 return state , fmt .Errorf ("failed to list ingresscontrollers in %q: %v" , r .config .Namespace , err )
350397 } else {
351398 state .IngressControllers = ingressList .Items
352399 }
353400
354401 if r .config .GatewayAPIEnabled {
355- if r .config .MarketplaceEnabled && r .config .OperatorLifecycleManagerEnabled {
402+ if len (co .Status .Extension .Raw ) > 0 {
403+ extension := & IngressOperatorStatusExtension {}
404+ if err := json .Unmarshal (co .Status .Extension .Raw , extension ); err != nil {
405+ return state , fmt .Errorf ("failed to unmarshal status extension of cluster operator %q: %w" , co .Name , err )
406+ }
407+ state .unmanagedGatewayAPICRDNames = extension .UnmanagedGatewayAPICRDNames
408+ }
409+
410+ if r .config .GatewayAPIControllerEnabled && r .config .MarketplaceEnabled && r .config .OperatorLifecycleManagerEnabled {
356411 var subscription operatorsv1alpha1.Subscription
357412 subscriptionName := operatorcontroller .ServiceMeshOperatorSubscriptionName ()
358- if err := r .cache .Get (context . TODO () , subscriptionName , & subscription ); err != nil {
413+ if err := r .cache .Get (ctx , subscriptionName , & subscription ); err != nil {
359414 if ! errors .IsNotFound (err ) {
360415 return state , fmt .Errorf ("failed to get subscription %q: %v" , subscriptionName , err )
361416 }
362417 } else {
363418 state .haveOSSMSubscription = true
364419
365420 }
366- }
367421
368- if len (co .Status .Extension .Raw ) > 0 {
369- extension := & IngressOperatorStatusExtension {}
370- if err := json .Unmarshal (co .Status .Extension .Raw , extension ); err != nil {
371- return state , fmt .Errorf ("failed to unmarshal status extension of cluster operator %q: %w" , co .Name , err )
422+ var (
423+ crd apiextensionsv1.CustomResourceDefinition
424+ gatewaysResourceNamespacedName = types.NamespacedName {Name : gatewaysResourceName }
425+ gatewayclassesResourceNamespacedName = types.NamespacedName {Name : gatewayclassesResourceName }
426+ istiosResourceNamespacedName = types.NamespacedName {Name : istiosResourceName }
427+ )
428+
429+ if err := r .cache .Get (ctx , gatewaysResourceNamespacedName , & crd ); err != nil {
430+ if ! errors .IsNotFound (err ) {
431+ return state , fmt .Errorf ("failed to get CRD %q: %v" , gatewaysResourceName , err )
432+ }
433+ } else {
434+ state .haveGatewaysResource = true
435+ }
436+ if err := r .cache .Get (ctx , gatewayclassesResourceNamespacedName , & crd ); err != nil {
437+ if ! errors .IsNotFound (err ) {
438+ return state , fmt .Errorf ("failed to get CRD %q: %v" , gatewayclassesResourceName , err )
439+ }
440+ } else {
441+ state .haveGatewayclassesResource = true
442+ }
443+ if err := r .cache .Get (ctx , istiosResourceNamespacedName , & crd ); err != nil {
444+ if ! errors .IsNotFound (err ) {
445+ return state , fmt .Errorf ("failed to get CRD %q: %v" , istiosResourceName , err )
446+ }
447+ } else {
448+ state .haveIstiosResource = true
372449 }
373- state .unmanagedGatewayAPICRDNames = extension .UnmanagedGatewayAPICRDNames
374450 }
375451 }
376452
0 commit comments