Skip to content

Commit 570e600

Browse files
committed
status: Conditionally add CRDs to relatedObjects
Check whether the gatewayclasses, gateways, and istios CRDs actually exist before adding them to relatedObjects. Also, use the "GatewayAPIController" featuregate to determine whether to add the gatewayclasses, gateways, istios, and subscriptions resources to relatedObjects, rather than using the "GatewayAPI" featuregate. Before this change, the operator could add istios to relatedObjects even if the OSSM subscription failed to install. By convention, an operator should only add resources to relatedObjects if those resources exist. This commit fixes OCPBUGS-54745. https://issues.redhat.com/browse/OCPBUGS-54745 * pkg/operator/controller/status/controller.go (gatewaysResourceName, gatewayclassesResourceName, istiosResourceName): New consts for the CRD names. (relatedObjectsCRDs): New var for a string set that contains gatewaysResourceName, gatewayclassesResourceName, and istiosResourceName. (New): Add a watch on CRDs, with a predicate for CRDs with names that are in relatedObjectsCRDs. (Config): Add GatewayAPIControllerEnabled. (Reconcile): Check the GatewayAPIControllerEnabled field in the operator config as well as the haveIstiosResource, haveGatewayclassesResource, and haveGatewaysResource fields in the operatorState object and conditionally add the corresponding resources to relatedObjects. (operatorState): Add haveIstiosResource, haveGatewaysResource, and haveGatewayclassesResource fields. (getOperatorState): Set haveGatewaysResource, haveGatewayclassesResource, and haveIstiosResource. * pkg/operator/operator.go (New): Specify GatewayAPIControllerEnabled in the status controller config. * test/e2e/operator_test.go (TestClusterOperatorStatusRelatedObjects): Expect to see "gateways" and "gatewayclasses" in relatedObjects if the "GatewayAPI" and "GatewayAPIController" featuregates are enabled.
1 parent 58e9a47 commit 570e600

File tree

3 files changed

+124
-31
lines changed

3 files changed

+124
-31
lines changed

pkg/operator/controller/status/controller.go

Lines changed: 101 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import (
2323

2424
corev1 "k8s.io/api/core/v1"
2525

26+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2627
"k8s.io/apimachinery/pkg/api/errors"
2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829
"k8s.io/apimachinery/pkg/types"
30+
"k8s.io/apimachinery/pkg/util/sets"
2931
utilclock "k8s.io/utils/clock"
3032

3133
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -49,13 +51,27 @@ const (
4951

5052
ingressesEqualConditionMessage = "desired and current number of IngressControllers are equal"
5153

54+
// gatewaysResourceName is the name of the Gateway API gateways CRD.
55+
gatewaysResourceName = "gateways.gateway.networking.k8s.io"
56+
// gatewayclassesResourceName is the name of the Gateway API
57+
// gatewayclasses CRD.
58+
gatewayclassesResourceName = "gatewayclasses.gateway.networking.k8s.io"
59+
// istiosResourceName is the name of the Sail Operator istios CRD.
60+
istiosResourceName = "istios.sailoperator.io"
61+
5262
controllerName = "status_controller"
5363
)
5464

55-
var log = logf.Logger.WithName(controllerName)
65+
var (
66+
log = logf.Logger.WithName(controllerName)
67+
68+
// clock is to enable unit testing
69+
clock utilclock.Clock = utilclock.RealClock{}
5670

57-
// clock is to enable unit testing
58-
var clock utilclock.Clock = utilclock.RealClock{}
71+
// relatedObjectsCRDs is a set of names of CRDs that we add to
72+
// relatedObjects if they exist.
73+
relatedObjectsCRDs = sets.New[string](gatewaysResourceName, gatewayclassesResourceName, istiosResourceName)
74+
)
5975

6076
// New creates the status controller. This is the controller that handles all
6177
// the logic for creating the ClusterOperator operator and updating its status.
@@ -115,6 +131,24 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
115131
})); err != nil {
116132
return nil, err
117133
}
134+
if config.GatewayAPIControllerEnabled {
135+
if err := c.Watch(source.Kind[client.Object](operatorCache, &apiextensionsv1.CustomResourceDefinition{}, handler.EnqueueRequestsFromMapFunc(toDefaultIngressController), predicate.Funcs{
136+
CreateFunc: func(e event.CreateEvent) bool {
137+
return relatedObjectsCRDs.Has(e.Object.GetName())
138+
},
139+
UpdateFunc: func(e event.UpdateEvent) bool {
140+
return false
141+
},
142+
DeleteFunc: func(e event.DeleteEvent) bool {
143+
return relatedObjectsCRDs.Has(e.Object.GetName())
144+
},
145+
GenericFunc: func(e event.GenericEvent) bool {
146+
return false
147+
},
148+
})); err != nil {
149+
return nil, err
150+
}
151+
}
118152
}
119153

