From 169a0688206718c5c12fb293d836172a93a15b94 Mon Sep 17 00:00:00 2001 From: Frank Jogeleit Date: Thu, 9 May 2024 11:32:04 +0200 Subject: [PATCH] fix slack tests Signed-off-by: Frank Jogeleit --- pkg/config/target_factory.go | 3 +- pkg/config/target_factory_test.go | 14 ++-- pkg/fixtures/policy_reports.go | 36 +++++++++ pkg/target/slack/client.go | 24 ++++++ pkg/target/slack/slack.go | 23 +++--- pkg/target/slack/slack_test.go | 127 +++++++++++++----------------- 6 files changed, 135 insertions(+), 92 deletions(-) create mode 100644 pkg/target/slack/client.go diff --git a/pkg/config/target_factory.go b/pkg/config/target_factory.go index ce78a8222..75e63828f 100644 --- a/pkg/config/target_factory.go +++ b/pkg/config/target_factory.go @@ -126,10 +126,9 @@ func (f *TargetFactory) createSlackClient(config, parent *Target[SlackOptions]) ResultFilter: f.createResultFilter(config.Filter, config.MinimumPriority, config.Sources), ReportFilter: createReportFilter(config.Filter), }, - Webhook: config.Config.Webhook, Channel: config.Config.Channel, CustomFields: config.CustomFields, - HTTPClient: http.NewClient("", false), + HTTPClient: slack.NewAPIClient(config.Config.Webhook, http.NewClient("", false)), }) } diff --git a/pkg/config/target_factory_test.go b/pkg/config/target_factory_test.go index 5201a3470..0e75e2e8c 100644 --- a/pkg/config/target_factory_test.go +++ b/pkg/config/target_factory_test.go @@ -31,6 +31,7 @@ func newFakeClient() v1.SecretInterface { "host": []byte("http://localhost:9200"), "username": []byte("username"), "password": []byte("password"), + "channel": []byte("general"), "apiKey": []byte("apiKey"), "webhook": []byte("http://localhost:9200/webhook"), "accountId": []byte("accountID"), @@ -50,6 +51,7 @@ func mountSecret() { secretValues := secrets.Values{ Host: "http://localhost:9200", Webhook: "http://localhost:9200/webhook", + Channel: "general", Username: "username", Password: "password", APIKey: "apiKey", @@ -376,9 +378,9 @@ func Test_GetValuesFromSecret(t *testing.T) { t.Run("Get Slack values from Secret", func(t *testing.T) { client := reflect.ValueOf(clients[2]).Elem() - webhook := client.FieldByName("webhook").String() - if webhook != "http://localhost:9200/webhook" { - t.Errorf("Expected webhook from secret, got %s", webhook) + webhook := client.FieldByName("channel").String() + if webhook != "general" { + t.Errorf("Expected channel from secret, got %s", webhook) } }) @@ -745,9 +747,9 @@ func Test_GetValuesFromMountedSecret(t *testing.T) { t.Run("Get Slack values from Secret", func(t *testing.T) { client := reflect.ValueOf(clients[2]).Elem() - webhook := client.FieldByName("webhook").String() - if webhook != "http://localhost:9200/webhook" { - t.Errorf("Expected webhook from secret, got %s", webhook) + webhook := client.FieldByName("channel").String() + if webhook != "general" { + t.Errorf("Expected channel from secret, got %s", webhook) } }) diff --git a/pkg/fixtures/policy_reports.go b/pkg/fixtures/policy_reports.go index 44671eb7f..cde8a261b 100644 --- a/pkg/fixtures/policy_reports.go +++ b/pkg/fixtures/policy_reports.go @@ -87,6 +87,42 @@ var DefaultPolicyReport = &v1alpha2.PolicyReport{ }, } +var ScopePolicyReport = &v1alpha2.PolicyReport{ + ObjectMeta: v1.ObjectMeta{ + Name: "policy-report", + Namespace: "test", + }, + Summary: v1alpha2.PolicyReportSummary{ + Pass: 0, + Skip: 0, + Warn: 0, + Fail: 3, + Error: 0, + }, + Scope: &corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Deployment", + Name: "nginx", + Namespace: "test", + UID: "dfd57c50-f30c-4729-b63f-b1954d8988d1", + }, + Results: []v1alpha2.PolicyReportResult{ + { + ID: "12348", + Message: "message", + Result: v1alpha2.StatusFail, + Scored: true, + Policy: "required-label", + Rule: "app-label-required", + Timestamp: v1.Timestamp{Seconds: 1614093000}, + Source: "test", + Category: "test", + Severity: v1alpha2.SeverityHigh, + Properties: map[string]string{"version": "1.2.0"}, + }, + }, +} + var MultiResourcePolicyReport = &v1alpha2.PolicyReport{ ObjectMeta: v1.ObjectMeta{ Name: "policy-report", diff --git a/pkg/target/slack/client.go b/pkg/target/slack/client.go new file mode 100644 index 000000000..173f4d39c --- /dev/null +++ b/pkg/target/slack/client.go @@ -0,0 +1,24 @@ +package slack + +import ( + "net/http" + + "github.com/slack-go/slack" +) + +type APIClient interface { + PostMessage(*slack.WebhookMessage) error +} + +type apiClient struct { + webhook string + client *http.Client +} + +func (c *apiClient) PostMessage(message *slack.WebhookMessage) error { + return slack.PostWebhookCustomHTTP(c.webhook, c.client, message) +} + +func NewAPIClient(webhook string, client *http.Client) APIClient { + return &apiClient{webhook: webhook, client: client} +} diff --git a/pkg/target/slack/slack.go b/pkg/target/slack/slack.go index d3cbd1534..1a72ad735 100644 --- a/pkg/target/slack/slack.go +++ b/pkg/target/slack/slack.go @@ -3,7 +3,6 @@ package slack import ( "context" "fmt" - "net/http" "strings" "github.com/slack-go/slack" @@ -13,23 +12,20 @@ import ( "github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2" "github.com/kyverno/policy-reporter/pkg/helper" "github.com/kyverno/policy-reporter/pkg/target" - rest "github.com/kyverno/policy-reporter/pkg/target/http" ) // Options to configure the Slack target type Options struct { target.ClientOptions - Webhook string Channel string CustomFields map[string]string - HTTPClient rest.Client + HTTPClient APIClient } type client struct { target.BaseClient - webhook string channel string - client rest.Client + client APIClient customFields map[string]string } @@ -271,17 +267,21 @@ func (s *client) batchMessage(polr v1alpha2.ReportInterface, results []v1alpha2. } func (s *client) Send(result v1alpha2.PolicyReportResult) { - client := s.client.(*http.Client) - - if err := slack.PostWebhookCustomHTTP(s.webhook, client, s.message(result)); err != nil { + if err := s.client.PostMessage(s.message(result)); err != nil { zap.L().Error(s.Name()+": PUSH FAILED", zap.Error(err)) } } func (s *client) BatchSend(report v1alpha2.ReportInterface, results []v1alpha2.PolicyReportResult) { - client := s.client.(*http.Client) + if report.GetScope() == nil { + for _, result := range results { + s.Send(result) + } + + return + } - if err := slack.PostWebhookCustomHTTP(s.webhook, client, s.batchMessage(report, results)); err != nil { + if err := s.client.PostMessage(s.batchMessage(report, results)); err != nil { zap.L().Error(s.Name()+": BATCH PUSH FAILED", zap.Error(err)) } } @@ -296,7 +296,6 @@ func (s *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func NewClient(options Options) target.Client { return &client{ target.NewBaseClient(options.ClientOptions), - options.Webhook, options.Channel, options.HTTPClient, options.CustomFields, diff --git a/pkg/target/slack/slack_test.go b/pkg/target/slack/slack_test.go index a09538e55..0ead08f31 100644 --- a/pkg/target/slack/slack_test.go +++ b/pkg/target/slack/slack_test.go @@ -1,124 +1,119 @@ package slack_test import ( - "net/http" "testing" + goslack "github.com/slack-go/slack" + "github.com/stretchr/testify/assert" + + "github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2" "github.com/kyverno/policy-reporter/pkg/fixtures" "github.com/kyverno/policy-reporter/pkg/target" "github.com/kyverno/policy-reporter/pkg/target/slack" ) type testClient struct { - callback func(req *http.Request) + callback func(req *goslack.WebhookMessage) statusCode int } -func (c testClient) Do(req *http.Request) (*http.Response, error) { +func (c testClient) PostMessage(req *goslack.WebhookMessage) error { c.callback(req) - return &http.Response{ - StatusCode: c.statusCode, - }, nil + return nil } func Test_SlackTarget(t *testing.T) { t.Run("Send Complete Result", func(t *testing.T) { - callback := func(req *http.Request) { - if contentType := req.Header.Get("Content-Type"); contentType != "application/json; charset=utf-8" { - t.Errorf("Unexpected Content-Type: %s", contentType) - } - - if agend := req.Header.Get("User-Agent"); agend != "Policy-Reporter" { - t.Errorf("Unexpected Host: %s", agend) - } - - if url := req.URL.String(); url != "http://hook.slack:80" { - t.Errorf("Unexpected Host: %s", url) - } + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 1, len(req.Attachments)) } client := slack.NewClient(slack.Options{ ClientOptions: target.ClientOptions{ Name: "Slack", }, - Webhook: "http://hook.slack:80", CustomFields: map[string]string{"Cluster": "Name"}, HTTPClient: testClient{callback, 200}, }) client.Send(fixtures.CompleteTargetSendResult) }) + t.Run("Send Batch Results", func(t *testing.T) { + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 3, len(req.Attachments)) + } + + client := slack.NewClient(slack.Options{ + ClientOptions: target.ClientOptions{ + Name: "Slack", + }, + CustomFields: map[string]string{"Cluster": "Name"}, + HTTPClient: testClient{callback, 200}, + }) + + client.BatchSend(fixtures.ScopePolicyReport, []v1alpha2.PolicyReportResult{ + fixtures.CompleteTargetSendResult, + fixtures.CritcalSendResult, + }) + }) + + t.Run("Send Batch Results without scope", func(t *testing.T) { + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 1, len(req.Attachments)) + } + + client := slack.NewClient(slack.Options{ + ClientOptions: target.ClientOptions{ + Name: "Slack", + }, + CustomFields: map[string]string{"Cluster": "Name"}, + HTTPClient: testClient{callback, 200}, + }) + + client.BatchSend(fixtures.EmptyPolicyReport, []v1alpha2.PolicyReportResult{ + fixtures.CompleteTargetSendResult, + fixtures.CritcalSendResult, + }) + }) + t.Run("Send Minimal Result", func(t *testing.T) { - callback := func(req *http.Request) { - if contentType := req.Header.Get("Content-Type"); contentType != "application/json; charset=utf-8" { - t.Errorf("Unexpected Content-Type: %s", contentType) - } - - if agend := req.Header.Get("User-Agent"); agend != "Policy-Reporter" { - t.Errorf("Unexpected Host: %s", agend) - } - - if url := req.URL.String(); url != "http://hook.slack:80" { - t.Errorf("Unexpected Host: %s", url) - } + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 1, len(req.Attachments)) } client := slack.NewClient(slack.Options{ ClientOptions: target.ClientOptions{ Name: "Slack", }, - Webhook: "http://hook.slack:80", HTTPClient: testClient{callback, 200}, }) client.Send(fixtures.MinimalTargetSendResult) }) t.Run("Send enforce Result", func(t *testing.T) { - callback := func(req *http.Request) { - if contentType := req.Header.Get("Content-Type"); contentType != "application/json; charset=utf-8" { - t.Errorf("Unexpected Content-Type: %s", contentType) - } - - if agend := req.Header.Get("User-Agent"); agend != "Policy-Reporter" { - t.Errorf("Unexpected Host: %s", agend) - } - - if url := req.URL.String(); url != "http://hook.slack:80" { - t.Errorf("Unexpected Host: %s", url) - } + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 1, len(req.Attachments)) } client := slack.NewClient(slack.Options{ ClientOptions: target.ClientOptions{ Name: "Slack", }, - Webhook: "http://hook.slack:80", HTTPClient: testClient{callback, 200}, }) client.Send(fixtures.EnforceTargetSendResult) }) t.Run("Send incomplete Result", func(t *testing.T) { - callback := func(req *http.Request) { - if contentType := req.Header.Get("Content-Type"); contentType != "application/json; charset=utf-8" { - t.Errorf("Unexpected Content-Type: %s", contentType) - } - - if agend := req.Header.Get("User-Agent"); agend != "Policy-Reporter" { - t.Errorf("Unexpected Host: %s", agend) - } - - if url := req.URL.String(); url != "http://hook.slack:80" { - t.Errorf("Unexpected Host: %s", url) - } + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 1, len(req.Attachments)) } client := slack.NewClient(slack.Options{ ClientOptions: target.ClientOptions{ Name: "Slack", }, - Webhook: "http://hook.slack:80", CustomFields: map[string]string{"Cluster": "Name"}, HTTPClient: testClient{callback, 200}, }) @@ -126,25 +121,14 @@ func Test_SlackTarget(t *testing.T) { }) t.Run("Send incomplete Result2", func(t *testing.T) { - callback := func(req *http.Request) { - if contentType := req.Header.Get("Content-Type"); contentType != "application/json; charset=utf-8" { - t.Errorf("Unexpected Content-Type: %s", contentType) - } - - if agend := req.Header.Get("User-Agent"); agend != "Policy-Reporter" { - t.Errorf("Unexpected Host: %s", agend) - } - - if url := req.URL.String(); url != "http://hook.slack:80" { - t.Errorf("Unexpected Host: %s", url) - } + callback := func(req *goslack.WebhookMessage) { + assert.Equal(t, 1, len(req.Attachments)) } client := slack.NewClient(slack.Options{ ClientOptions: target.ClientOptions{ Name: "Slack", }, - Webhook: "http://hook.slack:80", CustomFields: map[string]string{"Cluster": "Name"}, HTTPClient: testClient{callback, 200}, }) @@ -156,7 +140,6 @@ func Test_SlackTarget(t *testing.T) { ClientOptions: target.ClientOptions{ Name: "Slack", }, - Webhook: "http://hook.slack:80", CustomFields: map[string]string{"Cluster": "Name"}, HTTPClient: testClient{}, })