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

Merge pull request #342 from kyverno/google-chat

google chat notifications
This commit is contained in:
Frank Jogeleit 2023-09-05 12:15:20 +02:00 committed by GitHub
commit cf4655643c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 903 additions and 588 deletions

View file

@ -177,6 +177,31 @@ telegram:
{{- toYaml . | nindent 4 }}
{{- end }}
googleChat:
webhook: {{ .Values.target.googleChat.webhook | quote }}
certificate: {{ .Values.target.googleChat.certificate | quote }}
skipTLS: {{ .Values.target.googleChat.skipTLS }}
secretRef: {{ .Values.target.googleChat.secretRef | quote }}
mountedSecret: {{ .Values.target.googleChat.mountedSecret | quote }}
minimumPriority: {{ .Values.target.googleChat.minimumPriority | quote }}
skipExistingOnStartup: {{ .Values.target.googleChat.skipExistingOnStartup }}
{{- with .Values.target.googleChat.sources }}
sources:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.googleChat.customFields }}
customFields:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.googleChat.filter }}
filter:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.target.googleChat.channels }}
channels:
{{- toYaml . | nindent 4 }}
{{- end }}
ui:
host: {{ include "policyreporter.uihost" . }}
certificate: {{ .Values.target.ui.certificate | quote }}

View file

@ -514,6 +514,33 @@ target:
# add additional telegram channels with different configurations and filters
channels: []
googleChat:
# GoogleChat webhook
webhook: ""
# path to your custom certificate
# can be added under extraVolumes
certificate: ""
# skip TLS verification if necessary
skipTLS: false
# receive the host and/or token from an existing secret, the token is added as Authorization header
secretRef: ""
# Mounted secret path by Secrets Controller, secret should be in json format
mountedSecret: ""
# additional http headers
headers: {}
# minimum priority "" < info < warning < critical < error
minimumPriority: ""
# list of sources which should send to telegram
sources: []
# Skip already existing PolicyReportResults on startup
skipExistingOnStartup: true
# Added as additional properties to each notification
customFields: {}
# filter results send by namespaces, policies and priorities
filter: {}
# add additional telegram channels with different configurations and filters
channels: []
s3:
# S3 access key
accessKeyID: ""

View file

