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:
parent
86c805845f
commit
86c2d7f844
12 changed files with 166 additions and 33 deletions
2
go.mod
2
go.mod
|
@ -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
4
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
32
pkg/cache/cache.go
vendored
Normal 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),
|
||||
}
|
||||
}
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
43
pkg/redis/cache.go
Normal 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}
|
||||
}
|
Loading…
Reference in a new issue