Skip to content

Commit b1b9dfa

Browse files
POC - Test Gathering Prometheus data
1 parent e4e76d2 commit b1b9dfa

File tree

8 files changed

+141
-9
lines changed

8 files changed

+141
-9
lines changed

.github/workflows/e2e.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ jobs:
3535
- name: Run e2e tests
3636
run: ARTIFACT_PATH=/tmp/artifacts make test-e2e
3737

38+
- uses: actions/upload-artifact@v4
39+
with:
40+
name: upgrade-e2e-artifacts
41+
path: test/e2e/results/**
42+
3843
- uses: actions/upload-artifact@v4
3944
if: failure()
4045
with:

internal/catalogd/controllers/core/clustercatalog_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ func (r *ClusterCatalogReconciler) SetupWithManager(mgr ctrl.Manager) error {
152152

153153
return ctrl.NewControllerManagedBy(mgr).
154154
For(&ocv1.ClusterCatalog{}).
155+
Named("catalogd-clustercatalog-controller").
155156
Complete(r)
156157
}
157158

internal/catalogd/controllers/core/pull_secret_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func (r *PullSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request)
6464
func (r *PullSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
6565
_, err := ctrl.NewControllerManagedBy(mgr).
6666
For(&corev1.Secret{}).
67+
Named("catalogd-pull-secret-controller").
6768
WithEventFilter(newSecretPredicate(r.SecretKey)).
6869
Build(r)
6970

internal/operator-controller/controllers/clustercatalog_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Reque
9393
// SetupWithManager sets up the controller with the Manager.
9494
func (r *ClusterCatalogReconciler) SetupWithManager(mgr ctrl.Manager) error {
9595
_, err := ctrl.NewControllerManagedBy(mgr).
96+
Named("con-oper-clustercatalog-controller").
9697
For(&ocv1.ClusterCatalog{}).
9798
Build(r)
9899

internal/operator-controller/controllers/clusterextension_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ func SetDeprecationStatus(ext *ocv1.ClusterExtension, bundleName string, depreca
407407
func (r *ClusterExtensionReconciler) SetupWithManager(mgr ctrl.Manager) error {
408408
controller, err := ctrl.NewControllerManagedBy(mgr).
409409
For(&ocv1.ClusterExtension{}).
410+
Named("con-oper-cluster-extension-controller").
410411
Watches(&ocv1.ClusterCatalog{},
411412
crhandler.EnqueueRequestsFromMapFunc(clusterExtensionRequestsForCatalog(mgr.GetClient(), mgr.GetLogger())),
412413
builder.WithPredicates(predicate.Funcs{

internal/operator-controller/controllers/pull_secret_controller.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func (r *PullSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request)
6363
// SetupWithManager sets up the controller with the Manager.
6464
func (r *PullSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
6565
_, err := ctrl.NewControllerManagedBy(mgr).
66+
Named("con-oper-pull-secret-controller").
6667
For(&corev1.Secret{}).
6768
WithEventFilter(newSecretPredicate(r.SecretKey)).
6869
Build(r)

test/e2e/cluster_extension_install_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ func TestClusterExtensionInstallRegistry(t *testing.T) {
377377
assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
378378
}
379379
}, pollDuration, pollInterval)
380+
381+
// For this case we cannot get the metrics
382+
//FetchCatalogdMetricsExportedEndpoint(t)
383+
//FetchOperatorControllerMetricsExportedEndpoint(t)
380384
})
381385
}
382386
}
@@ -455,6 +459,9 @@ location = "docker-registry.operator-controller-e2e.svc.cluster.local:5000"`,
455459
assert.NotEmpty(ct, clusterExtension.Status.Install.Bundle)
456460
}
457461
}, pollDuration, pollInterval)
462+
463+
FetchCatalogdMetricsExportedEndpoint(t)
464+
FetchOperatorControllerMetricsExportedEndpoint(t)
458465
}
459466

460467
func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) {
@@ -505,6 +512,9 @@ func TestClusterExtensionInstallRegistryMultipleBundles(t *testing.T) {
505512
assert.Contains(ct, cond.Message, "in multiple catalogs with the same priority [extra-test-catalog test-catalog]")
506513
}
507514
}, pollDuration, pollInterval)
515+
516+
FetchCatalogdMetricsExportedEndpoint(t)
517+
FetchOperatorControllerMetricsExportedEndpoint(t)
508518
}
509519

510520
func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) {
@@ -568,6 +578,9 @@ func TestClusterExtensionBlockInstallNonSuccessorVersion(t *testing.T) {
568578
assert.Equal(ct, "error upgrading from currently installed version \"1.0.0\": no bundles found for package \"test\" matching version \"1.2.0\"", cond.Message)
569579
}
570580
}, pollDuration, pollInterval)
581+
582+
FetchCatalogdMetricsExportedEndpoint(t)
583+
FetchOperatorControllerMetricsExportedEndpoint(t)
571584
}
572585

573586
func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) {
@@ -618,6 +631,9 @@ func TestClusterExtensionForceInstallNonSuccessorVersion(t *testing.T) {
618631
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
619632
}
620633
}, pollDuration, pollInterval)
634+
635+
FetchCatalogdMetricsExportedEndpoint(t)
636+
FetchOperatorControllerMetricsExportedEndpoint(t)
621637
}
622638

623639
func TestClusterExtensionInstallSuccessorVersion(t *testing.T) {
@@ -666,6 +682,9 @@ func TestClusterExtensionInstallSuccessorVersion(t *testing.T) {
666682
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
667683
}
668684
}, pollDuration, pollInterval)
685+
686+
FetchCatalogdMetricsExportedEndpoint(t)
687+
FetchOperatorControllerMetricsExportedEndpoint(t)
669688
}
670689

671690
func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) {
@@ -733,6 +752,9 @@ func TestClusterExtensionInstallReResolvesWhenCatalogIsPatched(t *testing.T) {
733752
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
734753
}
735754
}, pollDuration, pollInterval)
755+
756+
FetchCatalogdMetricsExportedEndpoint(t)
757+
FetchOperatorControllerMetricsExportedEndpoint(t)
736758
}
737759

738760
func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) {
@@ -814,6 +836,9 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) {
814836
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
815837
}
816838
}, pollDuration, pollInterval)
839+
840+
FetchCatalogdMetricsExportedEndpoint(t)
841+
FetchOperatorControllerMetricsExportedEndpoint(t)
817842
}
818843

819844
func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T) {
@@ -866,6 +891,9 @@ func TestClusterExtensionInstallReResolvesWhenManagedContentChanged(t *testing.T
866891
require.EventuallyWithT(t, func(ct *assert.CollectT) {
867892
assert.NoError(ct, globalClient.Get(context.Background(), types.NamespacedName{Name: testConfigMap.Name, Namespace: testConfigMap.Namespace}, testConfigMap))
868893
}, pollDuration, pollInterval)
894+
895+
FetchCatalogdMetricsExportedEndpoint(t)
896+
FetchOperatorControllerMetricsExportedEndpoint(t)
869897
}
870898

871899
func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *testing.T) {
@@ -959,4 +987,7 @@ func TestClusterExtensionRecoversFromInitialInstallFailedWhenFailureFixed(t *tes
959987
assert.Equal(ct, ocv1.ReasonSucceeded, cond.Reason)
960988
}
961989
}, pollDuration, pollInterval)
990+
991+
FetchCatalogdMetricsExportedEndpoint(t)
992+
FetchOperatorControllerMetricsExportedEndpoint(t)
962993
}

test/e2e/metrics_test.go

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"bytes"
1818
"context"
1919
"errors"
20+
"fmt"
21+
"os"
2022
"strings"
2123
"testing"
2224
"time"
@@ -36,8 +38,8 @@ import (
3638
"sigs.k8s.io/controller-runtime/pkg/client/config"
3739
)
3840

39-
// TestOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
40-
func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
41+
// FetchOperatorControllerMetricsExportedEndpoint verifies that the metrics endpoint for the operator controller
42+
func FetchOperatorControllerMetricsExportedEndpoint(t *testing.T) {
4143
kubeClient, restConfig := findK8sClient(t)
4244
mtc := NewMetricsTestConfig(
4345
t,
@@ -49,13 +51,14 @@ func TestOperatorControllerMetricsExportedEndpoint(t *testing.T) {
4951
"operator-controller-controller-manager",
5052
"oper-curl-metrics",
5153
"https://operator-controller-service.NAMESPACE.svc.cluster.local:8443/metrics",
54+
"oper-con",
5255
)
5356

5457
mtc.run()
5558
}
5659

57-
// TestCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
58-
func TestCatalogdMetricsExportedEndpoint(t *testing.T) {
60+
// FetchCatalogdMetricsExportedEndpoint verifies that the metrics endpoint for catalogd
61+
func FetchCatalogdMetricsExportedEndpoint(t *testing.T) {
5962
kubeClient, restConfig := findK8sClient(t)
6063
mtc := NewMetricsTestConfig(
6164
t,
@@ -67,11 +70,68 @@ func TestCatalogdMetricsExportedEndpoint(t *testing.T) {
6770
"catalogd-controller-manager",
6871
"catalogd-curl-metrics",
6972
"https://catalogd-service.NAMESPACE.svc.cluster.local:7443/metrics",
73+
"catalogd",
7074
)
7175

7276
mtc.run()
7377
}
7478

79+
// fetchMetrics retrieves Prometheus metrics from the endpoint
80+
func (c *MetricsTestConfig) fetchMetrics(ctx context.Context, token string) string {
81+
c.t.Log("Fetching Prometheus metrics after test execution")
82+
83+
// Command to execute inside the pod
84+
cmd := []string{
85+
"curl", "-s", "-k",
86+
"-H", "Authorization: Bearer " + token,
87+
c.metricsURL,
88+
}
89+
90+
// Execute command in pod
91+
req := c.kubeClient.CoreV1().RESTClient().
92+
Post().
93+
Resource("pods").
94+
Namespace(c.namespace).
95+
Name(c.curlPodName).
96+
SubResource("exec").
97+
VersionedParams(&corev1.PodExecOptions{
98+
Container: "curl",
99+
Command: cmd,
100+
Stdin: false,
101+
Stdout: true,
102+
Stderr: true,
103+
TTY: false,
104+
}, scheme.ParameterCodec)
105+
106+
executor, err := remotecommand.NewSPDYExecutor(c.restConfig, "POST", req.URL())
107+
require.NoError(c.t, err, "Error creating SPDY executor")
108+
109+
var stdout, stderr bytes.Buffer
110+
streamOpts := remotecommand.StreamOptions{
111+
Stdout: &stdout,
112+
Stderr: &stderr,
113+
}
114+
115+
err = executor.StreamWithContext(ctx, streamOpts)
116+
require.NoError(c.t, err, "Error streaming exec request: %v", stderr.String())
117+
118+
return stdout.String()
119+
}
120+
121+
// saveMetricsToFile writes the fetched metrics to a text file
122+
func (c *MetricsTestConfig) saveMetricsToFile(metrics string) {
123+
dir := "results"
124+
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
125+
c.t.Fatalf("Failed to create directory %s: %v", dir, err)
126+
}
127+
128+
filePath := fmt.Sprintf("%s/metrics_%s_%s.txt", dir, c.name, c.t.Name())
129+
err := os.WriteFile(filePath, []byte(metrics), 0644)
130+
require.NoError(c.t, err, "Failed to save metrics to file")
131+
132+
c.t.Logf("Metrics saved to: %s", filePath)
133+
}
134+
75135
func findK8sClient(t *testing.T) (kubernetes.Interface, *rest.Config) {
76136
cfg, err := config.GetConfig()
77137
require.NoError(t, err, "Failed to get Kubernetes config")
@@ -94,6 +154,7 @@ type MetricsTestConfig struct {
94154
serviceAccount string
95155
curlPodName string
96156
metricsURL string
157+
name string
97158
}
98159

99160
// NewMetricsTestConfig initializes a new MetricsTestConfig.
@@ -107,6 +168,7 @@ func NewMetricsTestConfig(
107168
serviceAccount string,
108169
curlPodName string,
109170
metricsURL string,
171+
name string,
110172
) *MetricsTestConfig {
111173
// Discover which namespace the relevant Pod is running in
112174
namespace := getComponentNamespace(t, kubeClient, selector)
@@ -124,24 +186,41 @@ func NewMetricsTestConfig(
124186
serviceAccount: serviceAccount,
125187
curlPodName: curlPodName,
126188
metricsURL: metricsURL,
189+
name: name,
127190
}
128191
}
129192

130193
// run executes the entire test flow
131194
func (c *MetricsTestConfig) run() {
132195
ctx := context.Background()
133-
defer c.cleanup(ctx)
196+
// To speed up
197+
// defer c.cleanup(ctx)
134198
c.createMetricsClusterRoleBinding(ctx)
135199
token := c.getServiceAccountToken(ctx)
136200
c.createCurlMetricsPod(ctx)
137201
c.waitForPodReady(ctx)
138202
// Exec `curl` in the Pod to validate the metrics
139203
c.validateMetricsEndpoint(ctx, token)
204+
205+
// Fetch and save Prometheus metrics after test execution
206+
metrics := c.fetchMetrics(ctx, token)
207+
c.saveMetricsToFile(metrics)
140208
}
141209

142210
// createMetricsClusterRoleBinding to bind the cluster role so metrics are accessible
143211
func (c *MetricsTestConfig) createMetricsClusterRoleBinding(ctx context.Context) {
144-
c.t.Logf("Creating ClusterRoleBinding %q in namespace %q", c.clusterBinding, c.namespace)
212+
c.t.Logf("Ensuring ClusterRoleBinding %q exists in namespace %q", c.clusterBinding, c.namespace)
213+
214+
_, err := c.kubeClient.RbacV1().ClusterRoleBindings().Get(ctx, c.clusterBinding, metav1.GetOptions{})
215+
if err == nil {
216+
c.t.Logf("ClusterRoleBinding %q already exists, skipping creation", c.clusterBinding)
217+
return
218+
}
219+
220+
if !apierrors.IsNotFound(err) {
221+
require.NoError(c.t, err, "Error checking for existing ClusterRoleBinding")
222+
return
223+
}
145224

146225
crb := &rbacv1.ClusterRoleBinding{
147226
ObjectMeta: metav1.ObjectMeta{
@@ -161,8 +240,9 @@ func (c *MetricsTestConfig) createMetricsClusterRoleBinding(ctx context.Context)
161240
},
162241
}
163242

164-
_, err := c.kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, crb, metav1.CreateOptions{})
243+
_, err = c.kubeClient.RbacV1().ClusterRoleBindings().Create(ctx, crb, metav1.CreateOptions{})
165244
require.NoError(c.t, err, "Error creating ClusterRoleBinding")
245+
c.t.Logf("Successfully created ClusterRoleBinding %q", c.clusterBinding)
166246
}
167247

168248
// getServiceAccountToken creates a TokenRequest for the service account
@@ -188,7 +268,18 @@ func (c *MetricsTestConfig) getServiceAccountToken(ctx context.Context) string {
188268

189269
// createCurlMetricsPod spawns a pod running `curlimages/curl` to check metrics
190270
func (c *MetricsTestConfig) createCurlMetricsPod(ctx context.Context) {
191-
c.t.Logf("Creating curl pod (%s/%s) to validate the metrics endpoint", c.namespace, c.curlPodName)
271+
c.t.Logf("Ensuring curl pod (%s/%s) exists to validate the metrics endpoint", c.namespace, c.curlPodName)
272+
273+
_, err := c.kubeClient.CoreV1().Pods(c.namespace).Get(ctx, c.curlPodName, metav1.GetOptions{})
274+
if err == nil {
275+
c.t.Logf("Curl pod %q already exists, skipping creation", c.curlPodName)
276+
return
277+
}
278+
279+
if !apierrors.IsNotFound(err) {
280+
require.NoError(c.t, err, "Error checking for existing curl pod")
281+
return
282+
}
192283

193284
pod := &corev1.Pod{
194285
ObjectMeta: metav1.ObjectMeta{
@@ -220,7 +311,7 @@ func (c *MetricsTestConfig) createCurlMetricsPod(ctx context.Context) {
220311
},
221312
}
222313

223-
_, err := c.kubeClient.CoreV1().Pods(c.namespace).Create(ctx, pod, metav1.CreateOptions{})
314+
_, err = c.kubeClient.CoreV1().Pods(c.namespace).Create(ctx, pod, metav1.CreateOptions{})
224315
require.NoError(c.t, err, "Error creating curl pod")
225316
}
226317

0 commit comments

Comments
 (0)