1
0
Fork 0
mirror of https://github.com/kyverno/policy-reporter.git synced 2024-12-14 11:57:32 +00:00

Add optional external cache for new results

Signed-off-by: Frank Jogeleit <frank.jogeleit@web.de>
This commit is contained in:
Frank Jogeleit 2022-05-12 11:02:09 +02:00
parent 86c805845f
commit 86c2d7f844
12 changed files with 166 additions and 33 deletions

2
go.mod
View file

@ -20,9 +20,11 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v1.2.2 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.7 // indirect

4
go.sum
View file

@ -112,6 +112,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@ -157,6 +159,8 @@ github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=

View file

@ -93,7 +93,7 @@ spec:
automountServiceAccountToken: false
containers:
- name: ui
image: "ghcr.io/kyverno/policy-reporter-ui:1.3.3"
image: "ghcr.io/kyverno/policy-reporter-ui:1.3.4"
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
@ -145,7 +145,7 @@ spec:
fsGroup: 1234
containers:
- name: policy-reporter
image: "ghcr.io/kyverno/policy-reporter:2.5.0"
image: "ghcr.io/kyverno/policy-reporter:2.5.1"
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false

View file

@ -204,7 +204,7 @@ spec:
spec:
containers:
- name: ui
image: "ghcr.io/kyverno/policy-reporter-ui:1.3.3"
image: "ghcr.io/kyverno/policy-reporter-ui:4"
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
@ -256,7 +256,7 @@ spec:
fsGroup: 1234
containers:
- name: policy-reporter
image: "ghcr.io/kyverno/policy-reporter:2.5.0"
image: "ghcr.io/kyverno/policy-reporter:2.5.1"
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false

View file

@ -84,7 +84,7 @@ spec:
automountServiceAccountToken: true
containers:
- name: policy-reporter
image: "ghcr.io/kyverno/policy-reporter:2.5.0"
image: "ghcr.io/kyverno/policy-reporter:2.5.1"
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false

32
pkg/cache/cache.go vendored Normal file
View file

@ -0,0 +1,32 @@
package cache
import (
"time"
gocache "github.com/patrickmn/go-cache"
)
type Cache interface {
Has(id string) bool
Add(id string)
}
type InMemoryCache struct {
cache *gocache.Cache
}
func (c *InMemoryCache) Has(id string) bool {
_, ok := c.cache.Get(id)
return ok
}
func (c *InMemoryCache) Add(id string) {
c.cache.SetDefault(id, true)
}
func New(defaultExpiration, cleanupInterval time.Duration) *InMemoryCache {
return &InMemoryCache{
cache: gocache.New(defaultExpiration, cleanupInterval),
}
}

View file

