From 82e78f40feae3d7cb2b0c090013e73ede8662400 Mon Sep 17 00:00:00 2001 From: bharathi-tenneti Date: Wed, 15 Jul 2020 10:53:42 -0400 Subject: [PATCH] *Added new handler for metrics *Added changelog fragment --- .../fragments/enqueue-object-handler.yml | 7 ++ pkg/ansible/controller/controller.go | 12 +-- pkg/handler/instrumented_enqueue_object.go | 81 +++++++++++++++++++ pkg/handler/internal/metrics/metrics.go | 33 ++++++++ pkg/helm/controller/controller.go | 2 +- 5 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 changelog/fragments/enqueue-object-handler.yml create mode 100644 pkg/handler/instrumented_enqueue_object.go create mode 100644 pkg/handler/internal/metrics/metrics.go diff --git a/changelog/fragments/enqueue-object-handler.yml b/changelog/fragments/enqueue-object-handler.yml new file mode 100644 index 0000000000..80bc279a1b --- /dev/null +++ b/changelog/fragments/enqueue-object-handler.yml @@ -0,0 +1,7 @@ +entries: + - description: > + Add new handler for metrics which can be registered with controller-runtime registry, + and wraps existing EnqueueResuestForObject, for both Ansible and Helm controllers. + + kind: "addition" + breaking: false \ No newline at end of file diff --git a/pkg/ansible/controller/controller.go b/pkg/ansible/controller/controller.go index 9cd357797e..9991fa54f2 100644 --- a/pkg/ansible/controller/controller.go +++ b/pkg/ansible/controller/controller.go @@ -20,19 +20,19 @@ import ( "strings" "time" - "github.com/operator-framework/operator-sdk/pkg/ansible/events" - "github.com/operator-framework/operator-sdk/pkg/ansible/runner" - "github.com/operator-framework/operator-sdk/pkg/predicate" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/controller" - crthandler "sigs.k8s.io/controller-runtime/pkg/handler" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/operator-framework/operator-sdk/pkg/ansible/events" + "github.com/operator-framework/operator-sdk/pkg/ansible/runner" + handler "github.com/operator-framework/operator-sdk/pkg/handler" + "github.com/operator-framework/operator-sdk/pkg/predicate" ) var log = logf.Log.WithName("ansible-controller") @@ -106,7 +106,7 @@ func Add(mgr manager.Manager, options Options) *controller.Controller { os.Exit(1) } - if err := c.Watch(&source.Kind{Type: u}, &crthandler.EnqueueRequestForObject{}, + if err := c.Watch(&source.Kind{Type: u}, &handler.InstrumentedEnqueueRequestForObject{}, predicate.GenerationChangedPredicate{}, filterPredicate); err != nil { log.Error(err, "") os.Exit(1) diff --git a/pkg/handler/instrumented_enqueue_object.go b/pkg/handler/instrumented_enqueue_object.go new file mode 100644 index 0000000000..ae499d66c2 --- /dev/null +++ b/pkg/handler/instrumented_enqueue_object.go @@ -0,0 +1,81 @@ +// Copyright 2020 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handler + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/util/workqueue" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + + "github.com/operator-framework/operator-sdk/pkg/handler/internal/metrics" +) + +// InstrumentedEnqueueRequestForObject wraps controller-runtime handler for "EnqueueRequestForObject", and +// sets up primary resource metrics on event handlers. The main objective of this handler is to set +// prometheues metrics when create/update/delete events occur. These metrics contain below information on resource. +// +// resource_created_at_seconds{"name", "namespace", "group", "version", "kind"} +// +// '&handler.InstrumentedEnqueueRequestForObject{}' is used to call the handler. +type InstrumentedEnqueueRequestForObject struct { + handler.EnqueueRequestForObject +} + +// Create implements EventHandler, and creates the metrics. +func (h InstrumentedEnqueueRequestForObject) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) { + setResourceMetric(e.Meta, e.Object) + h.EnqueueRequestForObject.Create(e, q) +} + +// Update implements EventHandler, and updates the metrics. +func (h InstrumentedEnqueueRequestForObject) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) { + setResourceMetric(e.MetaOld, e.ObjectOld) + setResourceMetric(e.MetaNew, e.ObjectNew) + + h.EnqueueRequestForObject.Update(e, q) +} + +// Delete implements EventHandler, and deletes metrics. +func (h InstrumentedEnqueueRequestForObject) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) { + deleteResourceMetric(e.Meta, e.Object) + h.EnqueueRequestForObject.Delete(e, q) +} + +func setResourceMetric(metadata metav1.Object, obj runtime.Object) { + if metadata != nil && obj != nil { + labels := getResourceLabels(metadata, obj) + m, _ := metrics.ResourceCreatedAt.GetMetricWith(labels) + m.Set(float64(metadata.GetCreationTimestamp().UTC().Unix())) + } +} + +func deleteResourceMetric(metadata metav1.Object, obj runtime.Object) { + if metadata != nil && obj != nil { + labels := getResourceLabels(metadata, obj) + _ = metrics.ResourceCreatedAt.Delete(labels) + } +} + +func getResourceLabels(metadata metav1.Object, obj runtime.Object) map[string]string { + return map[string]string{ + "name": metadata.GetName(), + "namespace": metadata.GetNamespace(), + "group": obj.GetObjectKind().GroupVersionKind().Group, + "version": obj.GetObjectKind().GroupVersionKind().Version, + "kind": obj.GetObjectKind().GroupVersionKind().Kind, + } +} diff --git a/pkg/handler/internal/metrics/metrics.go b/pkg/handler/internal/metrics/metrics.go new file mode 100644 index 0000000000..07d19ea8aa --- /dev/null +++ b/pkg/handler/internal/metrics/metrics.go @@ -0,0 +1,33 @@ +// Copyright 2020 The Operator-SDK Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "sigs.k8s.io/controller-runtime/pkg/metrics" +) + +// ResourceCreatedAt creates new prometheus metrics for primary resource, +// with information {"name", "namespace", "group", "version", "kind"} +var ResourceCreatedAt = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "resource_created_at_seconds", + Help: "Timestamp at which a resource was created", +}, []string{"name", "namespace", "group", "version", "kind"}) + +func init() { + metrics.Registry.MustRegister( + ResourceCreatedAt, + ) +} diff --git a/pkg/helm/controller/controller.go b/pkg/helm/controller/controller.go index f8710f1924..c6d551840d 100644 --- a/pkg/helm/controller/controller.go +++ b/pkg/helm/controller/controller.go @@ -79,7 +79,7 @@ func Add(mgr manager.Manager, options WatchOptions) error { o := &unstructured.Unstructured{} o.SetGroupVersionKind(options.GVK) - if err := c.Watch(&source.Kind{Type: o}, &crthandler.EnqueueRequestForObject{}); err != nil { + if err := c.Watch(&source.Kind{Type: o}, &handler.InstrumentedEnqueueRequestForObject{}); err != nil { return err }