1
0
Fork 0
mirror of https://github.com/kyverno/policy-reporter.git synced 2024-12-15 17:50:58 +00:00

Add concurrency to metrics and loki client

This commit is contained in:
Frank Jogeleit 2021-02-20 11:00:10 +01:00
parent 438bd923d4
commit 4cfde4347b
6 changed files with 107 additions and 47 deletions

View file

@ -1,3 +1,4 @@
config.yaml config.yaml
build build
README.md README.md
docs

View file

@ -1,8 +1,8 @@
# PolicyReporter # PolicyReporter
PolicyReporter is a simple tool to watch for PolicyReports in your cluster. ## Motivation
It uses this resources to create Prometheus Metrics from it. It also provides a configuration to push rule validation results to Grafana Loki. Kyverno ships with two types of validation. You can either enforce a rule or audit it. If you don't want to block developers or if you want to try out what a new rule you can audit it. The audit configuration creates PolicyReports which you can describe or read with over `kubectl` but it's not that easy to get a good overview. To solve this problem this tool sends informations from PolicyReports to Loki and provide a Metrics endpoint to get metrics about summaries and each rule result.
## Installation with Helm v3 ## Installation with Helm v3
@ -34,3 +34,7 @@ policyPriorities:
![Grafana Loki](https://github.com/fjogeleit/policy-reporter/blob/main/docs/images/grafana-loki.png?raw=true) ![Grafana Loki](https://github.com/fjogeleit/policy-reporter/blob/main/docs/images/grafana-loki.png?raw=true)
![Prometheus Metrics](https://github.com/fjogeleit/policy-reporter/blob/main/docs/images/prometheus.png?raw=true) ![Prometheus Metrics](https://github.com/fjogeleit/policy-reporter/blob/main/docs/images/prometheus.png?raw=true)
# Todos
* Support for ClusterPolicyReports
* Additional Targets

View file

@ -6,7 +6,6 @@ import (
"net/http" "net/http"
"github.com/fjogeleit/policy-reporter/pkg/config" "github.com/fjogeleit/policy-reporter/pkg/config"
"github.com/fjogeleit/policy-reporter/pkg/metrics"
"github.com/fjogeleit/policy-reporter/pkg/report" "github.com/fjogeleit/policy-reporter/pkg/report"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -46,11 +45,16 @@ func NewCLI() *cobra.Command {
if loki != nil { if loki != nil {
go client.WatchRuleValidation(func(r report.Result) { go client.WatchRuleValidation(func(r report.Result) {
loki.Send(r) go loki.Send(r)
}) })
} }
go metrics.GenerateMetrics(client) metrics, err := resolver.Metrics()
if err != nil {
return err
}
go metrics.GenerateMetrics()
http.Handle("/metrics", promhttp.Handler()) http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":2112", nil) http.ListenAndServe(":2112", nil)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 265 KiB

View file

@ -2,13 +2,15 @@ package config
import ( import (
"github.com/fjogeleit/policy-reporter/pkg/kubernetes" "github.com/fjogeleit/policy-reporter/pkg/kubernetes"
"github.com/fjogeleit/policy-reporter/pkg/metrics"
"github.com/fjogeleit/policy-reporter/pkg/target" "github.com/fjogeleit/policy-reporter/pkg/target"
"github.com/fjogeleit/policy-reporter/pkg/target/loki" "github.com/fjogeleit/policy-reporter/pkg/target/loki"
) )
var ( var (
kubeClient kubernetes.Client kubeClient kubernetes.Client
lokiClient target.Client lokiClient target.Client
metricsGenerator *metrics.Metrics
) )
type Resolver struct { type Resolver struct {
@ -35,6 +37,19 @@ func (r *Resolver) LokiClient() target.Client {
return loki.NewClient(r.config.Loki.Host) return loki.NewClient(r.config.Loki.Host)
} }
func (r *Resolver) Metrics() (*metrics.Metrics, error) {
if metricsGenerator != nil {
return metricsGenerator, nil
}
client, err := r.KubernetesClient()
if err != nil {
return nil, err
}
return metrics.NewMetrics(client), nil
}
func NewResolver(config *Config) Resolver { func NewResolver(config *Config) Resolver {
return Resolver{config} return Resolver{config}
} }

View file

@ -1,6 +1,8 @@
package metrics package metrics
import ( import (
"sync"
"github.com/fjogeleit/policy-reporter/pkg/kubernetes" "github.com/fjogeleit/policy-reporter/pkg/kubernetes"
"github.com/fjogeleit/policy-reporter/pkg/report" "github.com/fjogeleit/policy-reporter/pkg/report"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -8,57 +10,83 @@ import (
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
) )
func GenerateMetrics(client kubernetes.Client) { type Metrics struct {
client kubernetes.Client
cache map[string]report.PolicyReport
rwmutex *sync.RWMutex
}
func (m Metrics) getCachedReport(i string) report.PolicyReport {
m.rwmutex.RLock()
defer m.rwmutex.RUnlock()
return m.cache[i]
}
func (m Metrics) cachedReport(r report.PolicyReport) {
m.rwmutex.Lock()
m.cache[r.GetIdentifier()] = r
m.rwmutex.Unlock()
}
func (m Metrics) removeCachedReport(i string) {
m.rwmutex.Lock()
delete(m.cache, i)
m.rwmutex.Unlock()
}
func (m Metrics) GenerateMetrics() {
policyGauge := promauto.NewGaugeVec(prometheus.GaugeOpts{ policyGauge := promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "policy_report", Name: "policy_report_summary",
Help: "Summary of all PolicyReports",
}, []string{"namespace", "name", "status"}) }, []string{"namespace", "name", "status"})
ruleGauge := promauto.NewGaugeVec(prometheus.GaugeOpts{ ruleGauge := promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "rule_validation", Name: "policy_report_result",
Help: "List of all PolicyReport Results",
}, []string{"namespace", "rule", "policy", "kind", "name", "status"}) }, []string{"namespace", "rule", "policy", "kind", "name", "status"})
prometheus.Register(policyGauge) prometheus.Register(policyGauge)
prometheus.Register(ruleGauge) prometheus.Register(ruleGauge)
cache := make(map[string]report.PolicyReport) m.client.WatchPolicyReports(func(e watch.EventType, r report.PolicyReport) {
go func(event watch.EventType, report report.PolicyReport) {
switch event {
case watch.Added:
updatePolicyGauge(policyGauge, report)
client.WatchPolicyReports(func(s watch.EventType, report report.PolicyReport) { for _, rule := range report.Results {
switch s { res := rule.Resources[0]
case watch.Added: ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(1)
updatePolicyGauge(policyGauge, report) }
for _, rule := range report.Results { m.cachedReport(report)
res := rule.Resources[0] case watch.Modified:
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(1) updatePolicyGauge(policyGauge, report)
for _, rule := range m.getCachedReport(report.GetIdentifier()).Results {
res := rule.Resources[0]
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(0)
}
for _, rule := range report.Results {
res := rule.Resources[0]
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(1)
}
case watch.Deleted:
policyGauge.WithLabelValues(report.Namespace, report.Name, "Pass").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Fail").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Warn").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Error").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Skip").Set(0)
for _, rule := range report.Results {
res := rule.Resources[0]
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(0)
}
m.removeCachedReport(report.GetIdentifier())
} }
}(e, r)
cache[report.GetIdentifier()] = report
case watch.Modified:
updatePolicyGauge(policyGauge, report)
for _, rule := range cache[report.GetIdentifier()].Results {
res := rule.Resources[0]
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(0)
}
for _, rule := range report.Results {
res := rule.Resources[0]
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(1)
}
case watch.Deleted:
policyGauge.WithLabelValues(report.Namespace, report.Name, "Pass").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Fail").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Warn").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Error").Set(0)
policyGauge.WithLabelValues(report.Namespace, report.Name, "Skip").Set(0)
for _, rule := range report.Results {
res := rule.Resources[0]
ruleGauge.WithLabelValues(report.Namespace, rule.Rule, rule.Policy, res.Kind, res.Name, rule.Status).Set(0)
}
delete(cache, report.GetIdentifier())
}
}) })
} }
@ -79,3 +107,11 @@ func updatePolicyGauge(policyGauge *prometheus.GaugeVec, report report.PolicyRep
WithLabelValues(report.Namespace, report.Name, "Skip"). WithLabelValues(report.Namespace, report.Name, "Skip").
Set(float64(report.Summary.Skip)) Set(float64(report.Summary.Skip))
} }
func NewMetrics(client kubernetes.Client) *Metrics {
return &Metrics{
client: client,
cache: make(map[string]report.PolicyReport),
rwmutex: new(sync.RWMutex),
}
}