@ -122,17 +122,30 @@ type Metrics struct {
Enabled bool `mapstructure:"enabled"`
}
// PriorityMap configuration
type PriorityMap = map[string]string
// ClusterReportFilter configuration
type ClusterReportFilter struct {
Disabled bool `mapstructure:"disabled"`
}
// ReportFilter configuration
type ReportFilter struct {
Namespaces NamespaceFilter `mapstructure:"namespaces"`
ClusterReports ClusterReportFilter `mapstructure:"clusterReports"`
}
// Redis configuration
type Redis struct {
Enabled bool `mapstructure:"enabled"`
Address string `mapstructure:"address"`
Prefix string `mapstructure:"prefix"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Database int `mapstructure:"database"`
}
// Config of the PolicyReporter
type Config struct {
Loki Loki `mapstructure:"loki"`
@ -150,4 +163,5 @@ type Config struct {
REST REST `mapstructure:"rest"`
PriorityMap PriorityMap `mapstructure:"priorityMap"`
ReportFilter ReportFilter `mapstructure:"reportFilter"`
Redis Redis `mapstructure:"redis"`
}

View file

@ -8,9 +8,11 @@ import (
"time"
"github.com/kyverno/policy-reporter/pkg/api"
"github.com/kyverno/policy-reporter/pkg/cache"
"github.com/kyverno/policy-reporter/pkg/helper"
"github.com/kyverno/policy-reporter/pkg/kubernetes"
"github.com/kyverno/policy-reporter/pkg/listener"
"github.com/kyverno/policy-reporter/pkg/redis"
"github.com/kyverno/policy-reporter/pkg/report"
"github.com/kyverno/policy-reporter/pkg/sqlite3"
"github.com/kyverno/policy-reporter/pkg/target"
@ -23,10 +25,9 @@ import (
"github.com/kyverno/policy-reporter/pkg/target/ui"
"github.com/kyverno/policy-reporter/pkg/target/webhook"
"github.com/patrickmn/go-cache"
"k8s.io/client-go/dynamic"
goredis "github.com/go-redis/redis/v8"
_ "github.com/mattn/go-sqlite3"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
@ -39,7 +40,7 @@ type Resolver struct {
policyStore sqlite3.PolicyReportStore
policyReportClient report.PolicyReportClient
targetClients []target.Client
resultCache *cache.Cache
resultCache cache.Cache
}
// APIServer resolver method
@ -303,11 +304,25 @@ func (r *Resolver) ReportFilter() report.Filter {
}
// ResultCache resolver method
func (r *Resolver) ResultCache() *cache.Cache {
func (r *Resolver) ResultCache() cache.Cache {
if r.resultCache != nil {
return r.resultCache
}
r.resultCache = cache.New(time.Minute*150, time.Minute*15)
if r.config.Redis.Enabled {
r.resultCache = redis.New(
r.config.Redis.Prefix,
goredis.NewClient(&goredis.Options{
Addr: r.config.Redis.Address,
Username: r.config.Redis.Username,
Password: r.config.Redis.Password,
DB: r.config.Redis.Database,
}),
2*time.Hour,
)
} else {
r.resultCache = cache.New(time.Minute*150, time.Minute*15)
}
return r.resultCache
}

View file

@ -3,7 +3,9 @@ package config_test
import (
"testing"
"github.com/kyverno/policy-reporter/pkg/cache"
"github.com/kyverno/policy-reporter/pkg/config"
"github.com/kyverno/policy-reporter/pkg/redis"
"github.com/kyverno/policy-reporter/pkg/report"
"k8s.io/client-go/rest"
)
@ -356,17 +358,38 @@ func Test_ResolveAPIServer(t *testing.T) {
}
func Test_ResolveCache(t *testing.T) {
resolver := config.NewResolver(testConfig, &rest.Config{})
t.Run("InMemory", func(t *testing.T) {
resolver := config.NewResolver(testConfig, &rest.Config{})
_, ok := resolver.ResultCache().(*cache.InMemoryCache)
if !ok {
t.Error("Expected Cache to be InMemory Cache")
}
cache1 := resolver.ResultCache()
if cache1 == nil {
t.Error("Error: Should return ResultCache")
}
cache1 := resolver.ResultCache()
if cache1 == nil {
t.Error("Error: Should return ResultCache")
}
cache2 := resolver.ResultCache()
if cache1 != cache2 {
t.Error("A second call resolver.ResultCache() should return the cached first cache")
}
cache2 := resolver.ResultCache()
if cache1 != cache2 {
t.Error("A second call resolver.ResultCache() should return the cached first cache")
}
})
t.Run("Redis", func(t *testing.T) {
var redisConfig = &config.Config{
Redis: config.Redis{
Enabled: true,
Address: "localhost:6379",
},
}
resolver := config.NewResolver(redisConfig, &rest.Config{})
_, ok := resolver.ResultCache().(*redis.RedisCache)
if !ok {
t.Error("Expected Cache to be Redis Cache")
}
})
}
func Test_ResolveMapper(t *testing.T) {

View file

@ -4,14 +4,14 @@ import (
"sync"
"time"
"github.com/kyverno/policy-reporter/pkg/cache"
"github.com/kyverno/policy-reporter/pkg/report"
"github.com/patrickmn/go-cache"
)
type ResultListener struct {
skipExisting bool
listener []report.PolicyReportResultListener
cache *cache.Cache
cache cache.Cache
startUp time.Time
}
@ -22,7 +22,7 @@ func (l *ResultListener) RegisterListener(listener report.PolicyReportResultList
func (l *ResultListener) Listen(event report.LifecycleEvent) {
if len(event.OldPolicyReport.Results) > 0 {
for id := range event.OldPolicyReport.Results {
l.cache.SetDefault(id, true)
l.cache.Add(id)
}
}
@ -49,7 +49,7 @@ func (l *ResultListener) Listen(event report.LifecycleEvent) {
wg := sync.WaitGroup{}
for _, r := range diff {
if _, found := l.cache.Get(r.GetIdentifier()); found {
if found := l.cache.Has(r.GetIdentifier()); found {
continue
}
@ -66,7 +66,7 @@ func (l *ResultListener) Listen(event report.LifecycleEvent) {
wg.Wait()
}
func NewResultListener(skipExisting bool, rcache *cache.Cache, startUp time.Time) *ResultListener {
func NewResultListener(skipExisting bool, rcache cache.Cache, startUp time.Time) *ResultListener {
return &ResultListener{
skipExisting: skipExisting,
cache: rcache,

View file

@ -4,16 +4,16 @@ import (
"testing"
"time"
"github.com/kyverno/policy-reporter/pkg/cache"
"github.com/kyverno/policy-reporter/pkg/listener"
"github.com/kyverno/policy-reporter/pkg/report"
"github.com/patrickmn/go-cache"
)
func Test_ResultListener(t *testing.T) {
t.Run("Publish Result", func(t *testing.T) {
var called *report.Result
slistener := listener.NewResultListener(true, cache.New(cache.DefaultExpiration, 5*time.Minute), time.Now())
slistener := listener.NewResultListener(true, cache.New(0, 5*time.Minute), time.Now())
slistener.RegisterListener(func(r *report.Result, b bool) {
called = r
})
@ -28,7 +28,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Ignore Delete Event", func(t *testing.T) {
var called bool
slistener := listener.NewResultListener(true, cache.New(cache.DefaultExpiration, 5*time.Minute), time.Now())
slistener := listener.NewResultListener(true, cache.New(0, 5*time.Minute), time.Now())
slistener.RegisterListener(func(r *report.Result, b bool) {
called = true
})
@ -43,7 +43,7 @@ func Test_ResultListener(t *testing.T) {
t.Run("Ignore Added Results created before startup", func(t *testing.T) {
var called bool
slistener := listener.NewResultListener(true, cache.New(cache.DefaultExpiration, 5*time.Minute), time.Now())
slistener := listener.NewResultListener(true, cache.New(0, 5*time.Minute), time.Now())
slistener.RegisterListener(func(r *report.Result, b bool) {
called = true
})
@ -58,8 +58,8 @@ func Test_ResultListener(t *testing.T) {
t.Run("Ignore CacheResults", func(t *testing.T) {
var called bool
rcache := cache.New(cache.DefaultExpiration, 5*time.Minute)
rcache.SetDefault(result2.ID, true)
rcache := cache.New(0, 5*time.Minute)
rcache.Add(result2.ID)
slistener := listener.NewResultListener(true, rcache, time.Now())
slistener.RegisterListener(func(r *report.Result, b bool) {
@ -76,8 +76,8 @@ func Test_ResultListener(t *testing.T) {
t.Run("Early Return if Rsults are empty", func(t *testing.T) {
var called bool
rcache := cache.New(cache.DefaultExpiration, 5*time.Minute)
rcache.SetDefault(result2.ID, true)
rcache := cache.New(0, 5*time.Minute)
rcache.Add(result2.ID)
slistener := listener.NewResultListener(true, rcache, time.Now())
slistener.RegisterListener(func(r *report.Result, b bool) {

43
pkg/redis/cache.go Normal file
View file

@ -0,0 +1,43 @@
package redis
import (
"context"
"fmt"
"log"
"time"
goredis "github.com/go-redis/redis/v8"
)
type RedisCache struct {
rdb *goredis.Client
prefix string
ttl time.Duration
}
func (r *RedisCache) Add(id string) {
err := r.rdb.Set(context.Background(), r.generateKey(id), true, r.ttl).Err()
if err != nil {
log.Printf("[ERROR] Failed to set result: %s\n", err)
}
}
func (r *RedisCache) Has(id string) bool {
_, err := r.rdb.Get(context.Background(), r.generateKey(id)).Result()
if err == goredis.Nil {
return false
} else if err != nil {
log.Printf("[ERROR] Failed to get result: %s\n", err)
return false
}
return true
}
func (r *RedisCache) generateKey(id string) string {
return fmt.Sprintf("%s:%s", r.prefix, id)
}
func New(prefix string, rdb *goredis.Client, ttl time.Duration) *RedisCache {
return &RedisCache{rdb: rdb, prefix: prefix, ttl: ttl}
}