120154
return c, nil
@@ -123,7 +157,10 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
123157
// Config holds all the things necessary for the controller to run.
124158
type Config struct {
125159
// GatewayAPIEnabled indicates that the "GatewayAPI" featuregate is enabled.
126-
GatewayAPIEnabled bool
160+
GatewayAPIEnabled bool
161+
// GatewayAPIControllerEnabled indicates that the "GatewayAPIControllerEnabled" featuregate is enabled.
162+
GatewayAPIControllerEnabled bool
163+
127164
IngressControllerImage string
128165
CanaryImage string
129166
OperatorReleaseVersion string
@@ -198,11 +235,7 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
198235
Name: state.CanaryNamespace.Name,
199236
})
200237
}
201-
if r.config.GatewayAPIEnabled {
202-
related = append(related, configv1.ObjectReference{
203-
Group: gatewayapiv1.GroupName,
204-
Resource: "gatewayclasses",
205-
})
238+
if r.config.GatewayAPIEnabled && r.config.GatewayAPIControllerEnabled {
206239
if state.haveOSSMSubscription {
207240
subscriptionName := operatorcontroller.ServiceMeshOperatorSubscriptionName()
208241
related = append(related, configv1.ObjectReference{
@@ -211,17 +244,25 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
211244
Namespace: subscriptionName.Namespace,
212245
Name: subscriptionName.Name,
213246
})
214-
if state.IngressNamespace != nil {
215-
related = append(related, configv1.ObjectReference{
216-
Group: sailv1.GroupVersion.Group,
217-
Resource: "istios",
218-
})
219-
related = append(related, configv1.ObjectReference{
220-
Group: gatewayapiv1.GroupName,
221-
Resource: "gateways",
222-
Namespace: "", // Include all namespaces.
223-
})
224-
}
247+
}
248+
if state.haveIstiosResource {
249+
related = append(related, configv1.ObjectReference{
250+
Group: sailv1.GroupVersion.Group,
251+
Resource: "istios",
252+
})
253+
}
254+
if state.haveGatewayclassesResource {
255+
related = append(related, configv1.ObjectReference{
256+
Group: gatewayapiv1.GroupName,
257+
Resource: "gatewayclasses",
258+
})
259+
}
260+
if state.haveGatewaysResource && state.IngressNamespace != nil {
261+
related = append(related, configv1.ObjectReference{
262+
Group: gatewayapiv1.GroupName,
263+
Resource: "gateways",
264+
Namespace: "", // Include all namespaces.
265+
})
225266
}
226267
}
227268

@@ -294,7 +335,16 @@ type operatorState struct {
294335
IngressControllers []operatorv1.IngressController
295336
DNSRecords []iov1.DNSRecord
296337

338+
// haveOSSMSubscription means that the subscription for OSSM 3 exists.
297339
haveOSSMSubscription bool
340+
// haveIstiosResource means that the "istios.sailproject.io" CRD exists.
341+
haveIstiosResource bool
342+
// haveGatewaysResource means that the
343+
// "gateways.gateway.networking.k8s.io" CRD exists.
344+
haveGatewaysResource bool
345+
// haveGatewayclassesResource means that the
346+
// "gatewayclasses.gateway.networking.k8s.io" CRD exists.
347+
haveGatewayclassesResource bool
298348
}
299349

300350
// getOperatorState gets and returns the resources necessary to compute the
@@ -337,6 +387,37 @@ func (r *reconciler) getOperatorState(ctx context.Context, ingressNamespace, can
337387
} else {
338388
state.haveOSSMSubscription = true
339389
}
390+
391+
if r.config.GatewayAPIControllerEnabled {
392+
var (
393+
crd apiextensionsv1.CustomResourceDefinition
394+
gatewaysResourceNamespacedName = types.NamespacedName{Name: gatewaysResourceName}
395+
gatewayclassesResourceNamespacedName = types.NamespacedName{Name: gatewayclassesResourceName}
396+
istiosResourceNamespacedName = types.NamespacedName{Name: istiosResourceName}
397+
)
398+
399+
if err := r.cache.Get(ctx, gatewaysResourceNamespacedName, &crd); err != nil {
400+
if !errors.IsNotFound(err) {
401+
return state, fmt.Errorf("failed to get CRD %q: %v", gatewaysResourceName, err)
402+
}
403+
} else {
404+
state.haveGatewaysResource = true
405+
}
406+
if err := r.cache.Get(ctx, gatewayclassesResourceNamespacedName, &crd); err != nil {
407+
if !errors.IsNotFound(err) {
408+
return state, fmt.Errorf("failed to get CRD %q: %v", gatewayclassesResourceName, err)
409+
}
410+
} else {
411+
state.haveGatewayclassesResource = true
412+
}
413+
if err := r.cache.Get(ctx, istiosResourceNamespacedName, &crd); err != nil {
414+
if !errors.IsNotFound(err) {
415+
return state, fmt.Errorf("failed to get CRD %q: %v", istiosResourceName, err)
416+
}
417+
} else {
418+
state.haveIstiosResource = true
419+
}
420+
}
340421
}
341422

