diff --git a/CHANGELOG.md b/CHANGELOG.md index a7276e62..b2045afc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/Makefile b/Makefile index cd1a1aa9..86d3d0b1 100644 --- a/Makefile +++ b/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 diff --git a/charts/policy-reporter/Chart.lock b/charts/policy-reporter/Chart.lock index f62d7987..0910301b 100644 --- a/charts/policy-reporter/Chart.lock +++ b/charts/policy-reporter/Chart.lock @@ -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" diff --git a/charts/policy-reporter/Chart.yaml b/charts/policy-reporter/Chart.yaml index 1adf31d8..202d1ea8 100644 --- a/charts/policy-reporter/Chart.yaml +++ b/charts/policy-reporter/Chart.yaml @@ -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" diff --git a/charts/policy-reporter/charts/kyvernoPlugin/Chart.yaml b/charts/policy-reporter/charts/kyvernoPlugin/Chart.yaml index 8fb9eeb8..589fdf7c 100644 --- a/charts/policy-reporter/charts/kyvernoPlugin/Chart.yaml +++ b/charts/policy-reporter/charts/kyvernoPlugin/Chart.yaml @@ -3,5 +3,5 @@ name: kyvernoPlugin description: Policy Reporter Kyverno Plugin type: application -version: 0.5.1 -appVersion: 0.3.1 \ No newline at end of file +version: 0.5.2 +appVersion: 0.3.2 \ No newline at end of file diff --git a/charts/policy-reporter/charts/kyvernoPlugin/values.yaml b/charts/policy-reporter/charts/kyvernoPlugin/values.yaml index f2665e03..2e3935ed 100644 --- a/charts/policy-reporter/charts/kyvernoPlugin/values.yaml +++ b/charts/policy-reporter/charts/kyvernoPlugin/values.yaml @@ -1,7 +1,7 @@ image: repository: fjogeleit/policy-reporter-kyverno-plugin pullPolicy: IfNotPresent - tag: 0.3.1 + tag: 0.3.2 imagePullSecrets: [] diff --git a/charts/policy-reporter/charts/ui/Chart.yaml b/charts/policy-reporter/charts/ui/Chart.yaml index 5e8f6867..3d20c367 100644 --- a/charts/policy-reporter/charts/ui/Chart.yaml +++ b/charts/policy-reporter/charts/ui/Chart.yaml @@ -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 diff --git a/charts/policy-reporter/charts/ui/values.yaml b/charts/policy-reporter/charts/ui/values.yaml index 1ba5e373..a89f1808 100644 --- a/charts/policy-reporter/charts/ui/values.yaml +++ b/charts/policy-reporter/charts/ui/values.yaml @@ -10,7 +10,7 @@ plugins: image: repository: fjogeleit/policy-reporter-ui pullPolicy: IfNotPresent - tag: 0.13.0 + tag: 0.13.1 imagePullSecrets: [] diff --git a/charts/policy-reporter/values.yaml b/charts/policy-reporter/values.yaml index e65b51ff..e8907498 100644 --- a/charts/policy-reporter/values.yaml +++ b/charts/policy-reporter/values.yaml @@ -1,7 +1,7 @@ image: repository: fjogeleit/policy-reporter pullPolicy: IfNotPresent - tag: 1.8.3 + tag: 1.8.4 imagePullSecrets: [] diff --git a/cmd/run.go b/cmd/run.go index 3aee3424..1d6b6dec 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -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()) diff --git a/manifest/default-policy-reporter-ui/install.yaml b/manifest/default-policy-reporter-ui/install.yaml index 0d45be1e..3d2864f1 100644 --- a/manifest/default-policy-reporter-ui/install.yaml +++ b/manifest/default-policy-reporter-ui/install.yaml @@ -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 diff --git a/manifest/kyverno-policy-reporter-ui/install.yaml b/manifest/kyverno-policy-reporter-ui/install.yaml index d2edd677..09879efd 100644 --- a/manifest/kyverno-policy-reporter-ui/install.yaml +++ b/manifest/kyverno-policy-reporter-ui/install.yaml @@ -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 diff --git a/manifest/policy-reporter/install.yaml b/manifest/policy-reporter/install.yaml index 5002c4a0..7325b260 100644 --- a/manifest/policy-reporter/install.yaml +++ b/manifest/policy-reporter/install.yaml @@ -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 diff --git a/pkg/api/handler.go b/pkg/api/handler.go index 003e1dc5..6d558687 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -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) { diff --git a/pkg/api/handler_test.go b/pkg/api/handler_test.go index f288676a..2cd0a577 100644 --- a/pkg/api/handler_test.go +++ b/pkg/api/handler_test.go @@ -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) - } - }) } diff --git a/pkg/api/server.go b/pkg/api/server.go index c44c2e68..a181fc52 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -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() diff --git a/pkg/api/server_test.go b/pkg/api/server_test.go index a9a3957c..1377fe9b 100644 --- a/pkg/api/server_test.go +++ b/pkg/api/server_test.go @@ -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() diff --git a/pkg/config/resolver.go b/pkg/config/resolver.go index 19cc24fe..966f8ae3 100644 --- a/pkg/config/resolver.go +++ b/pkg/config/resolver.go @@ -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 diff --git a/pkg/kubernetes/policy_report_client.go b/pkg/kubernetes/policy_report_client.go index d74b4a0c..351da51f 100644 --- a/pkg/kubernetes/policy_report_client.go +++ b/pkg/kubernetes/policy_report_client.go @@ -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") diff --git a/pkg/kubernetes/report_adapter.go b/pkg/kubernetes/report_adapter.go index c76a8631..3853d1d5 100644 --- a/pkg/kubernetes/report_adapter.go +++ b/pkg/kubernetes/report_adapter.go @@ -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), } } diff --git a/pkg/kubernetes/report_client_test.go b/pkg/kubernetes/report_client_test.go index a6f2d526..cd906b4f 100644 --- a/pkg/kubernetes/report_client_test.go +++ b/pkg/kubernetes/report_client_test.go @@ -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 } diff --git a/pkg/report/client.go b/pkg/report/client.go index 705de354..ed6def16 100644 --- a/pkg/report/client.go +++ b/pkg/report/client.go @@ -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 }