From 833d097c0a298063faed346bdcb4e0e3af86498c Mon Sep 17 00:00:00 2001 From: Yashvardhan Kukreja Date: Sat, 15 May 2021 18:48:38 +0530 Subject: [PATCH] feat: added kyverno_policy_changes_info metric Signed-off-by: Yashvardhan Kukreja --- pkg/metrics/metrics.go | 2 +- pkg/metrics/policychanges/parsers.go | 9 +++ pkg/metrics/policychanges/policyChanges.go | 65 ++++++++++++++++++ pkg/metrics/policychanges/types.go | 15 +++++ pkg/policy/validate_controller.go | 76 ++++++++++++++++++++++ 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 pkg/metrics/policychanges/parsers.go create mode 100644 pkg/metrics/policychanges/policyChanges.go create mode 100644 pkg/metrics/policychanges/types.go diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 09d01108da..cec874d519 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -52,7 +52,7 @@ func NewPromConfig() *PromConfig { } policyChangesMetric := prom.NewGaugeVec( prom.GaugeOpts{ - Name: "kyverno_policy_changes", + Name: "kyverno_policy_changes_info", Help: "can be used to track all the Kyverno policies which have been created, updated or deleted.", }, policyChangesLabels, diff --git a/pkg/metrics/policychanges/parsers.go b/pkg/metrics/policychanges/parsers.go new file mode 100644 index 0000000000..f0b4ac1237 --- /dev/null +++ b/pkg/metrics/policychanges/parsers.go @@ -0,0 +1,9 @@ +package policychanges + +import ( + "github.com/kyverno/kyverno/pkg/metrics" +) + +func ParsePromMetrics(pm metrics.PromMetrics) PromMetrics { + return PromMetrics(pm) +} diff --git a/pkg/metrics/policychanges/policyChanges.go b/pkg/metrics/policychanges/policyChanges.go new file mode 100644 index 0000000000..71f796ed11 --- /dev/null +++ b/pkg/metrics/policychanges/policyChanges.go @@ -0,0 +1,65 @@ +package policychanges + +import ( + "fmt" + kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1" + "github.com/kyverno/kyverno/pkg/metrics" + prom "github.com/prometheus/client_golang/prometheus" + "time" +) + +func (pm PromMetrics) registerPolicyChangesMetric( + policyValidationMode metrics.PolicyValidationMode, + policyType metrics.PolicyType, + policyBackgroundMode metrics.PolicyBackgroundMode, + policyNamespace, policyName string, + policyChangeType PolicyChangeType, + policyChangeTimestamp int64, +) error { + if policyType == metrics.Cluster { + policyNamespace = "-" + } + pm.PolicyChanges.With(prom.Labels{ + "policy_validation_mode": string(policyValidationMode), + "policy_type": string(policyType), + "policy_background_mode": string(policyBackgroundMode), + "policy_namespace": policyNamespace, + "policy_name": policyName, + "policy_change_type": string(policyChangeType), + "timestamp": fmt.Sprintf("%+v", time.Unix(policyChangeTimestamp, 0)), + }).Set(1) + return nil +} + +func (pm PromMetrics) RegisterPolicy(policy interface{}, policyChangeType PolicyChangeType, policyChangeTimestamp int64) error { + switch inputPolicy := policy.(type) { + case *kyverno.ClusterPolicy: + policyValidationMode, err := metrics.ParsePolicyValidationMode(inputPolicy.Spec.ValidationFailureAction) + if err != nil { + return err + } + policyBackgroundMode := metrics.ParsePolicyBackgroundMode(*inputPolicy.Spec.Background) + policyType := metrics.Cluster + policyNamespace := "" // doesn't matter for cluster policy + policyName := inputPolicy.ObjectMeta.Name + if err = pm.registerPolicyChangesMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, policyChangeType, policyChangeTimestamp); err != nil { + return err + } + return nil + case *kyverno.Policy: + policyValidationMode, err := metrics.ParsePolicyValidationMode(inputPolicy.Spec.ValidationFailureAction) + if err != nil { + return err + } + policyBackgroundMode := metrics.ParsePolicyBackgroundMode(*inputPolicy.Spec.Background) + policyType := metrics.Namespaced + policyNamespace := inputPolicy.ObjectMeta.Namespace + policyName := inputPolicy.ObjectMeta.Name + if err = pm.registerPolicyChangesMetric(policyValidationMode, policyType, policyBackgroundMode, policyNamespace, policyName, policyChangeType, policyChangeTimestamp); err != nil { + return err + } + return nil + default: + return fmt.Errorf("wrong input type provided %T. Only kyverno.Policy and kyverno.ClusterPolicy allowed", inputPolicy) + } +} diff --git a/pkg/metrics/policychanges/types.go b/pkg/metrics/policychanges/types.go new file mode 100644 index 0000000000..e58b6b21f0 --- /dev/null +++ b/pkg/metrics/policychanges/types.go @@ -0,0 +1,15 @@ +package policychanges + +import ( + "github.com/kyverno/kyverno/pkg/metrics" +) + +type PolicyChangeType string + +const ( + PolicyCreated PolicyChangeType = "created" + PolicyUpdated PolicyChangeType = "updated" + PolicyDeleted PolicyChangeType = "deleted" +) + +type PromMetrics metrics.PromMetrics diff --git a/pkg/policy/validate_controller.go b/pkg/policy/validate_controller.go index 2025153d06..609ecdce68 100644 --- a/pkg/policy/validate_controller.go +++ b/pkg/policy/validate_controller.go @@ -38,6 +38,8 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" + + policyChangesMetric "github.com/kyverno/kyverno/pkg/metrics/policychanges" ) const ( @@ -224,6 +226,37 @@ func (pc *PolicyController) registerPolicyRuleInfoMetricDeletePolicy(logger logr } } +func (pc *PolicyController) registerPolicyChangesMetricAddPolicy(logger logr.Logger, p *kyverno.ClusterPolicy, policyChangeTimestamp int64) { + err := policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(p, policyChangesMetric.PolicyCreated, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's creation", "name", p.Name) + } +} + +func (pc *PolicyController) registerPolicyChangesMetricUpdatePolicy(logger logr.Logger, oldP, curP *kyverno.ClusterPolicy, policyChangeTimestamp int64) { + if reflect.DeepEqual((*oldP).Spec, (*curP).Spec) { + return + } + err := policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(oldP, policyChangesMetric.PolicyUpdated, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's updation", "name", oldP.Name) + } + // curP will require a new kyverno_policy_changes_info metric if the above update involved change in the following fields: + if curP.Spec.Background != oldP.Spec.Background || curP.Spec.ValidationFailureAction != oldP.Spec.ValidationFailureAction { + err = policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(curP, policyChangesMetric.PolicyUpdated, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's updation", "name", curP.Name) + } + } +} + +func (pc *PolicyController) registerPolicyChangesMetricDeletePolicy(logger logr.Logger, p *kyverno.ClusterPolicy, policyChangeTimestamp int64) { + err := policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(p, policyChangesMetric.PolicyDeleted, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's deletion", "name", p.Name) + } +} + func (pc *PolicyController) addPolicy(obj interface{}) { logger := pc.log p := obj.(*kyverno.ClusterPolicy) @@ -232,6 +265,8 @@ func (pc *PolicyController) addPolicy(obj interface{}) { // register kyverno_policy_rule_info_total metric concurrently go pc.registerPolicyRuleInfoMetricAddPolicy(logger, p) + // register kyverno_policy_changes_info metric concurrently + go pc.registerPolicyChangesMetricAddPolicy(logger, p, time.Now().Unix()) if p.Spec.Background == nil || p.Spec.ValidationFailureAction == "" || missingAutoGenRules(p, logger) { pol, _ := common.MutatePolicy(p, logger) @@ -257,6 +292,8 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) { // register kyverno_policy_rule_info_total metric concurrently go pc.registerPolicyRuleInfoMetricUpdatePolicy(logger, oldP, curP) + // register kyverno_policy_changes_info metric concurrently + go pc.registerPolicyChangesMetricUpdatePolicy(logger, oldP, curP, time.Now().Unix()) if curP.Spec.Background == nil || curP.Spec.ValidationFailureAction == "" || missingAutoGenRules(curP, logger) { pol, _ := common.MutatePolicy(curP, logger) @@ -301,6 +338,8 @@ func (pc *PolicyController) deletePolicy(obj interface{}) { // register kyverno_policy_rule_info_total metric concurrently go pc.registerPolicyRuleInfoMetricDeletePolicy(logger, p) + // register kyverno_policy_changes_info metric concurrently + go pc.registerPolicyChangesMetricDeletePolicy(logger, p, time.Now().Unix()) logger.Info("policy deleted", "uid", p.UID, "kind", "ClusterPolicy", "name", p.Name) @@ -337,12 +376,45 @@ func (pc *PolicyController) registerPolicyRuleInfoMetricDeleteNsPolicy(logger lo } } +func (pc *PolicyController) registerPolicyChangesMetricAddNsPolicy(logger logr.Logger, p *kyverno.Policy, policyChangeTimestamp int64) { + err := policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(p, policyChangesMetric.PolicyCreated, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's creation", "name", p.Name) + } +} + +func (pc *PolicyController) registerPolicyChangesMetricUpdateNsPolicy(logger logr.Logger, oldP, curP *kyverno.Policy, policyChangeTimestamp int64) { + if reflect.DeepEqual((*oldP).Spec, (*curP).Spec) { + return + } + err := policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(oldP, policyChangesMetric.PolicyUpdated, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's updation", "name", oldP.Name) + } + // curP will require a new kyverno_policy_changes_info metric if the above update involved change in the following fields: + if curP.Spec.Background != oldP.Spec.Background || curP.Spec.ValidationFailureAction != oldP.Spec.ValidationFailureAction { + err = policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(curP, policyChangesMetric.PolicyUpdated, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's updation", "name", curP.Name) + } + } +} + +func (pc *PolicyController) registerPolicyChangesMetricDeleteNsPolicy(logger logr.Logger, p *kyverno.Policy, policyChangeTimestamp int64) { + err := policyChangesMetric.ParsePromMetrics(*pc.promConfig.Metrics).RegisterPolicy(p, policyChangesMetric.PolicyDeleted, policyChangeTimestamp) + if err != nil { + logger.Error(err, "error occurred while registering kyverno_policy_changes_info metrics for the above policy's deletion", "name", p.Name) + } +} + func (pc *PolicyController) addNsPolicy(obj interface{}) { logger := pc.log p := obj.(*kyverno.Policy) // register kyverno_policy_rule_info_total metric concurrently go pc.registerPolicyRuleInfoMetricAddNsPolicy(logger, p) + // register kyverno_policy_changes_info metric concurrently + go pc.registerPolicyChangesMetricAddNsPolicy(logger, p, time.Now().Unix()) logger.Info("policy created", "uid", p.UID, "kind", "Policy", "name", p.Name, "namespaces", p.Namespace) @@ -369,6 +441,8 @@ func (pc *PolicyController) updateNsPolicy(old, cur interface{}) { // register kyverno_policy_rule_info_total metric concurrently go pc.registerPolicyRuleInfoMetricUpdateNsPolicy(logger, oldP, curP) + // register kyverno_policy_changes_info metric concurrently + go pc.registerPolicyChangesMetricUpdateNsPolicy(logger, oldP, curP, time.Now().Unix()) ncurP := ConvertPolicyToClusterPolicy(curP) @@ -414,6 +488,8 @@ func (pc *PolicyController) deleteNsPolicy(obj interface{}) { // register kyverno_policy_rule_info_total metric concurrently go pc.registerPolicyRuleInfoMetricDeleteNsPolicy(logger, p) + // register kyverno_policy_changes_info metric concurrently + go pc.registerPolicyChangesMetricDeleteNsPolicy(logger, p, time.Now().Unix()) logger.Info("policy deleted event", "uid", p.UID, "kind", "Policy", "policy_name", p.Name, "namespaces", p.Namespace)