mirror of
https://github.com/kyverno/policy-reporter.git
synced 2024-12-14 11:57:32 +00:00
parent
33ccfbb3db
commit
c1b5dd549f
22 changed files with 124 additions and 100 deletions
|
@ -1,5 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
# 1.8.6
|
||||
* Update Policy Reporter UI to 0.13.1
|
||||
* Hide Rule Chips if rule name is empty
|
||||
* Update Policy Reporter Kyvern Plugin to 0.3.2
|
||||
* Improved LivenessProbe, checks now if Kyverno CRDs are available
|
||||
* Update Policy Reporter to 1.8.4
|
||||
* Improved LivenessProbe, checks now if any PolicyReport CRD is available
|
||||
|
||||
# 1.8.5
|
||||
* Added trusted root CA's [#52](https://github.com/kyverno/policy-reporter/pull/52) by [frezbo](https://github.com/frezbo)
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -1,7 +1,7 @@
|
|||
GO ?= go
|
||||
BUILD ?= build
|
||||
REPO ?= fjogeleit/policy-reporter
|
||||
IMAGE_TAG ?= 1.8.3
|
||||
IMAGE_TAG ?= 1.8.4
|
||||
LD_FLAGS="-s -w"
|
||||
|
||||
all: build
|
||||
|
|
|
@ -4,9 +4,9 @@ dependencies:
|
|||
version: 1.4.0
|
||||
- name: ui
|
||||
repository: ""
|
||||
version: 1.8.3
|
||||
version: 1.8.4
|
||||
- name: kyvernoPlugin
|
||||
repository: ""
|
||||
version: 0.5.1
|
||||
digest: sha256:0ef6012a60a7bd73488830f8b466172c27de8f4628e3ff2e3ff3615411c71e7a
|
||||
generated: "2021-07-06T18:15:23.735281+02:00"
|
||||
version: 0.5.2
|
||||
digest: sha256:685d62ebe8f290e74785bf444a072aa839087822aabeb8e91caa4816915bafe5
|
||||
generated: "2021-08-09T20:16:57.768228+02:00"
|
||||
|
|
|
@ -5,8 +5,8 @@ description: |
|
|||
It creates Prometheus Metrics and can send rule validation events to different targets like Loki, Elasticsearch, Slack or Discord
|
||||
|
||||
type: application
|
||||
version: 1.8.5
|
||||
appVersion: 1.8.3
|
||||
version: 1.8.6
|
||||
appVersion: 1.8.4
|
||||
|
||||
dependencies:
|
||||
- name: monitoring
|
||||
|
@ -16,8 +16,8 @@ dependencies:
|
|||
- name: ui
|
||||
condition: ui.enabled
|
||||
repository: ""
|
||||
version: "1.8.3"
|
||||
version: "1.8.4"
|
||||
- name: kyvernoPlugin
|
||||
condition: kyvernoPlugin.enabled
|
||||
repository: ""
|
||||
version: "0.5.1"
|
||||
version: "0.5.2"
|
||||
|
|
|
@ -3,5 +3,5 @@ name: kyvernoPlugin
|
|||
description: Policy Reporter Kyverno Plugin
|
||||
|
||||
type: application
|
||||
version: 0.5.1
|
||||
appVersion: 0.3.1
|
||||
version: 0.5.2
|
||||
appVersion: 0.3.2
|
|
@ -1,7 +1,7 @@
|
|||
image:
|
||||
repository: fjogeleit/policy-reporter-kyverno-plugin
|
||||
pullPolicy: IfNotPresent
|
||||
tag: 0.3.1
|
||||
tag: 0.3.2
|
||||
|
||||
imagePullSecrets: []
|
||||
|
||||
|
|
|
@ -3,5 +3,5 @@ name: ui
|
|||
description: Policy Reporter UI
|
||||
|
||||
type: application
|
||||
version: 1.8.3
|
||||
appVersion: 0.13.0
|
||||
version: 1.8.4
|
||||
appVersion: 0.13.1
|
||||
|
|
|
@ -10,7 +10,7 @@ plugins:
|
|||
image:
|
||||
repository: fjogeleit/policy-reporter-ui
|
||||
pullPolicy: IfNotPresent
|
||||
tag: 0.13.0
|
||||
tag: 0.13.1
|
||||
|
||||
imagePullSecrets: []
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
image:
|
||||
repository: fjogeleit/policy-reporter
|
||||
pullPolicy: IfNotPresent
|
||||
tag: 1.8.3
|
||||
tag: 1.8.4
|
||||
|
||||
imagePullSecrets: []
|
||||
|
||||
|
|
|
@ -66,10 +66,10 @@ func newRunCMD() *cobra.Command {
|
|||
|
||||
errorChan := make(chan error)
|
||||
|
||||
go func() { errorChan <- resolver.APIServer().Start() }()
|
||||
|
||||
go func() { errorChan <- client.StartWatching() }()
|
||||
|
||||
go func() { errorChan <- resolver.APIServer().Start() }()
|
||||
|
||||
go func() {
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ spec:
|
|||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: ui
|
||||
image: "fjogeleit/policy-reporter-ui:0.12.0"
|
||||
image: "fjogeleit/policy-reporter-ui:0.13.1"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
@ -148,7 +148,7 @@ spec:
|
|||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- name: policy-reporter
|
||||
image: "fjogeleit/policy-reporter:1.8.3"
|
||||
image: "fjogeleit/policy-reporter:1.8.4"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
|
|
@ -165,7 +165,7 @@ spec:
|
|||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- name: "kyverno-plugin"
|
||||
image: "fjogeleit/policy-reporter-kyverno-plugin:0.3.1"
|
||||
image: "fjogeleit/policy-reporter-kyverno-plugin:0.3.2"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
@ -215,7 +215,7 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: ui
|
||||
image: "fjogeleit/policy-reporter-ui:0.12.0"
|
||||
image: "fjogeleit/policy-reporter-ui:0.13.1"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
@ -266,7 +266,7 @@ spec:
|
|||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- name: policy-reporter
|
||||
image: "fjogeleit/policy-reporter:1.8.3"
|
||||
image: "fjogeleit/policy-reporter:1.8.4"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
|
|
@ -84,7 +84,7 @@ spec:
|
|||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- name: policy-reporter
|
||||
image: "fjogeleit/policy-reporter:1.8.3"
|
||||
image: "fjogeleit/policy-reporter:1.8.4"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
|
|
@ -3,28 +3,22 @@ package api
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/kyverno/policy-reporter/pkg/report"
|
||||
)
|
||||
|
||||
// HealthzHandler for the Halthz REST API
|
||||
func HealthzHandler() http.HandlerFunc {
|
||||
func HealthzHandler(found map[string]string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, "{}")
|
||||
}
|
||||
}
|
||||
|
||||
// ReadyHandler for the Halthz REST API
|
||||
func ReadyHandler(s *report.PolicyReportStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
if len(s.List(report.PolicyReportType))+len(s.List(report.ClusterPolicyReportType)) == 0 {
|
||||
if len(found) == 0 {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
fmt.Fprint(w, "{}")
|
||||
log.Println("[ERROR] No PolicyReport CRDs found")
|
||||
|
||||
fmt.Fprint(w, `{ "error": "No PolicyReport CRDs found" }`)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -36,6 +30,15 @@ func ReadyHandler(s *report.PolicyReportStore) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// ReadyHandler for the Halthz REST API
|
||||
func ReadyHandler() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, "{}")
|
||||
}
|
||||
}
|
||||
|
||||
// PolicyReportHandler for the PolicyReport REST API
|
||||
func PolicyReportHandler(s *report.PolicyReportStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
@ -204,7 +204,7 @@ func Test_HealthzAPI(t *testing.T) {
|
|||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(api.HealthzHandler())
|
||||
handler := http.HandlerFunc(api.HealthzHandler(map[string]string{"wgpolicyk8s.io/v1alpha2": "wgpolicyk8s.io/v1alpha2"}))
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
|
@ -212,6 +212,22 @@ func Test_HealthzAPI(t *testing.T) {
|
|||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unavailable Respose", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/healthz", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(api.HealthzHandler(map[string]string{}))
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusServiceUnavailable {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusServiceUnavailable)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReadyAPI(t *testing.T) {
|
||||
|
@ -221,36 +237,8 @@ func Test_ReadyAPI(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result := report.Result{
|
||||
Message: "validation error: requests and limits required. Rule autogen-check-for-requests-and-limits failed at path /spec/template/spec/containers/0/resources/requests/",
|
||||
Policy: "require-requests-and-limits-required",
|
||||
Rule: "autogen-check-for-requests-and-limits",
|
||||
Priority: report.ErrorPriority,
|
||||
Status: report.Fail,
|
||||
Category: "resources",
|
||||
Scored: true,
|
||||
Resource: report.Resource{
|
||||
APIVersion: "v1",
|
||||
Kind: "Deployment",
|
||||
Name: "nginx",
|
||||
Namespace: "test",
|
||||
UID: "536ab69f-1b3c-4bd9-9ba4-274a56188409",
|
||||
},
|
||||
}
|
||||
|
||||
preport := report.PolicyReport{
|
||||
Name: "polr-test",
|
||||
Namespace: "test",
|
||||
Results: map[string]report.Result{"": result},
|
||||
Summary: report.Summary{},
|
||||
CreationTimestamp: time.Now(),
|
||||
}
|
||||
|
||||
store := report.NewPolicyReportStore()
|
||||
store.Add(preport)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(api.ReadyHandler(store))
|
||||
handler := http.HandlerFunc(api.ReadyHandler())
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
|
@ -258,22 +246,4 @@ func Test_ReadyAPI(t *testing.T) {
|
|||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Unavailable Respose", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/ready", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store := report.NewPolicyReportStore()
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(api.ReadyHandler(store))
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusServiceUnavailable {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusServiceUnavailable)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,18 +15,19 @@ type Server interface {
|
|||
}
|
||||
|
||||
type httpServer struct {
|
||||
port int
|
||||
mux *http.ServeMux
|
||||
store *report.PolicyReportStore
|
||||
targets []Target
|
||||
port int
|
||||
mux *http.ServeMux
|
||||
store *report.PolicyReportStore
|
||||
targets []Target
|
||||
foundResources map[string]string
|
||||
}
|
||||
|
||||
func (s *httpServer) registerHandler() {
|
||||
s.mux.HandleFunc("/policy-reports", Gzip(PolicyReportHandler(s.store)))
|
||||
s.mux.HandleFunc("/cluster-policy-reports", Gzip(ClusterPolicyReportHandler(s.store)))
|
||||
s.mux.HandleFunc("/targets", Gzip(TargetsHandler(s.targets)))
|
||||
s.mux.HandleFunc("/healthz", HealthzHandler())
|
||||
s.mux.HandleFunc("/ready", ReadyHandler(s.store))
|
||||
s.mux.HandleFunc("/healthz", HealthzHandler(s.foundResources))
|
||||
s.mux.HandleFunc("/ready", ReadyHandler())
|
||||
}
|
||||
|
||||
func (s *httpServer) Start() error {
|
||||
|
@ -39,17 +40,18 @@ func (s *httpServer) Start() error {
|
|||
}
|
||||
|
||||
// NewServer constructor for a new API Server
|
||||
func NewServer(store *report.PolicyReportStore, targets []target.Client, port int) Server {
|
||||
func NewServer(store *report.PolicyReportStore, targets []target.Client, port int, foundResources map[string]string) Server {
|
||||
apiTargets := make([]Target, 0, len(targets))
|
||||
for _, t := range targets {
|
||||
apiTargets = append(apiTargets, mapTarget(t))
|
||||
}
|
||||
|
||||
s := &httpServer{
|
||||
port: port,
|
||||
targets: apiTargets,
|
||||
store: store,
|
||||
mux: http.NewServeMux(),
|
||||
port: port,
|
||||
targets: apiTargets,
|
||||
store: store,
|
||||
mux: http.NewServeMux(),
|
||||
foundResources: foundResources,
|
||||
}
|
||||
|
||||
s.registerHandler()
|
||||
|
|
|
@ -19,6 +19,7 @@ func Test_NewServer(t *testing.T) {
|
|||
discord.NewClient("http://webhook:2000", "", false, &http.Client{}),
|
||||
},
|
||||
8080,
|
||||
make(map[string]string),
|
||||
)
|
||||
|
||||
go server.Start()
|
||||
|
|
|
@ -27,6 +27,7 @@ type Resolver struct {
|
|||
config *Config
|
||||
k8sConfig *rest.Config
|
||||
mapper kubernetes.Mapper
|
||||
policyAdapter kubernetes.PolicyReportAdapter
|
||||
policyStore *report.PolicyReportStore
|
||||
policyClient report.PolicyResultClient
|
||||
lokiClient target.Client
|
||||
|
@ -40,10 +41,18 @@ type Resolver struct {
|
|||
|
||||
// APIServer resolver method
|
||||
func (r *Resolver) APIServer() api.Server {
|
||||
foundResources := make(map[string]string, 0)
|
||||
|
||||
client := r.policyClient
|
||||
if client != nil {
|
||||
foundResources = client.GetFoundResources()
|
||||
}
|
||||
|
||||
return api.NewServer(
|
||||
r.PolicyReportStore(),
|
||||
r.TargetClients(),
|
||||
r.config.API.Port,
|
||||
foundResources,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -305,6 +314,10 @@ func (r *Resolver) configMapAPI() (kubernetes.ConfigMapAdapter, error) {
|
|||
}
|
||||
|
||||
func (r *Resolver) policyReportAPI(ctx context.Context) (kubernetes.PolicyReportAdapter, error) {
|
||||
if r.policyAdapter != nil {
|
||||
return r.policyAdapter, nil
|
||||
}
|
||||
|
||||
client, err := dynamic.NewForConfig(r.k8sConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -314,7 +327,9 @@ func (r *Resolver) policyReportAPI(ctx context.Context) (kubernetes.PolicyReport
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return kubernetes.NewPolicyReportAdapter(client, mapper), nil
|
||||
r.policyAdapter = kubernetes.NewPolicyReportAdapter(client, mapper)
|
||||
|
||||
return r.policyAdapter, nil
|
||||
}
|
||||
|
||||
// ResultCache resolver method
|
||||
|
|
|
@ -30,6 +30,10 @@ func (c *policyReportClient) RegisterPolicyResultCallback(cb report.PolicyResult
|
|||
c.resultCallbacks = append(c.resultCallbacks, cb)
|
||||
}
|
||||
|
||||
func (c *policyReportClient) GetFoundResources() map[string]string {
|
||||
return c.policyAPI.GetFoundResources()
|
||||
}
|
||||
|
||||
func (c *policyReportClient) StartWatching() error {
|
||||
if c.started {
|
||||
return errors.New("StartWatching was already started")
|
||||
|
|
|
@ -3,6 +3,7 @@ package kubernetes
|
|||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/kyverno/policy-reporter/pkg/report"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -45,13 +46,18 @@ type WatchEvent struct {
|
|||
// PolicyReportAdapter translates API responses to an internal struct
|
||||
type PolicyReportAdapter interface {
|
||||
WatchPolicyReports() (chan WatchEvent, error)
|
||||
GetFoundResources() map[string]string
|
||||
}
|
||||
|
||||
type k8sPolicyReportAdapter struct {
|
||||
client dynamic.Interface
|
||||
policyReports schema.GroupVersionResource
|
||||
clusterPolicyReports schema.GroupVersionResource
|
||||
mapper Mapper
|
||||
client dynamic.Interface
|
||||
found map[string]string
|
||||
mapper Mapper
|
||||
mx *sync.Mutex
|
||||
}
|
||||
|
||||
func (k *k8sPolicyReportAdapter) GetFoundResources() map[string]string {
|
||||
return k.found
|
||||
}
|
||||
|
||||
func (k *k8sPolicyReportAdapter) WatchPolicyReports() (chan WatchEvent, error) {
|
||||
|
@ -70,9 +76,16 @@ func (k *k8sPolicyReportAdapter) WatchPolicyReports() (chan WatchEvent, error) {
|
|||
w, err := k.client.Resource(r).Watch(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Resource not Found: %s\n", r.String())
|
||||
k.mx.Lock()
|
||||
delete(k.found, r.String())
|
||||
k.mx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
k.mx.Lock()
|
||||
k.found[r.String()] = r.String()
|
||||
k.mx.Unlock()
|
||||
|
||||
for result := range w.ResultChan() {
|
||||
if item, ok := result.Object.(*unstructured.Unstructured); ok {
|
||||
report := k.mapper.MapPolicyReport(item.Object)
|
||||
|
@ -91,5 +104,7 @@ func NewPolicyReportAdapter(dynamic dynamic.Interface, mapper Mapper) PolicyRepo
|
|||
return &k8sPolicyReportAdapter{
|
||||
client: dynamic,
|
||||
mapper: mapper,
|
||||
mx: &sync.Mutex{},
|
||||
found: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ type fakeClient struct {
|
|||
mapper kubernetes.Mapper
|
||||
}
|
||||
|
||||
func (f *fakeClient) GetFoundResources() map[string]string {
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
func (f *fakeClient) ListPolicyReports() ([]report.PolicyReport, error) {
|
||||
return f.List, f.Error
|
||||
}
|
||||
|
|
|
@ -20,4 +20,6 @@ type PolicyResultClient interface {
|
|||
RegisterPolicyResultWatcher(skipExisting bool)
|
||||
// StartWatching calls the WatchAPI, waiting for incoming PolicyReport watch.Events and call the registered Handlers
|
||||
StartWatching() error
|
||||
// GetFoundResources as Map of Names
|
||||
GetFoundResources() map[string]string
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue