Skip to content

Commit 81e314f

Browse files
Merge pull request #933 from Miciah/NE-1277-status-add-Gateway-API-objects-to-relatedObjects
NE-1277: status: Add Gateway API objects to relatedObjects
2 parents 7f0fd6d + 92e89a2 commit 81e314f

File tree

6 files changed

+162
-47
lines changed

6 files changed

+162
-47
lines changed

pkg/operator/controller/status/controller.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88

99
"github.com/google/go-cmp/cmp"
1010
"github.com/google/go-cmp/cmp/cmpopts"
11+
sailv1 "github.com/istio-ecosystem/sail-operator/api/v1"
12+
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
1113

1214
configv1 "github.com/openshift/api/config/v1"
1315
operatorv1 "github.com/openshift/api/operator/v1"
@@ -26,9 +28,12 @@ import (
2628
"k8s.io/apimachinery/pkg/types"
2729
utilclock "k8s.io/utils/clock"
2830

31+
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
32+
2933
"sigs.k8s.io/controller-runtime/pkg/cache"
3034
"sigs.k8s.io/controller-runtime/pkg/client"
3135
"sigs.k8s.io/controller-runtime/pkg/controller"
36+
"sigs.k8s.io/controller-runtime/pkg/event"
3237
"sigs.k8s.io/controller-runtime/pkg/handler"
3338
"sigs.k8s.io/controller-runtime/pkg/manager"
3439
"sigs.k8s.io/controller-runtime/pkg/predicate"
@@ -92,11 +97,33 @@ func New(mgr manager.Manager, config Config) (controller.Controller, error) {
9297
if err := c.Watch(source.Kind[client.Object](operatorCache, &configv1.ClusterOperator{}, handler.EnqueueRequestsFromMapFunc(toDefaultIngressController), predicate.NewPredicateFuncs(isIngressClusterOperator))); err != nil {
9398
return nil, err
9499
}
100+
101+
if config.GatewayAPIEnabled {
102+
if err := c.Watch(source.Kind[client.Object](operatorCache, &operatorsv1alpha1.Subscription{}, handler.EnqueueRequestsFromMapFunc(toDefaultIngressController), predicate.Funcs{
103+
CreateFunc: func(e event.CreateEvent) bool {
104+
return e.Object.GetNamespace() == operatorcontroller.OpenshiftOperatorNamespace
105+
},
106+
UpdateFunc: func(e event.UpdateEvent) bool {
107+
return false
108+
},
109+
DeleteFunc: func(e event.DeleteEvent) bool {
110+
return e.Object.GetNamespace() == operatorcontroller.OpenshiftOperatorNamespace
111+
},
112+
GenericFunc: func(e event.GenericEvent) bool {
113+
return false
114+
},
115+
})); err != nil {
116+
return nil, err
117+
}
118+
}
119+
95120
return c, nil
96121
}
97122

98123
// Config holds all the things necessary for the controller to run.
99124
type Config struct {
125+
// GatewayAPIEnabled indicates that the "GatewayAPI" featuregate is enabled.
126+
GatewayAPIEnabled bool
100127
IngressControllerImage string
101128
CanaryImage string
102129
OperatorReleaseVersion string
@@ -159,6 +186,10 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
159186
related = append(related, configv1.ObjectReference{
160187
Resource: "namespaces",
161188
Name: state.IngressNamespace.Name,
189+
}, configv1.ObjectReference{
190+
Group: iov1.GroupVersion.Group,
191+
Resource: "dnsrecords",
192+
Namespace: state.IngressNamespace.Name,
162193
})
163194
}
164195
if state.CanaryNamespace != nil {
@@ -167,6 +198,33 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
167198
Name: state.CanaryNamespace.Name,
168199
})
169200
}
201+
if r.config.GatewayAPIEnabled {
202+
related = append(related, configv1.ObjectReference{
203+
Group: gatewayapiv1.GroupName,
204+
Resource: "gatewayclasses",
205+
})
206+
if state.haveOSSMSubscription {
207+
subscriptionName := operatorcontroller.ServiceMeshOperatorSubscriptionName()
208+
related = append(related, configv1.ObjectReference{
209+
Group: operatorsv1alpha1.GroupName,
210+
Resource: "subscriptions",
211+
Namespace: subscriptionName.Namespace,
212+
Name: subscriptionName.Name,
213+
})
214+
if state.IngressNamespace != nil {
215+
related = append(related, configv1.ObjectReference{
216+
Group: sailv1.GroupVersion.Group,
217+
Resource: "istios",
218+
Namespace: state.IngressNamespace.Name,
219+
})
220+
related = append(related, configv1.ObjectReference{
221+
Group: gatewayapiv1.GroupName,
222+
Resource: "gateways",
223+
Namespace: state.IngressNamespace.Name,
224+
})
225+
}
226+
}
227+
}
170228

171229
co.Status.RelatedObjects = related
172230

@@ -236,6 +294,8 @@ type operatorState struct {
236294
CanaryNamespace *corev1.Namespace
237295
IngressControllers []operatorv1.IngressController
238296
DNSRecords []iov1.DNSRecord
297+
298+
haveOSSMSubscription bool
239299
}
240300

241301
// getOperatorState gets and returns the resources necessary to compute the
@@ -268,6 +328,18 @@ func (r *reconciler) getOperatorState(ingressNamespace, canaryNamespace string)
268328
state.IngressControllers = ingressList.Items
269329
}
270330

331+
if r.config.GatewayAPIEnabled {
332+
var subscription operatorsv1alpha1.Subscription
333+
subscriptionName := operatorcontroller.ServiceMeshOperatorSubscriptionName()
334+
if err := r.cache.Get(context.TODO(), subscriptionName, &subscription); err != nil {
335+
if !errors.IsNotFound(err) {
336+
return state, fmt.Errorf("failed to get subscription %q: %v", subscriptionName, err)
337+
}
338+
} else {
339+
state.haveOSSMSubscription = true
340+
}
341+
}
342+
271343
return state, nil
272344
}
273345

pkg/operator/operator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ func New(config operatorconfig.Config, kubeConfig *rest.Config) (*Operator, erro
212212
IngressControllerImage: config.IngressControllerImage,
213213
CanaryImage: config.CanaryImage,
214214
OperatorReleaseVersion: config.OperatorReleaseVersion,
215+
GatewayAPIEnabled: gatewayAPIEnabled,
215216
}); err != nil {
216217
return nil, fmt.Errorf("failed to create status controller: %v", err)
217218
}

