Skip to content

Commit 1c68435

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 f3fa969 commit 1c68435

File tree

3 files changed

+116
-21
lines changed

3 files changed

+116
-21
lines changed

pkg/operator/controller/status/controller.go

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
@@ -116,6 +132,24 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
116132
})); err != nil {
117133
return nil, err
118134
}
135+
if config.GatewayAPIControllerEnabled {
136+
if err := c.Watch(source.Kind[client.Object](operatorCache, &apiextensionsv1.CustomResourceDefinition{}, handler.EnqueueRequestsFromMapFunc(toDefaultIngressController), predicate.Funcs{
137+
CreateFunc: func(e event.CreateEvent) bool {
138+
return relatedObjectsCRDs.Has(e.Object.GetName())
139+
},
140+
UpdateFunc: func(e event.UpdateEvent) bool {
141+
return false
142+
},
143+
DeleteFunc: func(e event.DeleteEvent) bool {
144+
return relatedObjectsCRDs.Has(e.Object.GetName())
145+
},
146+
GenericFunc: func(e event.GenericEvent) bool {
147+
return false
148+
},
149+
})); err != nil {
150+
return nil, err
151+
}
152+
}
119153
}
120154

121155
return c, nil
@@ -124,7 +158,10 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
124158
// Config holds all the things necessary for the controller to run.
125159
type Config struct {
126160
// GatewayAPIEnabled indicates that the "GatewayAPI" featuregate is enabled.
127-
GatewayAPIEnabled bool
161+
GatewayAPIEnabled bool
162+
// GatewayAPIControllerEnabled indicates that the "GatewayAPIControllerEnabled" featuregate is enabled.
163+
GatewayAPIControllerEnabled bool
164+
128165
IngressControllerImage string
129166
CanaryImage string
130167
OperatorReleaseVersion string
@@ -206,11 +243,7 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
206243
Name: state.CanaryNamespace.Name,
207244
})
208245
}
209-
if r.config.GatewayAPIEnabled {
210-
related = append(related, configv1.ObjectReference{
211-
Group: gatewayapiv1.GroupName,
212-
Resource: "gatewayclasses",
213-
})
246+
if r.config.GatewayAPIEnabled && r.config.GatewayAPIControllerEnabled {
214247
if state.haveOSSMSubscription {
215248
subscriptionName := operatorcontroller.ServiceMeshOperatorSubscriptionName()
216249
related = append(related, configv1.ObjectReference{
@@ -219,10 +252,20 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
219252
Namespace: subscriptionName.Namespace,
220253
Name: subscriptionName.Name,
221254
})
255+
}
256+
if state.haveIstiosResource {
222257
related = append(related, configv1.ObjectReference{
223258
Group: sailv1.GroupVersion.Group,
224259
Resource: "istios",
225260
})
261+
}
262+
if state.haveGatewayclassesResource {
263+
related = append(related, configv1.ObjectReference{
264+
Group: gatewayapiv1.GroupName,
265+
Resource: "gatewayclasses",
266+
})
267+
}
268+
if state.haveGatewaysResource {
226269
related = append(related, configv1.ObjectReference{
227270
Group: gatewayapiv1.GroupName,
228271
Resource: "gateways",
@@ -300,8 +343,17 @@ type operatorState struct {
300343
IngressControllers []operatorv1.IngressController
301344
DNSRecords []iov1.DNSRecord
302345

303-
haveOSSMSubscription bool
304346
unmanagedGatewayAPICRDNames string
347+
// haveOSSMSubscription means that the subscription for OSSM 3 exists.
348+
haveOSSMSubscription bool
349+
// haveIstiosResource means that the "istios.sailproject.io" CRD exists.
350+
haveIstiosResource bool
351+
// haveGatewaysResource means that the
352+
// "gateways.gateway.networking.k8s.io" CRD exists.
353+
haveGatewaysResource bool
354+
// haveGatewayclassesResource means that the
355+
// "gatewayclasses.gateway.networking.k8s.io" CRD exists.
356+
haveGatewayclassesResource bool
305357
}
306358

307359
// getOperatorState gets and returns the resources necessary to compute the
@@ -352,6 +404,37 @@ func (r *reconciler) getOperatorState(ctx context.Context, ingressNamespace, can
352404
}
353405
state.unmanagedGatewayAPICRDNames = extension.UnmanagedGatewayAPICRDNames
354406
}
407+
408+
if r.config.GatewayAPIControllerEnabled {
409+
var (
410+
crd apiextensionsv1.CustomResourceDefinition
411+
gatewaysResourceNamespacedName = types.NamespacedName{Name: gatewaysResourceName}
412+
gatewayclassesResourceNamespacedName = types.NamespacedName{Name: gatewayclassesResourceName}
413+
istiosResourceNamespacedName = types.NamespacedName{Name: istiosResourceName}
414+
)
415+
416+
if err := r.cache.Get(ctx, gatewaysResourceNamespacedName, &crd); err != nil {
417+
if !errors.IsNotFound(err) {
418+
return state, fmt.Errorf("failed to get CRD %q: %v", gatewaysResourceName, err)
419+
}
420+
} else {
421+
state.haveGatewaysResource = true
422+
}
423+
if err := r.cache.Get(ctx, gatewayclassesResourceNamespacedName, &crd); err != nil {
424+
if !errors.IsNotFound(err) {
425+
return state, fmt.Errorf("failed to get CRD %q: %v", gatewayclassesResourceName, err)
426+
}
427+
} else {
428+
state.haveGatewayclassesResource = true
429+
}
430+
if err := r.cache.Get(ctx, istiosResourceNamespacedName, &crd); err != nil {
431+
if !errors.IsNotFound(err) {
432+
return state, fmt.Errorf("failed to get CRD %q: %v", istiosResourceName, err)
433+
}
434+
} else {
435+
state.haveIstiosResource = true
436+
}
437+
}
355438
}
356439

357440
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)