open-cluster-management.io/governance-policy-propagator@v0.13.0/controllers/policymetrics/policymetrics_controller.go (about) 1 // Copyright (c) 2021 Red Hat, Inc. 2 // Copyright Contributors to the Open Cluster Management project 3 4 package policymetrics 5 6 import ( 7 "context" 8 "strings" 9 10 "github.com/prometheus/client_golang/prometheus" 11 "k8s.io/apimachinery/pkg/api/errors" 12 "k8s.io/apimachinery/pkg/runtime" 13 ctrl "sigs.k8s.io/controller-runtime" 14 "sigs.k8s.io/controller-runtime/pkg/client" 15 "sigs.k8s.io/controller-runtime/pkg/controller" 16 "sigs.k8s.io/controller-runtime/pkg/reconcile" 17 18 policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1" 19 "open-cluster-management.io/governance-policy-propagator/controllers/common" 20 ) 21 22 const ControllerName string = "policy-metrics" 23 24 var log = ctrl.Log.WithName(ControllerName) 25 26 // SetupWithManager sets up the controller with the Manager. 27 func (r *MetricReconciler) SetupWithManager(mgr ctrl.Manager, maxConcurrentReconciles uint) error { 28 return ctrl.NewControllerManagedBy(mgr). 29 // The work queue prevents the same item being reconciled concurrently: 30 // https://github.com/kubernetes-sigs/controller-runtime/issues/1416#issuecomment-899833144 31 WithOptions(controller.Options{MaxConcurrentReconciles: int(maxConcurrentReconciles)}). 32 Named(ControllerName). 33 For(&policiesv1.Policy{}). 34 Complete(r) 35 } 36 37 // blank assignment to verify that ReconcilePolicy implements reconcile.Reconciler 38 var _ reconcile.Reconciler = &MetricReconciler{} 39 40 // MetricReconciler reconciles the metrics for the Policy 41 type MetricReconciler struct { 42 client.Client 43 Scheme *runtime.Scheme 44 } 45 46 //+kubebuilder:rbac:groups=policy.open-cluster-management.io,resources=policies,verbs=get;list;watch;create;update;patch;delete 47 //+kubebuilder:rbac:groups=policy.open-cluster-management.io,resources=policies/status,verbs=get;update;patch 48 //+kubebuilder:rbac:groups=policy.open-cluster-management.io,resources=policies/finalizers,verbs=update 49 50 // Reconcile reads the state of the cluster for the Policy object and ensures that the exported 51 // policy metrics are accurate, updating them as necessary. 52 func (r *MetricReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) { 53 log := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) 54 log.Info("Reconciling metric for the policy") 55 56 // Need to know if the policy is a root policy to create the correct prometheus labels 57 // Can't try to use a label on the policy, because the policy might have been deleted. 58 inClusterNs, err := common.IsInClusterNamespace(ctx, r.Client, request.Namespace) 59 if err != nil { 60 log.Error(err, "Failed to determine if the policy is a replicated policy") 61 62 return reconcile.Result{}, err 63 } 64 65 var promLabels map[string]string 66 67 if inClusterNs { 68 // propagated policies should look like <namespace>.<name> 69 // also note: k8s namespace names follow RFC 1123 (so no "." in it) 70 splitName := strings.SplitN(request.Name, ".", 2) 71 if len(splitName) < 2 { 72 // Don't do any metrics if the policy is invalid. 73 log.Info("Invalid policy in cluster namespace: missing root policy ns prefix") 74 75 return reconcile.Result{}, nil 76 } 77 78 promLabels = prometheus.Labels{ 79 "type": "propagated", 80 "policy": splitName[1], 81 "policy_namespace": splitName[0], 82 "cluster_namespace": request.Namespace, 83 } 84 } else { 85 promLabels = prometheus.Labels{ 86 "type": "root", 87 "policy": request.Name, 88 "policy_namespace": request.Namespace, 89 "cluster_namespace": "<null>", // this is basically a sentinel value 90 } 91 } 92 93 pol := &policiesv1.Policy{} 94 95 err = r.Get(ctx, request.NamespacedName, pol) 96 if err != nil { 97 if errors.IsNotFound(err) { 98 // Try to delete the gauge, but don't get hung up on errors. Log whether it was deleted. 99 statusGaugeDeleted := policyStatusGauge.Delete(promLabels) 100 log.Info("Policy not found. It must have been deleted.", "status-gauge-deleted", statusGaugeDeleted) 101 102 return reconcile.Result{}, nil 103 } 104 105 log.Error(err, "Failed to get Policy") 106 107 return reconcile.Result{}, err 108 } 109 110 log.V(2).Info("Got active state", "pol.Spec.Disabled", pol.Spec.Disabled) 111 112 if pol.Spec.Disabled { 113 // The policy is no longer active, so delete its metric 114 statusGaugeDeleted := policyStatusGauge.Delete(promLabels) 115 log.V(1).Info("Metric removed for non-active policy", "status-gauge-deleted", statusGaugeDeleted) 116 117 return reconcile.Result{}, nil 118 } 119 120 log.V(2).Info("Got ComplianceState", "pol.Status.ComplianceState", pol.Status.ComplianceState) 121 122 statusMetric, err := policyStatusGauge.GetMetricWith(promLabels) 123 if err != nil { 124 log.Error(err, "Failed to get status metric from GaugeVec") 125 126 return reconcile.Result{}, err 127 } 128 129 if pol.Status.ComplianceState == policiesv1.Compliant { 130 statusMetric.Set(0) 131 } else if pol.Status.ComplianceState == policiesv1.NonCompliant { 132 statusMetric.Set(1) 133 } 134 135 return reconcile.Result{}, nil 136 }