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

Add customFields to missing targets

Signed-off-by: Frank Jogeleit <frank.jogeleit@web.de>
This commit is contained in:
Frank Jogeleit 2022-10-17 10:31:11 +02:00
parent ab90514fc3
commit 573a1108c7
16 changed files with 224 additions and 99 deletions

View file

@ -1,5 +1,11 @@
# Changelog
# 2.13.2
* Policy Reporter
* Add `customFields` property to missing targets: `Elasticsearch`, `S3`, `Webhook`, `Kinesis`
* Policy Reporter UI
* Create Links out of URL property values
# 2.13.1
* Policy Reporter

View file

@ -4,9 +4,9 @@ dependencies:
version: 2.4.1
- name: ui
repository: ""
version: 2.6.4
version: 2.6.5
- name: kyvernoPlugin
repository: ""
version: 1.4.3
digest: sha256:688fdfcd63fee5b3b9e24b8a15591590235ddab3967b48ae2768e00762b2e66c
generated: "2022-09-23T11:22:26.239174+02:00"
digest: sha256:34f738ba910f4eeddb3b278fa73fd891bb19f7327bffa850d7f8300c967b7edb
generated: "2022-10-17T10:19:27.566911+02:00"

View file

@ -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: 2.13.1
appVersion: 2.10.1
version: 2.13.2
appVersion: 2.10.2
icon: https://github.com/kyverno/kyverno/raw/main/img/logo.png
home: https://kyverno.github.io/policy-reporter
@ -21,7 +21,7 @@ dependencies:
version: "2.4.1"
- name: ui
condition: ui.enabled
version: "2.6.4"
version: "2.6.5"
- name: kyvernoPlugin
condition: kyvernoPlugin.enabled
version: "1.4.3"

View file

@ -3,5 +3,5 @@ name: ui
description: Policy Reporter UI
type: application
version: 2.6.4
appVersion: 1.6.6
version: 2.6.5
appVersion: 1.6.7

View file

@ -4,7 +4,7 @@ image:
registry: ghcr.io
repository: kyverno/policy-reporter-ui
pullPolicy: IfNotPresent
tag: 1.6.6
tag: 1.6.7
# possible default displayModes: light/dark
displayMode: ""

View file

@ -38,6 +38,10 @@ elasticsearch:
sources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.elasticsearch.customFields }}
customFields:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.elasticsearch.filter }}
filter:
{{- toYaml . | nindent 4 }}
@ -126,6 +130,10 @@ webhook:
sources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.webhook.customFields }}
customFields:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.webhook.filter }}
filter:
{{- toYaml . | nindent 4 }}
@ -160,6 +168,10 @@ s3:
sources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.s3.customFields }}
customFields:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.s3.filter }}
filter:
{{- toYaml . | nindent 4 }}
@ -182,6 +194,10 @@ kinesis:
sources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.kinesis.customFields }}
customFields:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.kinesis.filter }}
filter:
{{- toYaml . | nindent 4 }}

View file

@ -292,6 +292,8 @@ target:
sources: []
# Skip already existing PolicyReportResults on startup
skipExistingOnStartup: true
# Added as additional properties to each elasticsearch event
customField: {}
# filter results send by namespaces, policies and priorities
filter: {}
# add additional elasticsearch channels with different configurations and filters
@ -398,6 +400,8 @@ target:
sources: []
# Skip already existing PolicyReportResults on startup
skipExistingOnStartup: true
# Added as additional properties to each webhook event
customField: {}
# filter results send by namespaces, policies and priorities
filter: {}
# add additional webhook channels with different configurations and filters
@ -424,6 +428,8 @@ target:
sources: []
# Skip already existing PolicyReportResults on startup
skipExistingOnStartup: true
# Added as additional properties to each s3 event
customField: {}
# filter results send by namespaces, policies and priorities
filter: {}
# add additional s3 channels with different configurations and filters
@ -448,6 +454,8 @@ target:
sources: []
# Skip already existing PolicyReportResults on startup
skipExistingOnStartup: true
# Added as additional properties to each kinesis event
customField: {}
# filter results send by namespaces, policies and priorities
filter: {}
# add additional s3 channels with different configurations and filters

View file