test/e2e/canary_test.go

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,65 +36,66 @@ import (
3636
func TestCanaryRoute(t *testing.T) {
3737
kubeConfig, err := config.GetConfig()
3838
if err != nil {
39-
t.Fatalf("failed to get kube config: %v", err)
39+
t.Fatalf("Failed to get kube config: %v", err)
4040
}
4141

4242
client, err := kubernetes.NewForConfig(kubeConfig)
4343
if err != nil {
44-
t.Fatalf("failed to create kube client: %v", err)
44+
t.Fatalf("Failed to create kube client: %v", err)
4545
}
4646

47-
// check that the default ingress controller is ready.
47+
t.Log("Checking that the default ingresscontroller is ready...")
4848
def := &operatorv1.IngressController{}
4949
if err := waitForIngressControllerCondition(t, kclient, 5*time.Minute, defaultName, defaultAvailableConditions...); err != nil {
50-
t.Fatalf("failed to observe expected conditions: %v", err)
50+
t.Fatalf("Failed to observe expected conditions: %v", err)
5151
}
5252

5353
if err := kclient.Get(context.TODO(), defaultName, def); err != nil {
54-
t.Fatalf("failed to get default ingresscontroller: %v", err)
54+
t.Fatalf("Failed to get the default ingresscontroller: %v", err)
5555
}
5656

57-
// Get default ingress controller deployment.
57+
t.Log("Getting the default ingresscontroller deployment...")
5858
deployment := &appsv1.Deployment{}
5959
if err := kclient.Get(context.TODO(), controller.RouterDeploymentName(def), deployment); err != nil {
60-
t.Fatalf("failed to get ingresscontroller deployment: %v", err)
60+
t.Fatalf("Failed to get the router deployment: %v", err)
6161
}
6262

63-
// Get canary route.
63+
t.Log("Getting the canary route...")
6464
canaryRoute := &routev1.Route{}
6565
name := controller.CanaryRouteName()
6666
err = wait.PollImmediate(1*time.Second, 1*time.Minute, func() (bool, error) {
6767
if err := kclient.Get(context.TODO(), name, canaryRoute); err != nil {
68-
t.Logf("failed to get canary route %s: %v", name, err)
68+
t.Logf("Failed to get route %s: %v", name, err)
6969
return false, nil
7070
}
7171

7272
return true, nil
7373
})
7474
if err != nil {
75-
t.Fatalf("failed to observe canary route: %v", err)
75+
t.Fatalf("Failed to observe canary route: %v", err)
7676
}
7777

7878
canaryRouteHost := getRouteHost(canaryRoute, defaultName.Name)
7979
if canaryRouteHost == "" {
80-
t.Fatalf("failed to find host name for the %q router in route %s/%s: %#v", defaultName.Name, name.Namespace, name.Name, canaryRoute)
80+
t.Fatalf("Failed to find host name for the %q router in route %s: %+v", defaultName.Name, name, canaryRoute)
8181
}
8282

8383
image := deployment.Spec.Template.Spec.Containers[0].Image
8484
clientPod := buildCanaryCurlPod("canary-route-check", canaryRoute.Namespace, image, canaryRouteHost)
8585
if err := kclient.Create(context.TODO(), clientPod); err != nil {
86-
t.Fatalf("failed to create pod %s/%s: %v", clientPod.Namespace, clientPod.Name, err)
86+
t.Fatalf("Failed to create pod %s/%s: %v", clientPod.Namespace, clientPod.Name, err)
8787
}
8888
t.Cleanup(func() {
8989
if err := kclient.Delete(context.TODO(), clientPod); err != nil {
9090
if errors.IsNotFound(err) {
9191
return
9292
}
93-
t.Errorf("failed to delete pod %s/%s: %v", clientPod.Namespace, clientPod.Name, err)
93+
t.Errorf("Failed to delete pod %s/%s: %v", clientPod.Namespace, clientPod.Name, err)
9494
}
9595
})
9696

97-
// Test canary route and verify that the hello-openshift echo pod is running properly.
97+
t.Log("Curl the canary route and verify that it sends the expected response...")
98+
var lines []string
9899
err = wait.PollImmediate(1*time.Second, 5*time.Minute, func() (bool, error) {
99100
readCloser, err := client.CoreV1().Pods(clientPod.Namespace).GetLogs(clientPod.Name, &corev1.PodLogOptions{
100101
Container: "curl",
@@ -103,14 +104,17 @@ func TestCanaryRoute(t *testing.T) {
103104
if err != nil {
104105
return false, nil
105106
}
107+
106108
scanner := bufio.NewScanner(readCloser)
107-
t.Cleanup(func() {
109+
defer func() {
108110
if err := readCloser.Close(); err != nil {
109-
t.Errorf("failed to close reader for pod %s: %v", clientPod.Name, err)
111+
t.Errorf("Failed to close reader for logs from pod %s/%s: %v", clientPod.Namespace, clientPod.Name, err)
110112
}
111-
})
113+
}()
114+
112115
foundBody := false
113116
foundRequestPortHeader := false
117+
lines = []string{}
114118
for scanner.Scan() {
115119
line := scanner.Text()
116120
if strings.Contains(line, canarycontroller.CanaryHealthcheckResponse) {
@@ -119,14 +123,21 @@ func TestCanaryRoute(t *testing.T) {
119123
if strings.Contains(strings.ToLower(line), "x-request-port:") {
120124
foundRequestPortHeader = true
121125
}
122-
if foundBody && foundRequestPortHeader {
123-
return true, nil
124-
}
126+
lines = append(lines, line)
125127
}
128+
129+
if foundBody && foundRequestPortHeader {
130+
t.Log("Found the expected response body and header")
131+
132+
return true, nil
133+
}
134+
126135
return false, nil
127136
})
128137
if err != nil {
129-
t.Fatalf("failed to observe the expected canary response body: %v", err)
138+
t.Logf("Got pods logs:\n%s", strings.Join(lines, "\n"))
139+
140+
t.Fatalf("Failed to observe the expected canary response body: %v", err)
130141
}
131142
}
132143

test/e2e/gateway_api_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,27 +492,27 @@ func ensureGatewayObjectCreation(ns *corev1.Namespace) error {
492492
// ensureGatewayObjectSuccess tests that gateway class, gateway, and http route objects were accepted as valid,
493493
// and that a curl to the application via the http route returns with a valid response.
494494
func ensureGatewayObjectSuccess(t *testing.T, ns *corev1.Namespace) []string {
495-
t.Helper()
496495
errs := []string{}
497496
gateway := &gatewayapiv1.Gateway{}
498497

499-
// Make sure gateway class was created successfully.
498+
t.Log("Making sure the gatewayclass is created and accepted...")
500499
_, err := assertGatewayClassSuccessful(t, operatorcontroller.OpenShiftDefaultGatewayClassName)
501500
if err != nil {
502501
errs = append(errs, error.Error(err))
503502
}
504503

505-
// Make sure gateway was created successfully.
504+
t.Log("Making sure the gateway is created and accepted...")
506505
gateway, err = assertGatewaySuccessful(t, operatorcontroller.DefaultOperandNamespace, testGatewayName)
507506
if err != nil {
508507
errs = append(errs, error.Error(err))
509508
}
510509

510+
t.Log("Making sure the httproute is created and accepted...")
511511
_, err = assertHttpRouteSuccessful(t, ns.Name, "test-httproute", gateway)
512512
if err != nil {
513513
errs = append(errs, error.Error(err))
514514
} else {
515-
// Validate the connectivity to the backend app via http route.
515+
t.Log("Validating the connectivity to the backend application via the httproute...")
516516
err = assertHttpRouteConnection(t, defaultRoutename, gateway)
517517
if err != nil {
518518
errs = append(errs, error.Error(err))

test/e2e/operator_test.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,24 +219,49 @@ func TestClusterOperatorStatusRelatedObjects(t *testing.T) {
219219
Resource: "namespaces",
220220
Name: "openshift-ingress",
221221
},
222+
{
223+
Group: iov1.GroupVersion.Group,
224+
Resource: "dnsrecords",
225+
Namespace: "openshift-ingress",
226+
},
222227
{
223228
Resource: "namespaces",
224229
Name: "openshift-ingress-canary",
225230
},
226231
}
227232

233+
if gatewayAPIEnabled, err := isFeatureGateEnabled(features.FeatureGateGatewayAPI); err != nil {
234+
t.Fatalf("Failed to look up %q featuregate: %v", features.FeatureGateGatewayAPI, err)
235+
} 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.
242+
}
243+
228244
coName := controller.IngressClusterOperatorName()
229245
err := wait.PollImmediate(1*time.Second, 5*time.Minute, func() (bool, error) {
230246
co := &configv1.ClusterOperator{}
231247
if err := kclient.Get(context.TODO(), coName, co); err != nil {
232-
t.Logf("failed to get ingress cluster operator %s: %v", coName, err)
248+
t.Logf("Failed to get clusteroperator %q: %v", coName.Name, err)
249+
233250
return false, nil
234251
}
235252

236-
return reflect.DeepEqual(expected, co.Status.RelatedObjects), nil
253+
if !reflect.DeepEqual(expected, co.Status.RelatedObjects) {
254+
t.Logf("Expected %+v, found %+v", expected, co.Status.RelatedObjects)
255+
256+
return false, nil
257+
}
258+
259+
t.Log("Found the expected status.relatedObjects")
260+
261+
return true, nil
237262
})
238263
if err != nil {
239-
t.Errorf("did not get expected status related objects: %v", err)
264+
t.Errorf("Did not get expected status related objects: %v", err)
240265
}
241266
}
242267

0 commit comments

Comments
 (0)