From 0dc2909735e20986ac4a20d28f51e640be86370a Mon Sep 17 00:00:00 2001 From: Kim Tsao Date: Mon, 25 Sep 2023 12:07:31 -0400 Subject: [PATCH] Component metrics Signed-off-by: Kim Tsao --- controllers/component_controller.go | 6 +++++ .../component_controller_finalizer_test.go | 14 +++++++++++ pkg/metrics/metrics.go | 24 ++++++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/controllers/component_controller.go b/controllers/component_controller.go index 46ea10e91..b5a78b050 100644 --- a/controllers/component_controller.go +++ b/controllers/component_controller.go @@ -182,6 +182,7 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if hasApplication.Status.Devfile != "" && len(component.Status.Conditions) > 0 && component.Status.Conditions[len(component.Status.Conditions)-1].Status == metav1.ConditionTrue && containsString(component.GetFinalizers(), compFinalizerName) { // only attempt to finalize and update the gitops repo if an Application is present & the previous Component status is good // A finalizer is present for the Component CR, so make sure we do the necessary cleanup steps + metrics.ComponentDeletionTotalReqs.Inc() if err := r.Finalize(ctx, &component, &hasApplication, ghClient, gitToken); err != nil { if errors.IsConflict(err) { //conflict means we just retry, we are updating the shared application so conflicts are not unexpected @@ -190,6 +191,9 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // if fail to delete the external dependency here, log the error, but don't return error // Don't want to get stuck in a cycle of repeatedly trying to update the repository and failing log.Error(err, "Unable to update GitOps repository for component %v in namespace %v", component.GetName(), component.GetNamespace()) + + // Increment the Component deletion failed metric as the component delete did not fully succeed + metrics.ComponentDeletionFailed.Inc() } } @@ -199,6 +203,8 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( controllerutil.RemoveFinalizer(&component, compFinalizerName) if err := r.Update(ctx, &component); err != nil { return ctrl.Result{}, err + } else { + metrics.ComponentDeletionSucceeded.Inc() } } } diff --git a/controllers/component_controller_finalizer_test.go b/controllers/component_controller_finalizer_test.go index fa297216b..a57f57901 100644 --- a/controllers/component_controller_finalizer_test.go +++ b/controllers/component_controller_finalizer_test.go @@ -19,6 +19,10 @@ import ( "context" "strings" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/redhat-appstudio/application-service/pkg/metrics" + "github.com/devfile/library/v2/pkg/devfile/parser" . "github.com/onsi/ginkgo" @@ -43,11 +47,17 @@ var _ = Describe("Application controller finalizer counter tests", func() { SampleRepoLink = "https://github.com/devfile-samples/devfile-sample-java-springboot-basic" ) + prometheus.MustRegister(metrics.ComponentDeletionTotalReqs, metrics.ComponentDeletionFailed, metrics.ComponentDeletionSucceeded) + Context("Delete Component CR with an invalid Application devfile", func() { It("Should delete successfully even when finalizer fails", func() { applicationName := AppName + "1" componentName := CompName + "1" + beforeDeleteTotalReqs := testutil.ToFloat64(metrics.ComponentDeletionTotalReqs) + beforeDeleteSucceedReqs := testutil.ToFloat64(metrics.ComponentDeletionSucceeded) + beforeDeleteFailedReqs := testutil.ToFloat64(metrics.ComponentDeletionFailed) + // Create a simple Application CR and get its devfile createAndFetchSimpleApp(applicationName, AppNamespace, DisplayName, Description) @@ -127,6 +137,10 @@ var _ = Describe("Application controller finalizer counter tests", func() { f := &appstudiov1alpha1.Application{} return k8sClient.Get(context.Background(), hasAppLookupKey, f) }, timeout, interval).ShouldNot(Succeed()) + + Expect(testutil.ToFloat64(metrics.ComponentDeletionTotalReqs) > beforeDeleteTotalReqs).To(BeTrue()) + Expect(testutil.ToFloat64(metrics.ComponentDeletionSucceeded) > beforeDeleteSucceedReqs).To(BeTrue()) + Expect(testutil.ToFloat64(metrics.ComponentDeletionFailed) > beforeDeleteFailedReqs).To(BeTrue()) }) }) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index cffb2c63e..a5494d7f0 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -96,11 +96,33 @@ var ( Help: "Number of successful application deletion requests", }, ) + + ComponentDeletionTotalReqs = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "has_component_deletion_total", + Help: "Number of component deletion requests processed", + }, + ) + ComponentDeletionFailed = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "has_component_failed_deletion_total", + Help: "Number of failed component deletion requests", + }, + ) + + ComponentDeletionSucceeded = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "has_component_successful_deletion_total", + Help: "Number of successful component deletion requests", + }, + ) ) func init() { // Register custom metrics with the global prometheus registry - metrics.Registry.MustRegister(GitOpsRepoCreationTotalReqs, GitOpsRepoCreationFailed, GitOpsRepoCreationSucceeded, ControllerGitRequest, SecondaryRateLimitCounter, PrimaryRateLimitCounter, TokenPoolGauge, ApplicationDeletionTotalReqs, ApplicationDeletionSucceeded, ApplicationDeletionFailed) + metrics.Registry.MustRegister(GitOpsRepoCreationTotalReqs, GitOpsRepoCreationFailed, GitOpsRepoCreationSucceeded, ControllerGitRequest, SecondaryRateLimitCounter, + PrimaryRateLimitCounter, TokenPoolGauge, ApplicationDeletionTotalReqs, ApplicationDeletionSucceeded, ApplicationDeletionFailed, + ComponentDeletionTotalReqs, ComponentDeletionSucceeded, ComponentDeletionFailed) } // HandleRateLimitMetrics checks the error type to verify a primary or secondary rate limit has been encountered