@ -1,5 +1,7 @@
package config
import "github.com/kyverno/policy-reporter/pkg/target"
type ValueFilter struct {
Include []string `mapstructure:"include"`
Exclude []string `mapstructure:"exclude"`
@ -37,6 +39,54 @@ type TargetBaseOptions struct {
SkipExisting bool `mapstructure:"skipExistingOnStartup"`
}
func (config *TargetBaseOptions) MapBaseParent(parent TargetBaseOptions) {
if config.MinimumPriority == "" {
config.MinimumPriority = parent.MinimumPriority
}
if !config.SkipExisting {
config.SkipExisting = parent.SkipExisting
}
}
func (config *TargetBaseOptions) ClientOptions() target.ClientOptions {
return target.ClientOptions{
Name: config.Name,
SkipExistingOnStartup: config.SkipExisting,
ResultFilter: createResultFilter(config.Filter, config.MinimumPriority, config.Sources),
ReportFilter: createReportFilter(config.Filter),
}
}
type AWSConfig struct {
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Region string `mapstructure:"region"`
Endpoint string `mapstructure:"endpoint"`
}
func (config *AWSConfig) MapAWSParent(parent AWSConfig) {
if config.Endpoint == "" {
config.Endpoint = parent.Endpoint
}
if config.AccessKeyID == "" {
config.AccessKeyID = parent.AccessKeyID
}
if config.SecretAccessKey == "" {
config.SecretAccessKey = parent.SecretAccessKey
}
if config.Region == "" {
config.Region = parent.Region
}
}
type TargetOption interface {
BaseOptions() *TargetBaseOptions
}
// Loki configuration
type Loki struct {
TargetBaseOptions `mapstructure:",squash"`
@ -45,7 +95,7 @@ type Loki struct {
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Path string `mapstructure:"path"`
Channels []Loki `mapstructure:"channels"`
Channels []*Loki `mapstructure:"channels"`
}
// Elasticsearch configuration
@ -58,7 +108,7 @@ type Elasticsearch struct {
Rotation string `mapstructure:"rotation"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Channels []Elasticsearch `mapstructure:"channels"`
Channels []*Elasticsearch `mapstructure:"channels"`
}
// Slack configuration
@ -66,14 +116,14 @@ type Slack struct {
TargetBaseOptions `mapstructure:",squash"`
Webhook string `mapstructure:"webhook"`
Channel string `mapstructure:"channel"`
Channels []Slack `mapstructure:"channels"`
Channels []*Slack `mapstructure:"channels"`
}
// Discord configuration
type Discord struct {
TargetBaseOptions `mapstructure:",squash"`
Webhook string `mapstructure:"webhook"`
Channels []Discord `mapstructure:"channels"`
Channels []*Discord `mapstructure:"channels"`
}
// Teams configuration
@ -82,7 +132,7 @@ type Teams struct {
Webhook string `mapstructure:"webhook"`
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Channels []Teams `mapstructure:"channels"`
Channels []*Teams `mapstructure:"channels"`
}
// UI configuration
@ -100,7 +150,7 @@ type Webhook struct {
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Headers map[string]string `mapstructure:"headers"`
Channels []Webhook `mapstructure:"channels"`
Channels []*Webhook `mapstructure:"channels"`
}
// Telegram configuration
@ -112,14 +162,17 @@ type Telegram struct {
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Headers map[string]string `mapstructure:"headers"`
Channels []Telegram `mapstructure:"channels"`
Channels []*Telegram `mapstructure:"channels"`
}
type AWSConfig struct {
AccessKeyID string `mapstructure:"accessKeyID"`
SecretAccessKey string `mapstructure:"secretAccessKey"`
Region string `mapstructure:"region"`
Endpoint string `mapstructure:"endpoint"`
// GoogleChat configuration
type GoogleChat struct {
TargetBaseOptions `mapstructure:",squash"`
Webhook string `mapstructure:"webhook"`
SkipTLS bool `mapstructure:"skipTLS"`
Certificate string `mapstructure:"certificate"`
Headers map[string]string `mapstructure:"headers"`
Channels []*GoogleChat `mapstructure:"channels"`
}
// S3 configuration
@ -132,7 +185,7 @@ type S3 struct {
KmsKeyID string `mapstructure:"kmsKeyId"`
ServerSideEncryption string `mapstructure:"serverSideEncryption"`
PathStyle bool `mapstructure:"pathStyle"`
Channels []S3 `mapstructure:"channels"`
Channels []*S3 `mapstructure:"channels"`
}
// Kinesis configuration
@ -140,7 +193,7 @@ type Kinesis struct {
TargetBaseOptions `mapstructure:",squash"`
AWSConfig `mapstructure:",squash"`
StreamName string `mapstructure:"streamName"`
Channels []Kinesis `mapstructure:"channels"`
Channels []*Kinesis `mapstructure:"channels"`
}
// SecurityHub configuration
@ -148,7 +201,7 @@ type SecurityHub struct {
TargetBaseOptions `mapstructure:",squash"`
AWSConfig `mapstructure:",squash"`
AccountID string `mapstructure:"accountId"`
Channels []SecurityHub `mapstructure:"channels"`
Channels []*SecurityHub `mapstructure:"channels"`
}
// GCS configuration
@ -158,7 +211,7 @@ type GCS struct {
Prefix string `mapstructure:"prefix"`
Bucket string `mapstructure:"bucket"`
Sources []string `mapstructure:"sources"`
Channels []GCS `mapstructure:"channels"`
Channels []*GCS `mapstructure:"channels"`
}
// SMTP configuration
@ -283,18 +336,19 @@ type Database struct {
type Config struct {
Version string
Namespace string `mapstructure:"namespace"`
Loki Loki `mapstructure:"loki"`
Elasticsearch Elasticsearch `mapstructure:"elasticsearch"`
Slack Slack `mapstructure:"slack"`
Discord Discord `mapstructure:"discord"`
Teams Teams `mapstructure:"teams"`
S3 S3 `mapstructure:"s3"`
Kinesis Kinesis `mapstructure:"kinesis"`
SecurityHub SecurityHub `mapstructure:"securityHub"`
GCS GCS `mapstructure:"gcs"`
UI UI `mapstructure:"ui"`
Webhook Webhook `mapstructure:"webhook"`
Telegram Telegram `mapstructure:"telegram"`
Loki *Loki `mapstructure:"loki"`
Elasticsearch *Elasticsearch `mapstructure:"elasticsearch"`
Slack *Slack `mapstructure:"slack"`
Discord *Discord `mapstructure:"discord"`
Teams *Teams `mapstructure:"teams"`
S3 *S3 `mapstructure:"s3"`
Kinesis *Kinesis `mapstructure:"kinesis"`
SecurityHub *SecurityHub `mapstructure:"securityHub"`
GCS *GCS `mapstructure:"gcs"`
UI *UI `mapstructure:"ui"`
Webhook *Webhook `mapstructure:"webhook"`
Telegram *Telegram `mapstructure:"telegram"`
GoogleChat *GoogleChat `mapstructure:"googleChat"`
API API `mapstructure:"api"`
WorkerCount int `mapstructure:"worker"`
DBFile string `mapstructure:"dbfile"`

View file

@ -280,6 +280,7 @@ func (r *Resolver) TargetClients() []target.Client {
clients = append(clients, factory.WebhookClients(r.config.Webhook)...)
clients = append(clients, factory.GCSClients(r.config.GCS)...)
clients = append(clients, factory.TelegramClients(r.config.Telegram)...)
clients = append(clients, factory.GoogleChatClients(r.config.GoogleChat)...)
if ui := factory.UIClient(r.config.UI); ui != nil {
clients = append(clients, ui)

View file

@ -12,7 +12,7 @@ import (
)
var testConfig = &config.Config{
Loki: config.Loki{
Loki: &config.Loki{
Host: "http://localhost:3100",
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
@ -20,7 +20,7 @@ var testConfig = &config.Config{
CustomFields: map[string]string{"field": "value"},
},
SkipTLS: true,
Channels: []config.Loki{
Channels: []*config.Loki{
{
TargetBaseOptions: config.TargetBaseOptions{
CustomFields: map[string]string{"label2": "value2"},
@ -28,7 +28,7 @@ var testConfig = &config.Config{
},
},
},
Elasticsearch: config.Elasticsearch{
Elasticsearch: &config.Elasticsearch{
Host: "http://localhost:9200",
Index: "policy-reporter",
Rotation: "daily",
@ -38,33 +38,33 @@ var testConfig = &config.Config{
CustomFields: map[string]string{"field": "value"},
},
SkipTLS: true,
Channels: []config.Elasticsearch{{}},
Channels: []*config.Elasticsearch{{}},
},
Slack: config.Slack{
Slack: &config.Slack{
Webhook: "http://hook.slack:80",
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
CustomFields: map[string]string{"field": "value"},
},
Channels: []config.Slack{{
Channels: []*config.Slack{{
Webhook: "http://localhost:9200",
}, {
Channel: "general",
}},
},
Discord: config.Discord{
Discord: &config.Discord{
Webhook: "http://hook.discord:80",
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
CustomFields: map[string]string{"field": "value"},
},
Channels: []config.Discord{{
Channels: []*config.Discord{{
Webhook: "http://localhost:9200",
}},
},
Teams: config.Teams{
Teams: &config.Teams{
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
@ -72,18 +72,18 @@ var testConfig = &config.Config{
},
Webhook: "http://hook.teams:80",
SkipTLS: true,
Channels: []config.Teams{{
Channels: []*config.Teams{{
Webhook: "http://localhost:9200",
}},
},
UI: config.UI{
UI: &config.UI{
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
},
Host: "http://localhost:8080",
},
Webhook: config.Webhook{
Webhook: &config.Webhook{
Host: "http://localhost:8080",
Headers: map[string]string{
"X-Custom": "Header",
@ -94,14 +94,14 @@ var testConfig = &config.Config{
CustomFields: map[string]string{"field": "value"},
},
SkipTLS: true,
Channels: []config.Webhook{{
Channels: []*config.Webhook{{
Host: "http://localhost:8081",
Headers: map[string]string{
"X-Custom-2": "Header",
},
}},
},
S3: config.S3{
S3: &config.S3{
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
@ -119,9 +119,9 @@ var testConfig = &config.Config{
ServerSideEncryption: "",
PathStyle: true,
Prefix: "prefix",
Channels: []config.S3{{}},
Channels: []*config.S3{{}},
},
Kinesis: config.Kinesis{
Kinesis: &config.Kinesis{
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
@ -134,9 +134,9 @@ var testConfig = &config.Config{
Region: "ru-central1",
},
StreamName: "policy-reporter",
Channels: []config.Kinesis{{}},
Channels: []*config.Kinesis{{}},
},
SecurityHub: config.SecurityHub{
SecurityHub: &config.SecurityHub{
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
@ -149,9 +149,9 @@ var testConfig = &config.Config{
Region: "ru-central1",
},
AccountID: "AccountID",
Channels: []config.SecurityHub{{}},
Channels: []*config.SecurityHub{{}},
},
GCS: config.GCS{
GCS: &config.GCS{
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
@ -160,7 +160,7 @@ var testConfig = &config.Config{
Credentials: `{"token": "token", "type": "authorized_user"}`,
Bucket: "test",
Prefix: "prefix",
Channels: []config.GCS{{}},
Channels: []*config.GCS{{}},
},
EmailReports: config.EmailReports{
Templates: config.EmailTemplates{
@ -175,22 +175,26 @@ var testConfig = &config.Config{
Encryption: "ssl/tls",
},
},
Telegram: config.Telegram{
Telegram: &config.Telegram{
Token: "XXX",
ChatID: "123456",
Channels: []config.Telegram{
Channels: []*config.Telegram{
{
ChatID: "1234567",
},
},
},
GoogleChat: &config.GoogleChat{
Webhook: "http://localhost:900/webhook",
Channels: []*config.GoogleChat{{}},
},
}
func Test_ResolveTargets(t *testing.T) {
resolver := config.NewResolver(testConfig, &rest.Config{})
if count := len(resolver.TargetClients()); count != 24 {
t.Errorf("Expected 24 Clients, got %d", count)
if count := len(resolver.TargetClients()); count != 26 {
t.Errorf("Expected 26 Clients, got %d", count)
}
}
@ -204,14 +208,14 @@ func Test_ResolveHasTargets(t *testing.T) {
func Test_ResolveSkipExistingOnStartup(t *testing.T) {
testConfig := &config.Config{
Loki: config.Loki{
Loki: &config.Loki{
Host: "http://localhost:3100",
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
MinimumPriority: "debug",
},
},
Elasticsearch: config.Elasticsearch{
Elasticsearch: &config.Elasticsearch{
Host: "http://localhost:9200",
TargetBaseOptions: config.TargetBaseOptions{
SkipExisting: true,
@ -554,7 +558,7 @@ func Test_ResolveEnableLeaderElection(t *testing.T) {
t.Run("general disabled", func(t *testing.T) {
resolver := config.NewResolver(&config.Config{
LeaderElection: config.LeaderElection{Enabled: false},
Loki: config.Loki{Host: "localhost:3100"},
Loki: &config.Loki{Host: "localhost:3100"},
Database: config.Database{Type: database.MySQL},
}, &rest.Config{})
@ -579,7 +583,7 @@ func Test_ResolveEnableLeaderElection(t *testing.T) {
resolver := config.NewResolver(&config.Config{
LeaderElection: config.LeaderElection{Enabled: true},
Database: config.Database{Type: database.SQLite},
Loki: config.Loki{Host: "localhost:3100"},
Loki: &config.Loki{Host: "localhost:3100"},
DBFile: "test.db",
}, &rest.Config{})

File diff suppressed because it is too large Load diff

View file

@ -108,6 +108,12 @@ func Test_ResolveTarget(t *testing.T) {
t.Errorf("Expected 2 Client, got %d clients", len(clients))
}
})
t.Run("GoogleChat", func(t *testing.T) {
clients := factory.GoogleChatClients(testConfig.GoogleChat)
if len(clients) != 2 {
t.Errorf("Expected 2 Client, got %d clients", len(clients))
}
})
t.Run("S3", func(t *testing.T) {
clients := factory.S3Clients(testConfig.S3)
if len(clients) != 2 {
@ -138,137 +144,142 @@ func Test_ResolveTargetWithoutHost(t *testing.T) {
factory := config.NewTargetFactory(nil)
t.Run("Loki", func(t *testing.T) {
if len(factory.LokiClients(config.Loki{})) != 0 {
if len(factory.LokiClients(&config.Loki{})) != 0 {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Elasticsearch", func(t *testing.T) {
if len(factory.ElasticsearchClients(config.Elasticsearch{})) != 0 {
if len(factory.ElasticsearchClients(&config.Elasticsearch{})) != 0 {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Slack", func(t *testing.T) {
if len(factory.SlackClients(config.Slack{})) != 0 {
if len(factory.SlackClients(&config.Slack{})) != 0 {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Discord", func(t *testing.T) {
if len(factory.DiscordClients(config.Discord{})) != 0 {
if len(factory.DiscordClients(&config.Discord{})) != 0 {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Teams", func(t *testing.T) {
if len(factory.TeamsClients(config.Teams{})) != 0 {
if len(factory.TeamsClients(&config.Teams{})) != 0 {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Webhook", func(t *testing.T) {
if len(factory.WebhookClients(config.Webhook{})) != 0 {
if len(factory.WebhookClients(&config.Webhook{})) != 0 {
t.Error("Expected Client to be nil if no host is configured")
}
})
t.Run("Telegram", func(t *testing.T) {
if len(factory.TelegramClients(config.Telegram{})) != 0 {
if len(factory.TelegramClients(&config.Telegram{})) != 0 {
t.Error("Expected Client to be nil if no chatID is configured")
}
})
t.Run("GoogleChat", func(t *testing.T) {
if len(factory.GoogleChatClients(&config.GoogleChat{})) != 0 {
t.Error("Expected Client to be nil if no webhook is configured")
}
})
t.Run("S3.Endoint", func(t *testing.T) {
if len(factory.S3Clients(config.S3{})) != 0 {
if len(factory.S3Clients(&config.S3{})) != 0 {
t.Error("Expected Client to be nil if no endpoint is configured")
}
})
t.Run("S3.AccessKey", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net"}})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net"}})) != 0 {
t.Error("Expected Client to be nil if no accessKey is configured")
}
})
t.Run("S3.SecretAccessKey", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access"}})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access"}})) != 0 {
t.Error("Expected Client to be nil if no secretAccessKey is configured")
}
})
t.Run("S3.Region", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret"}})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret"}})) != 0 {
t.Error("Expected Client to be nil if no region is configured")
}
})
t.Run("S3.Bucket", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}})) != 0 {
t.Error("Expected Client to be nil if no bucket is configured")
}
})
t.Run("S3.SSE-S3", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, ServerSideEncryption: "AES256"})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, ServerSideEncryption: "AES256"})) != 0 {
t.Error("Expected Client to be nil if server side encryption is not configured")
}
})
t.Run("S3.SSE-KMS", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, ServerSideEncryption: "aws:kms"})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, ServerSideEncryption: "aws:kms"})) != 0 {
t.Error("Expected Client to be nil if server side encryption is not configured")
}
})
t.Run("S3.SSE-KMS-S3-KEY", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, BucketKeyEnabled: true, ServerSideEncryption: "aws:kms"})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, BucketKeyEnabled: true, ServerSideEncryption: "aws:kms"})) != 0 {
t.Error("Expected Client to be nil if server side encryption is not configured")
}
})
t.Run("S3.SSE-KMS-KEY-ID", func(t *testing.T) {
if len(factory.S3Clients(config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, ServerSideEncryption: "aws:kms", KmsKeyID: "kmsKeyId"})) != 0 {
if len(factory.S3Clients(&config.S3{AWSConfig: config.AWSConfig{Endpoint: "https://storage.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}, ServerSideEncryption: "aws:kms", KmsKeyID: "kmsKeyId"})) != 0 {
t.Error("Expected Client to be nil if server side encryption is not configured")
}
})
t.Run("Kinesis.Endpoint", func(t *testing.T) {
if len(factory.KinesisClients(config.Kinesis{})) != 0 {
if len(factory.KinesisClients(&config.Kinesis{})) != 0 {
t.Error("Expected Client to be nil if no endpoint is configured")
}
})
t.Run("Kinesis.AccessKey", func(t *testing.T) {
if len(factory.KinesisClients(config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net"}})) != 0 {
if len(factory.KinesisClients(&config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net"}})) != 0 {
t.Error("Expected Client to be nil if no accessKey is configured")
}
})
t.Run("Kinesis.SecretAccessKey", func(t *testing.T) {
if len(factory.KinesisClients(config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net", AccessKeyID: "access"}})) != 0 {
if len(factory.KinesisClients(&config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net", AccessKeyID: "access"}})) != 0 {
t.Error("Expected Client to be nil if no secretAccessKey is configured")
}
})
t.Run("Kinesis.Region", func(t *testing.T) {
if len(factory.KinesisClients(config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret"}})) != 0 {
if len(factory.KinesisClients(&config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret"}})) != 0 {
t.Error("Expected Client to be nil if no region is configured")
}
})
t.Run("Kinesis.StreamName", func(t *testing.T) {
if len(factory.KinesisClients(config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}})) != 0 {
if len(factory.KinesisClients(&config.Kinesis{AWSConfig: config.AWSConfig{Endpoint: "https://yds.serverless.yandexcloud.net", AccessKeyID: "access", SecretAccessKey: "secret", Region: "ru-central1"}})) != 0 {
t.Error("Expected Client to be nil if no stream name is configured")
}
})
t.Run("SecurityHub.AccountID", func(t *testing.T) {
if len(factory.SecurityHubs(config.SecurityHub{})) != 0 {
if len(factory.SecurityHubs(&config.SecurityHub{})) != 0 {
t.Error("Expected Client to be nil if no accountID is configured")
}
})
t.Run("SecurityHub.AccessKey", func(t *testing.T) {
if len(factory.SecurityHubs(config.SecurityHub{AccountID: "accountID"})) != 0 {
if len(factory.SecurityHubs(&config.SecurityHub{AccountID: "accountID"})) != 0 {
t.Error("Expected Client to be nil if no accessKey is configured")
}
})
t.Run("SecurityHub.SecretAccessKey", func(t *testing.T) {
if len(factory.SecurityHubs(config.SecurityHub{AccountID: "accountID", AWSConfig: config.AWSConfig{AccessKeyID: "access"}})) != 0 {
if len(factory.SecurityHubs(&config.SecurityHub{AccountID: "accountID", AWSConfig: config.AWSConfig{AccessKeyID: "access"}})) != 0 {
t.Error("Expected Client to be nil if no secretAccessKey is configured")
}
})
t.Run("SecurityHub.Region", func(t *testing.T) {
if len(factory.SecurityHubs(config.SecurityHub{AccountID: "accountID", AWSConfig: config.AWSConfig{AccessKeyID: "access", SecretAccessKey: "secret"}})) != 0 {
if len(factory.SecurityHubs(&config.SecurityHub{AccountID: "accountID", AWSConfig: config.AWSConfig{AccessKeyID: "access", SecretAccessKey: "secret"}})) != 0 {
t.Error("Expected Client to be nil if no region is configured")
}
})
t.Run("GCS.Bucket", func(t *testing.T) {
if len(factory.GCSClients(config.GCS{})) != 0 {
if len(factory.GCSClients(&config.GCS{})) != 0 {
t.Error("Expected Client to be nil if no bucket is configured")
}
})
t.Run("GCS.Credentials", func(t *testing.T) {
if len(factory.GCSClients(config.GCS{Bucket: "policy-reporter"})) != 0 {
if len(factory.GCSClients(&config.GCS{Bucket: "policy-reporter"})) != 0 {
t.Error("Expected Client to be nil if no accessKey is configured")
}
})
@ -278,9 +289,9 @@ func Test_GetValuesFromSecret(t *testing.T) {
factory := config.NewTargetFactory(secrets.NewClient(newFakeClient()))
t.Run("Get Loki values from Secret", func(t *testing.T) {
clients := factory.LokiClients(config.Loki{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
clients := factory.LokiClients(&config.Loki{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
t.Fatal("Expected one client created")
}
fv := reflect.ValueOf(clients[0]).Elem().FieldByName("host")
@ -290,9 +301,9 @@ func Test_GetValuesFromSecret(t *testing.T) {
})
t.Run("Get Elasticsearch values from Secret", func(t *testing.T) {
clients := factory.ElasticsearchClients(config.Elasticsearch{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
clients := factory.ElasticsearchClients(&config.Elasticsearch{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
t.Fatal("Expected one client created")
}
client := reflect.ValueOf(clients[0]).Elem()
@ -307,6 +318,16 @@ func Test_GetValuesFromSecret(t *testing.T) {
t.Errorf("Expected username from secret, got %s", username)
}
rotation := client.FieldByName("rotation").String()
if rotation != "daily" {
t.Errorf("Expected rotation from secret, got %s", rotation)
}
index := client.FieldByName("index").String()
if index != "policy-reporter" {
t.Errorf("Expected rotation from secret, got %s", index)
}
password := client.FieldByName("password").String()
if password != "password" {
t.Errorf("Expected password from secret, got %s", password)
@ -314,7 +335,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
})
t.Run("Get Discord values from Secret", func(t *testing.T) {
clients := factory.DiscordClients(config.Discord{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
clients := factory.DiscordClients(&config.Discord{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -328,7 +349,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
})
t.Run("Get MS Teams values from Secret", func(t *testing.T) {
clients := factory.TeamsClients(config.Teams{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
clients := factory.TeamsClients(&config.Teams{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -342,7 +363,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
})
t.Run("Get Slack values from Secret", func(t *testing.T) {
clients := factory.SlackClients(config.Slack{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
clients := factory.SlackClients(&config.Slack{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -356,7 +377,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
})
t.Run("Get Webhook Authentication Token from Secret", func(t *testing.T) {
clients := factory.WebhookClients(config.Webhook{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
clients := factory.WebhookClients(&config.Webhook{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -370,7 +391,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
})
t.Run("Get Telegram Token from Secret", func(t *testing.T) {
clients := factory.TelegramClients(config.Telegram{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, ChatID: "1234"})
clients := factory.TelegramClients(&config.Telegram{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, ChatID: "1234"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -382,44 +403,57 @@ func Test_GetValuesFromSecret(t *testing.T) {
t.Errorf("Expected host with token from secret, got %s", host)
}
})
t.Run("Get GoogleChat Webhook from Secret", func(t *testing.T) {
clients := factory.GoogleChatClients(&config.GoogleChat{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
client := reflect.ValueOf(clients[0]).Elem()
host := client.FieldByName("webhook").String()
if host != "http://localhost:9200/webhook" {
t.Errorf("Expected host with token from secret, got %s", host)
}
})
t.Run("Get S3 values from Secret", func(t *testing.T) {
clients := factory.S3Clients(config.S3{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endoint", Region: "region"}, Bucket: "bucket"})
clients := factory.S3Clients(&config.S3{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endoint", Region: "region"}, Bucket: "bucket"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get S3 values from Secret with KMS", func(t *testing.T) {
clients := factory.S3Clients(config.S3{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endoint", Region: "region"}, Bucket: "bucket", BucketKeyEnabled: true, ServerSideEncryption: "aws:kms"})
clients := factory.S3Clients(&config.S3{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endoint", Region: "region"}, Bucket: "bucket", BucketKeyEnabled: true, ServerSideEncryption: "aws:kms"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get Kinesis values from Secret", func(t *testing.T) {
clients := factory.KinesisClients(config.Kinesis{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, StreamName: "stream"})
clients := factory.KinesisClients(&config.Kinesis{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, StreamName: "stream"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get GCS values from Secret", func(t *testing.T) {
clients := factory.GCSClients(config.GCS{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, Bucket: "bucket"})
clients := factory.GCSClients(&config.GCS{TargetBaseOptions: config.TargetBaseOptions{SecretRef: secretName}, Bucket: "bucket"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get none existing secret skips target", func(t *testing.T) {
clients := factory.LokiClients(config.Loki{TargetBaseOptions: config.TargetBaseOptions{SecretRef: "no-exist"}})
clients := factory.LokiClients(&config.Loki{TargetBaseOptions: config.TargetBaseOptions{SecretRef: "no-exist"}})
if len(clients) != 0 {
t.Error("Expected client are skipped")
}
})
t.Run("Get CustomFields from Slack", func(t *testing.T) {
clients := factory.SlackClients(config.Slack{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http://localhost"})
clients := factory.SlackClients(&config.Slack{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http://localhost"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -432,7 +466,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})
t.Run("Get CustomFields from Discord", func(t *testing.T) {
clients := factory.DiscordClients(config.Discord{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http://localhost"})
clients := factory.DiscordClients(&config.Discord{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http://localhost"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -445,7 +479,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})
t.Run("Get CustomFields from MS Teams", func(t *testing.T) {
clients := factory.TeamsClients(config.Teams{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http://localhost"})
clients := factory.TeamsClients(&config.Teams{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http://localhost"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -458,7 +492,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})
t.Run("Get CustomFields from Elasticsearch", func(t *testing.T) {
clients := factory.ElasticsearchClients(config.Elasticsearch{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Host: "http://localhost"})
clients := factory.ElasticsearchClients(&config.Elasticsearch{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Host: "http://localhost"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -471,7 +505,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})
t.Run("Get CustomFields from Webhook", func(t *testing.T) {
clients := factory.WebhookClients(config.Webhook{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Host: "http://localhost"})
clients := factory.WebhookClients(&config.Webhook{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Host: "http://localhost"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -484,7 +518,20 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})
t.Run("Get CustomFields from Telegram", func(t *testing.T) {
clients := factory.TelegramClients(config.Telegram{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Token: "XXX", ChatID: "1234"})
clients := factory.TelegramClients(&config.Telegram{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Token: "XXX", ChatID: "1234"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
client := reflect.ValueOf(clients[0]).Elem()
customFields := client.FieldByName("customFields").MapKeys()
if customFields[0].String() != "field" {
t.Errorf("Expected customFields are added")
}
})
t.Run("Get CustomFields from GoogleChat", func(t *testing.T) {
clients := factory.GoogleChatClients(&config.GoogleChat{TargetBaseOptions: config.TargetBaseOptions{CustomFields: map[string]string{"field": "value"}}, Webhook: "http;//googlechat.webhook"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -523,7 +570,7 @@ func Test_GetValuesFromSecret(t *testing.T) {
}
})
t.Run("Get CustomLabels from Loki", func(t *testing.T) {
clients := factory.LokiClients(config.Loki{
clients := factory.LokiClients(&config.Loki{
CustomLabels: map[string]string{"label": "value"},
Host: "http://localhost",
})
@ -559,7 +606,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
defer os.Remove(mountedSecret)
t.Run("Get Loki values from MountedSecret", func(t *testing.T) {
clients := factory.LokiClients(config.Loki{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
clients := factory.LokiClients(&config.Loki{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -571,7 +618,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
})
t.Run("Get Elasticsearch values from MountedSecret", func(t *testing.T) {
clients := factory.ElasticsearchClients(config.Elasticsearch{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
clients := factory.ElasticsearchClients(&config.Elasticsearch{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -595,7 +642,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
})
t.Run("Get Discord values from MountedSecret", func(t *testing.T) {
clients := factory.DiscordClients(config.Discord{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
clients := factory.DiscordClients(&config.Discord{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -609,7 +656,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
})
t.Run("Get MS Teams values from MountedSecret", func(t *testing.T) {
clients := factory.TeamsClients(config.Teams{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
clients := factory.TeamsClients(&config.Teams{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -623,7 +670,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
})
t.Run("Get Slack values from MountedSecret", func(t *testing.T) {
clients := factory.SlackClients(config.Slack{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
clients := factory.SlackClients(&config.Slack{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -637,7 +684,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
})
t.Run("Get Webhook Authentication Token from MountedSecret", func(t *testing.T) {
clients := factory.WebhookClients(config.Webhook{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
clients := factory.WebhookClients(&config.Webhook{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -651,7 +698,7 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
})
t.Run("Get Telegram Token from MountedSecret", func(t *testing.T) {
clients := factory.TelegramClients(config.Telegram{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, ChatID: "123"})
clients := factory.TelegramClients(&config.Telegram{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, ChatID: "123"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
@ -664,36 +711,50 @@ func Test_GetValuesFromMountedSecret(t *testing.T) {
}
})
t.Run("Get GoogleChat Webhook from MountedSecret", func(t *testing.T) {
clients := factory.GoogleChatClients(&config.GoogleChat{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}})
if len(clients) != 1 {
t.Error("Expected one client created")
}
client := reflect.ValueOf(clients[0]).Elem()
token := client.FieldByName("webhook").String()
if token != "http://localhost:9200/webhook" {
t.Errorf("Expected token from mounted secret, got %s", token)
}
})
t.Run("Get S3 values from MountedSecret", func(t *testing.T) {
clients := factory.S3Clients(config.S3{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, Bucket: "bucket"})
clients := factory.S3Clients(&config.S3{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, Bucket: "bucket"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get S3 values from MountedSecret with KMS", func(t *testing.T) {
clients := factory.S3Clients(config.S3{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, Bucket: "bucket", BucketKeyEnabled: true, ServerSideEncryption: "aws:kms"})
clients := factory.S3Clients(&config.S3{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, Bucket: "bucket", BucketKeyEnabled: true, ServerSideEncryption: "aws:kms"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get Kinesis values from MountedSecret", func(t *testing.T) {
clients := factory.KinesisClients(config.Kinesis{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, StreamName: "stream"})
clients := factory.KinesisClients(&config.Kinesis{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, AWSConfig: config.AWSConfig{Endpoint: "endpoint", Region: "region"}, StreamName: "stream"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get GCS values from MountedSecret", func(t *testing.T) {
clients := factory.GCSClients(config.GCS{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, Bucket: "bucket"})
clients := factory.GCSClients(&config.GCS{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: mountedSecret}, Bucket: "bucket"})
if len(clients) != 1 {
t.Error("Expected one client created")
}
})
t.Run("Get none existing mounted secret skips target", func(t *testing.T) {
clients := factory.LokiClients(config.Loki{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: "no-exists"}})
clients := factory.LokiClients(&config.Loki{TargetBaseOptions: config.TargetBaseOptions{MountedSecret: "no-exists"}})
if len(clients) != 0 {
t.Error("Expected client are skipped")
}

View file

@ -0,0 +1,228 @@
package googlechat
import (
"bytes"
"text/template"
"time"
"github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2"
"github.com/kyverno/policy-reporter/pkg/target"
"github.com/kyverno/policy-reporter/pkg/target/http"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
)
const messageTempl string = `[{{ .Priority }}] {{ or .Result.Policy .Result.Rule }}`
const resourceTempl string = `{{ if .Namespace }}[{{ .Namespace }}] {{ end }} {{ .APIVersion }}/{{ .Kind }} {{ .Name }}`
type values struct {
Result v1alpha2.PolicyReportResult
Priority string
Resource *corev1.ObjectReference
}
type header struct {
Title string `json:"title"`
SubTitle string `json:"subtitle"`
}
type decoratedText struct {
TopLabel string `json:"topLabel"`
Text string `json:"text"`
}
type column struct {
Widgets []widget `json:"widgets"`
}
type columns struct {
ColumnItems []column `json:"columnItems"`
}
type textParagraph struct {
Text string `json:"text"`
}
type widget struct {
DecoratedText *decoratedText `json:"decoratedText,omitempty"`
TextParagraph *textParagraph `json:"textParagraph,omitempty"`
Columns *columns `json:"columns,omitempty"`
}
type section struct {
Header string `json:"header,omitempty"`
Collapsible bool `json:"collapsible,omitempty"`
Widgets []widget `json:"widgets,omitempty"`
}
type card struct {
Header *header `json:"header,omitempty"`
Sections []section `json:"sections,omitempty"`
}
type cardsV2 struct {
CardID string `json:"cardId,omitempty"`
Card card `json:"card,omitempty"`
}
type Payload struct {
CardsV2 []cardsV2 `json:"cardsV2,omitempty"`
}
// Options to configure the Discord target
type Options struct {
target.ClientOptions
Webhook string
Headers map[string]string
CustomFields map[string]string
HTTPClient http.Client
}
type client struct {
target.BaseClient
webhook string
headers map[string]string
customFields map[string]string
client http.Client
}
func mapPayload(result v1alpha2.PolicyReportResult) (*Payload, error) {
widgets := []widget{{TextParagraph: &textParagraph{Text: result.Message}}}
ttmpl, err := template.New("googlechat").Parse(messageTempl)
if err != nil {
return nil, err
}
var prio = result.Priority.String()
if prio == "" {
prio = v1alpha2.DebugPriority.String()
}
var textBuffer bytes.Buffer
err = ttmpl.Execute(&textBuffer, values{Result: result, Priority: prio, Resource: result.GetResource()})
if err != nil {
return nil, err
}
subtitle := ""
if result.HasResource() {
res := result.GetResource()
widgets = append(widgets, widget{
Columns: &columns{
ColumnItems: []column{
{
Widgets: []widget{
{DecoratedText: &decoratedText{TopLabel: "Kind", Text: res.Kind}},
{DecoratedText: &decoratedText{TopLabel: "Namespace", Text: res.Namespace}},
{DecoratedText: &decoratedText{"Status", string(result.Result)}},
},
},
{
Widgets: []widget{
{DecoratedText: &decoratedText{TopLabel: "APIVersion", Text: res.APIVersion}},
{DecoratedText: &decoratedText{TopLabel: "Name", Text: res.Name}},
{DecoratedText: &decoratedText{"Source", result.Source}},
},
},
},
},
})
stmpl, err := template.New("googlechat:resource").Parse(resourceTempl)
if err != nil {
return nil, err
}
var subTitleBuffer bytes.Buffer
err = stmpl.Execute(&subTitleBuffer, res)
if err != nil {
return nil, err
}
subtitle = subTitleBuffer.String()
}
header := header{
Title: textBuffer.String(),
SubTitle: subtitle,
}
if result.Policy != "" {
widgets = append(widgets, widget{DecoratedText: &decoratedText{"Rule", result.Rule}})
}
if result.Category != "" {
widgets = append(widgets, widget{DecoratedText: &decoratedText{"Category", result.Category}})
}
for key, value := range result.Properties {
widgets = append(widgets, widget{DecoratedText: &decoratedText{TopLabel: key, Text: value}})
}
widgets = append(widgets, widget{DecoratedText: &decoratedText{"time", time.Now().Format("02 Jan 06 15:04 MST")}})
return &Payload{
CardsV2: []cardsV2{
{
CardID: result.ID,
Card: card{
Header: &header,
Sections: []section{
{
Header: "Details",
Collapsible: true,
Widgets: widgets,
},
},
},
},
},
}, nil
}
func (e *client) Send(result v1alpha2.PolicyReportResult) {
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
}
payload, err := mapPayload(result)
if err != nil {
zap.L().Error(e.Name()+": PUSH FAILED", zap.Error(err))
return
}
req, err := http.CreateJSONRequest(e.Name(), "POST", e.webhook, payload)
if err != nil {
return
}
for header, value := range e.headers {
req.Header.Set(header, value)
}
resp, err := e.client.Do(req)
http.ProcessHTTPResponse(e.Name(), resp, err)
}
// NewClient creates a new loki.client to send Results to Elasticsearch
func NewClient(options Options) target.Client {
return &client{
target.NewBaseClient(options.ClientOptions),
options.Webhook,
options.Headers,
options.CustomFields,
options.HTTPClient,
}
}

View file

@ -0,0 +1,79 @@
package googlechat_test
import (
"io"
"net/http"
"strings"
"testing"
"github.com/kyverno/policy-reporter/pkg/fixtures"
"github.com/kyverno/policy-reporter/pkg/target"
"github.com/kyverno/policy-reporter/pkg/target/googlechat"
)
type testClient struct {
callback func(req *http.Request) error
statusCode int
}
func (c testClient) Do(req *http.Request) (*http.Response, error) {
err := c.callback(req)
return &http.Response{
StatusCode: c.statusCode,
Body: io.NopCloser(strings.NewReader("")),
}, err
}
func Test_GoogleChatTarget(t *testing.T) {
t.Run("Send", func(t *testing.T) {
callback := func(req *http.Request) error {
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 != "https://googlechat.webhook" {
t.Errorf("Unexpected Host: %s", url)
}
if value := req.Header.Get("X-Code"); value != "1234" {
t.Errorf("Unexpected Header X-Code: %s", value)
}
return nil
}
client := googlechat.NewClient(googlechat.Options{
ClientOptions: target.ClientOptions{
Name: "GoogleChat",
},
Webhook: "https://googlechat.webhook",
Headers: map[string]string{"X-Code": "1234"},
CustomFields: map[string]string{"cluster": "name"},
HTTPClient: testClient{callback, 200},
})
client.Send(fixtures.CompleteTargetSendResult)
if len(fixtures.CompleteTargetSendResult.Properties) > 1 || fixtures.CompleteTargetSendResult.Properties["cluster"] != "" {
t.Error("expected customFields are not added to the actuel result")
}
})
t.Run("Name", func(t *testing.T) {
client := googlechat.NewClient(googlechat.Options{
ClientOptions: target.ClientOptions{
Name: "GoogleChat",
},
Webhook: "https://googlechat.webhook",
Headers: map[string]string{"X-Code": "1234"},
HTTPClient: testClient{},
})
if client.Name() != "GoogleChat" {
t.Errorf("Unexpected Name %s", client.Name())
}
})
}