342423
return state, nil

pkg/operator/operator.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,12 @@ func New(config operatorconfig.Config, kubeConfig *rest.Config) (*Operator, erro
208208

209209
// Set up the status controller.
210210
if _, err := statuscontroller.New(mgr, statuscontroller.Config{
211-
Namespace: config.Namespace,
212-
IngressControllerImage: config.IngressControllerImage,
213-
CanaryImage: config.CanaryImage,
214-
OperatorReleaseVersion: config.OperatorReleaseVersion,
215-
GatewayAPIEnabled: gatewayAPIEnabled,
211+
Namespace: config.Namespace,
212+
IngressControllerImage: config.IngressControllerImage,
213+
CanaryImage: config.CanaryImage,
214+
OperatorReleaseVersion: config.OperatorReleaseVersion,
215+
GatewayAPIEnabled: gatewayAPIEnabled,
216+
GatewayAPIControllerEnabled: gatewayAPIControllerEnabled,
216217
}); err != nil {
217218
return nil, fmt.Errorf("failed to create status controller: %v", err)
218219
}

test/e2e/operator_test.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,23 @@ func TestClusterOperatorStatusRelatedObjects(t *testing.T) {
233233
if gatewayAPIEnabled, err := isFeatureGateEnabled(features.FeatureGateGatewayAPI); err != nil {
234234
t.Fatalf("Failed to look up %q featuregate: %v", features.FeatureGateGatewayAPI, err)
235235
} else if gatewayAPIEnabled {
236-
expected = append(expected, configv1.ObjectReference{
237-
Group: "gateway.networking.k8s.io",
238-
Resource: "gatewayclasses",
239-
})
240-
// This test runs before TestGatewayAPI, so we do *not* expect
241-
// to see subscriptions, istios, or gateways in relatedObjects.
236+
if gatewayAPIControllerEnabled, err := isFeatureGateEnabled(features.FeatureGateGatewayAPIController); err != nil {
237+
t.Fatalf("Failed to look up %q featuregate: %v", features.FeatureGateGatewayAPIController, err)
238+
} else if gatewayAPIControllerEnabled {
239+
// This test runs before TestGatewayAPI creates the
240+
// subscription to install OSSM, so we do *not* expect
241+
// to see subscriptions or istios in relatedObjects.
242+
// However, we *do* expect to see gatewayclasses and
243+
// gateways whenever the GatewayAPI and
244+
// GatewayAPIController featuregates are enabled.
245+
expected = append(expected, configv1.ObjectReference{
246+
Group: "gateway.networking.k8s.io",
247+
Resource: "gatewayclasses",
248+
}, configv1.ObjectReference{
249+
Group: "gateway.networking.k8s.io",
250+
Resource: "gateways",
251+
})
252+
}
242253
}
243254

244255
coName := controller.IngressClusterOperatorName()

0 commit comments

Comments
 (0)