@ -43,20 +43,21 @@ type Loki struct {
// Elasticsearch configuration
type Elasticsearch struct {
Name string `mapstructure:"name"`
Host string `mapstructure:"host"`
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Index string `mapstructure:"index"`
Rotation string `mapstructure:"rotation"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
SecretRef string `mapstructure:"secretRef"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
Sources []string `mapstructure:"sources"`
Channels []Elasticsearch `mapstructure:"channels"`
Name string `mapstructure:"name"`
Host string `mapstructure:"host"`
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Index string `mapstructure:"index"`
Rotation string `mapstructure:"rotation"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
SecretRef string `mapstructure:"secretRef"`
CustomFields map[string]string `mapstructure:"customFields"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
Sources []string `mapstructure:"sources"`
Channels []Elasticsearch `mapstructure:"channels"`
}
// Slack configuration
@ -118,6 +119,7 @@ type Webhook struct {
Certificate string `mapstructure:"certificate"`
Headers map[string]string `mapstructure:"headers"`
SecretRef string `mapstructure:"secretRef"`
CustomFields map[string]string `mapstructure:"customFields"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
@ -127,35 +129,37 @@ type Webhook struct {
// S3 configuration
type S3 struct {
Name string `mapstructure:"name"`
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Region string `mapstructure:"region"`
Endpoint string `mapstructure:"endpoint"`
Prefix string `mapstructure:"prefix"`
Bucket string `mapstructure:"bucket"`
SecretRef string `mapstructure:"secretRef"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
Sources []string `mapstructure:"sources"`
Channels []S3 `mapstructure:"channels"`
Name string `mapstructure:"name"`
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Region string `mapstructure:"region"`
Endpoint string `mapstructure:"endpoint"`
Prefix string `mapstructure:"prefix"`
Bucket string `mapstructure:"bucket"`
SecretRef string `mapstructure:"secretRef"`
CustomFields map[string]string `mapstructure:"customFields"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
Sources []string `mapstructure:"sources"`
Channels []S3 `mapstructure:"channels"`
}
// Kinesis configuration
type Kinesis struct {
Name string `mapstructure:"name"`
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Region string `mapstructure:"region"`
Endpoint string `mapstructure:"endpoint"`
StreamName string `mapstructure:"streamName"`
SecretRef string `mapstructure:"secretRef"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
Sources []string `mapstructure:"sources"`
Channels []Kinesis `mapstructure:"channels"`
Name string `mapstructure:"name"`
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Region string `mapstructure:"region"`
Endpoint string `mapstructure:"endpoint"`
StreamName string `mapstructure:"streamName"`
SecretRef string `mapstructure:"secretRef"`
CustomFields map[string]string `mapstructure:"customFields"`
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
MinimumPriority string `mapstructure:"minimumPriority"`
Filter TargetFilter `mapstructure:"filter"`
Sources []string `mapstructure:"sources"`
Channels []Kinesis `mapstructure:"channels"`
}
// SMTP configuration

View file

@ -11,12 +11,13 @@ import (
// Options to configure elasticsearch target
type Options struct {
target.ClientOptions
Host string
Username string
Password string
Index string
Rotation string
HTTPClient http.Client
Host string
Username string
Password string
Index string
Rotation string
CustomFields map[string]string
HTTPClient http.Client
}
// Rotation Enum
@ -32,12 +33,13 @@ const (
type client struct {
target.BaseClient
host string
index string
username string
password string
rotation Rotation
client http.Client
host string
index string
username string
password string
rotation Rotation
customFields map[string]string
client http.Client
}
func (e *client) Send(result report.Result) {
@ -53,6 +55,20 @@ func (e *client) Send(result report.Result) {
host = e.host + "/" + e.index + "-" + time.Now().Format("2006.01.02") + "/event"
}
if len(e.customFields) > 0 {
props := make(map[string]string, 0)
for property, value := range e.customFields {
props[property] = value
}
for property, value := range result.Properties {
props[property] = value
}
result.Properties = props
}
req, err := http.CreateJSONRequest(e.Name(), "POST", host, result)
if err != nil {
return
@ -75,6 +91,7 @@ func NewClient(options Options) target.Client {
options.Username,
options.Password,
options.Rotation,
options.CustomFields,
options.HTTPClient,
}
}

View file

@ -68,14 +68,19 @@ func Test_ElasticsearchTarget(t *testing.T) {
ClientOptions: target.ClientOptions{
Name: "Elasticsearch",
},
Host: "http://localhost:9200",
Username: "username",
Password: "password",
Index: "policy-reporter",
Rotation: elasticsearch.Annually,
HTTPClient: testClient{callback, 200},
Host: "http://localhost:9200",
Username: "username",
Password: "password",
Index: "policy-reporter",
Rotation: elasticsearch.Annually,
HTTPClient: testClient{callback, 200},
CustomFields: map[string]string{"cluster": "name"},
})
client.Send(completeResult)
if len(completeResult.Properties) > 1 {
t.Error("expected customFields are not added to the actuel result")
}
})
t.Run("Send with Monthly Result", func(t *testing.T) {
callback := func(req *http.Request) {

View file

@ -15,15 +15,31 @@ import (
// Options to configure the Kinesis target
type Options struct {
target.ClientOptions
Kinesis helper.AWSClient
CustomFields map[string]string
Kinesis helper.AWSClient
}
type client struct {
target.BaseClient
kinesis helper.AWSClient
customFields map[string]string
kinesis helper.AWSClient
}
func (c *client) Send(result report.Result) {
if len(c.customFields) > 0 {
props := make(map[string]string, 0)
for property, value := range c.customFields {
props[property] = value
}
for property, value := range result.Properties {
props[property] = value
}
result.Properties = props
}
body := new(bytes.Buffer)
if err := json.NewEncoder(body).Encode(result); err != nil {
@ -45,6 +61,7 @@ func (c *client) Send(result report.Result) {
func NewClient(options Options) target.Client {
return &client{
target.NewBaseClient(options.ClientOptions),
options.CustomFields,
options.Kinesis,
}
}

View file

@ -11,14 +11,15 @@ import (
)
var completeResult = 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.WarningPriority,
Status: report.Fail,
Severity: report.High,
Category: "resources",
Scored: true,
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.WarningPriority,
Status: report.Fail,
Severity: report.High,
Category: "resources",
Scored: true,
Properties: map[string]string{"version": "1234"},
Resource: report.Resource{
APIVersion: "v1",
Kind: "Deployment",
@ -61,9 +62,14 @@ func Test_KinesisTarget(t *testing.T) {
ClientOptions: target.ClientOptions{
Name: "Kinesis",
},
Kinesis: &testClient{nil, callback},
CustomFields: map[string]string{"cluster": "name"},
Kinesis: &testClient{nil, callback},
})
client.Send(completeResult)
if len(completeResult.Properties) > 1 || completeResult.Properties["cluster"] != "" {
t.Error("expected customFields are not added to the actuel result")
}
})
t.Run("Name", func(t *testing.T) {
client := kinesis.NewClient(kinesis.Options{

View file

@ -15,17 +15,33 @@ import (
// Options to configure the Kinesis target
type Options struct {
target.ClientOptions
S3 helper.AWSClient
Prefix string
CustomFields map[string]string
S3 helper.AWSClient
Prefix string
}
type client struct {
target.BaseClient
s3 helper.AWSClient
prefix string
customFields map[string]string
s3 helper.AWSClient
prefix string
}
func (c *client) Send(result report.Result) {
if len(c.customFields) > 0 {
props := make(map[string]string, 0)
for property, value := range c.customFields {
props[property] = value
}
for property, value := range result.Properties {
props[property] = value
}
result.Properties = props
}
body := new(bytes.Buffer)
if err := json.NewEncoder(body).Encode(result); err != nil {
@ -47,6 +63,7 @@ func (c *client) Send(result report.Result) {
func NewClient(options Options) target.Client {
return &client{
target.NewBaseClient(options.ClientOptions),
options.CustomFields,
options.S3,
options.Prefix,
}

View file

@ -11,14 +11,15 @@ import (
)
var completeResult = 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.WarningPriority,
Status: report.Fail,
Severity: report.High,
Category: "resources",
Scored: true,
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.WarningPriority,
Status: report.Fail,
Severity: report.High,
Category: "resources",
Scored: true,
Properties: map[string]string{"version": "1234"},
Resource: report.Resource{
APIVersion: "v1",
Kind: "Deployment",
@ -57,9 +58,14 @@ func Test_S3Target(t *testing.T) {
ClientOptions: target.ClientOptions{
Name: "S3",
},
S3: &testClient{nil, callback},
CustomFields: map[string]string{"cluster": "name"},
S3: &testClient{nil, callback},
})
client.Send(completeResult)
if len(completeResult.Properties) > 1 || completeResult.Properties["cluster"] != "" {
t.Error("expected customFields are not added to the actuel result")
}
})
t.Run("Name", func(t *testing.T) {
client := s3.NewClient(s3.Options{

View file

@ -9,19 +9,35 @@ import (
// Options to configure the Discord target
type Options struct {
target.ClientOptions
Host string
Headers map[string]string
HTTPClient http.Client
Host string
Headers map[string]string
CustomFields map[string]string
HTTPClient http.Client
}
type client struct {
target.BaseClient
host string
headers map[string]string
client http.Client
host string
headers map[string]string
customFields map[string]string
client http.Client
}
func (e *client) Send(result report.Result) {
if len(e.customFields) > 0 {
props := make(map[string]string, 0)
for property, value := range e.customFields {
props[property] = value
}
for property, value := range result.Properties {
props[property] = value
}
result.Properties = props
}
req, err := http.CreateJSONRequest(e.Name(), "POST", e.host, http.NewJSONResult(result))
if err != nil {
return
@ -41,6 +57,7 @@ func NewClient(options Options) target.Client {
target.NewBaseClient(options.ClientOptions),
options.Host,
options.Headers,
options.CustomFields,
options.HTTPClient,
}
}

View file

@ -27,6 +27,7 @@ var completeResult = report.Result{
Namespace: "default",
UID: "536ab69f-1b3c-4bd9-9ba4-274a56188409",
},
Properties: map[string]string{"version": "1234"},
}
type testClient struct {
@ -69,11 +70,16 @@ func Test_UITarget(t *testing.T) {
ClientOptions: target.ClientOptions{
Name: "UI",
},
Host: "http://localhost:8080/webhook",
Headers: map[string]string{"X-Code": "1234"},
HTTPClient: testClient{callback, 200},
Host: "http://localhost:8080/webhook",
Headers: map[string]string{"X-Code": "1234"},
CustomFields: map[string]string{"cluster": "name"},
HTTPClient: testClient{callback, 200},
})
client.Send(completeResult)
if len(completeResult.Properties) > 1 || completeResult.Properties["cluster"] != "" {
t.Error("expected customFields are not added to the actuel result")
}
})
t.Run("Name", func(t *testing.T) {
client := webhook.NewClient(webhook.Options{