Repository: abahmed/kwatch
Pull request #192 (2023-02-15 05:00:59)

Custom Webhook addition
Fixes # .

Changes proposed in this pull request:
- Changed yaml type from map[string]string to map[string]interface{} for flexibility 
- Addition of custom webhook to send alert to any endpoint with headers and authentication added
-



Patch:
diff --git a/alertmanager/alertmanager.go b/alertmanager/alertmanager.go
index f6ade1f..75b2fbc 100644
--- a/alertmanager/alertmanager.go
+++ b/alertmanager/alertmanager.go
@@ -1,6 +1,7 @@
 package alertmanager
 
 import (
+	"github.com/abahmed/kwatch/alertmanager/webhook"
 	"reflect"
 	"strings"
 
@@ -35,7 +36,7 @@ type Provider interface {
 
 // Init initializes AlertManager with provided config
 func (a *AlertManager) Init(
-	alertCfg map[string]map[string]string,
+	alertCfg map[string]map[string]interface{},
 	appCfg *config.App) {
 	a.providers = make([]Provider, 0)
 	for k, v := range alertCfg {
@@ -65,8 +66,9 @@ func (a *AlertManager) Init(
 			pvdr = dingtalk.NewDingTalk(v, appCfg)
 		} else if lowerCaseKey == "feishu" {
 			pvdr = feishu.NewFeiShu(v, appCfg)
+		} else if lowerCaseKey == "webhook" {
+			pvdr = webhook.NewWebhook(v, appCfg)
 		}
-
 		if !reflect.ValueOf(pvdr).IsNil() {
 			a.providers = append(a.providers, pvdr)
 		}
diff --git a/alertmanager/dingtalk/dingtalk.go b/alertmanager/dingtalk/dingtalk.go
index 411a345..c1a90fa 100644
--- a/alertmanager/dingtalk/dingtalk.go
+++ b/alertmanager/dingtalk/dingtalk.go
@@ -38,20 +38,21 @@ type DingTalk struct {
 }
 
 // NewDingTalk returns new DingTalk instance
-func NewDingTalk(config map[string]string, appCfg *config.App) *DingTalk {
+func NewDingTalk(config map[string]interface{}, appCfg *config.App) *DingTalk {
 	accessToken, ok := config["accessToken"]
-	if !ok || len(accessToken) == 0 {
+	accessTokenString := fmt.Sprint(accessToken)
+	if !ok || len(accessTokenString) == 0 {
 		logrus.Warnf("initializing dingtalk with empty access token")
 		return nil
 	}
 
-	logrus.Infof("initializing dingtalk with access token: %s", accessToken)
+	logrus.Infof("initializing dingtalk with access token: %s", accessTokenString)
 
 	return &DingTalk{
-		accessToken: accessToken,
+		accessToken: accessTokenString,
 		url:         dingTalkAPIURL,
-		title:       config["title"],
-		secret:      config["secret"],
+		title:       fmt.Sprint(config["title"]),
+		secret:      fmt.Sprint(config["secret"]),
 		appCfg:      appCfg,
 	}
 }
diff --git a/alertmanager/discord/discord.go b/alertmanager/discord/discord.go
index 9b3fc16..de88cc1 100644
--- a/alertmanager/discord/discord.go
+++ b/alertmanager/discord/discord.go
@@ -1,6 +1,7 @@
 package discord
 
 import (
+	"fmt"
 	"strings"
 
 	"github.com/abahmed/kwatch/config"
@@ -27,19 +28,20 @@ type Discord struct {
 }
 
 // NewDiscord returns new Discord instance
-func NewDiscord(config map[string]string, appCfg *config.App) *Discord {
+func NewDiscord(config map[string]interface{}, appCfg *config.App) *Discord {
 	webhook, ok := config["webhook"]
-	if !ok || len(webhook) == 0 {
+	webhookString := fmt.Sprint(webhook)
+	if !ok || len(webhookString) == 0 {
 		logrus.Warnf("initializing discord with empty webhook url")
 		return nil
 	}
 
-	webhookList := strings.Split(webhook, "/")
+	webhookList := strings.Split(webhookString, "/")
 	if len(webhookList) <= 1 {
 		logrus.Warnf("initializing discord with missing id or token")
 		return nil
 	}
-	logrus.Infof("initializing discord with webhook url: %s", webhook)
+	logrus.Infof("initializing discord with webhook url: %s", webhookString)
 
 	webhookToken := webhookList[len(webhookList)-1]
 	webhookID := webhookList[len(webhookList)-2]
@@ -49,8 +51,8 @@ func NewDiscord(config map[string]string, appCfg *config.App) *Discord {
 	return &Discord{
 		id:     webhookID,
 		token:  webhookToken,
-		title:  config["title"],
-		text:   config["text"],
+		title:  fmt.Sprint(config["title"]),
+		text:   fmt.Sprint(config["text"]),
 		send:   discordClient.WebhookExecute,
 		appCfg: appCfg,
 	}
diff --git a/alertmanager/email/email.go b/alertmanager/email/email.go
index 9f38ef8..738b5f4 100644
--- a/alertmanager/email/email.go
+++ b/alertmanager/email/email.go
@@ -23,37 +23,42 @@ type Email struct {
 }
 
 // NewEmail returns new email instance
-func NewEmail(config map[string]string, appCfg *config.App) *Email {
+func NewEmail(config map[string]interface{}, appCfg *config.App) *Email {
 	from, ok := config["from"]
-	if !ok || len(from) == 0 {
+	fromString := fmt.Sprint(from)
+	if !ok || len(fromString) == 0 {
 		logrus.Warnf("initializing email with an empty from")
 		return nil
 	}
 
 	to, ok := config["to"]
-	if !ok || len(to) == 0 {
+	toString := fmt.Sprint(to)
+	if !ok || len(toString) == 0 {
 		logrus.Warnf("initializing email with an empty to")
 		return nil
 	}
 
 	password, ok := config["password"]
-	if !ok || len(password) == 0 {
+	passwordString := fmt.Sprint(password)
+	if !ok || len(passwordString) == 0 {
 		logrus.Warnf("initializing email with an empty password")
 		return nil
 	}
 
 	host, ok := config["host"]
-	if !ok || len(host) == 0 {
+	hostString := fmt.Sprint(host)
+	if !ok || len(hostString) == 0 {
 		logrus.Warnf("initializing email with an empty host")
 		return nil
 	}
 
 	port, ok := config["port"]
-	if !ok || len(port) == 0 {
+	portString := fmt.Sprint(port)
+	if !ok || len(portString) == 0 {
 		logrus.Warnf("initializing email with an empty port number")
 		return nil
 	}
-	portNumber, err := strconv.Atoi(port)
+	portNumber, err := strconv.Atoi(portString)
 	if err != nil {
 		logrus.Warnf("initializing email with an invalid port number: %s", err)
 		return nil
@@ -64,11 +69,11 @@ func NewEmail(config map[string]string, appCfg *config.App) *Email {
 		return nil
 	}
 
-	d := gomail.NewDialer(host, portNumber, from, password)
+	d := gomail.NewDialer(hostString, portNumber, fromString, passwordString)
 
 	return &Email{
-		from:   from,
-		to:     to,
+		from:   fromString,
+		to:     toString,
 		send:   d.DialAndSend,
 		appCfg: appCfg,
 	}
diff --git a/alertmanager/feishu/feishu.go b/alertmanager/feishu/feishu.go
index 73748f4..4d57d4f 100644
--- a/alertmanager/feishu/feishu.go
+++ b/alertmanager/feishu/feishu.go
@@ -26,20 +26,28 @@ type feiShuWebhookContent struct {
 }
 
 // NewFeiShu returns new feishu web bot instance
-func NewFeiShu(config map[string]string, appCfg *config.App) *FeiShu {
+func NewFeiShu(config map[string]interface{}, appCfg *config.App) *FeiShu {
 	webhook, ok := config["webhook"]
-	if !ok || len(webhook) == 0 {
+	webhookString := fmt.Sprint(webhook)
+	if !ok || len(webhookString) == 0 {
 		logrus.Warnf("initializing Fei Shu with empty webhook url")
 		return nil
 	}
 
-	logrus.Infof("initializing Fei Shu with webhook url: %s", webhook)
+	logrus.Infof("initializing Fei Shu with webhook url: %s", webhookString)
+
+	title, ok := config["title"]
+	titleFormatted := fmt.Sprint(title)
+	if !ok {
+		titleFormatted = ""
+	}
 
 	return &FeiShu{
-		webhook: webhook,
-		title:   config["title"],
+		webhook: webhookString,
+		title:   titleFormatted,
 		appCfg:  appCfg,
 	}
+
 }
 
 // Name returns name of the provider
diff --git a/alertmanager/matrix/matrix.go b/alertmanager/matrix/matrix.go
index 806a8b5..5cee7ca 100644
--- a/alertmanager/matrix/matrix.go
+++ b/alertmanager/matrix/matrix.go
@@ -26,31 +26,34 @@ type Matrix struct {
 }
 
 // NewMatrix returns new Matrix instance
-func NewMatrix(config map[string]string, appCfg *config.App) *Matrix {
+func NewMatrix(config map[string]interface{}, appCfg *config.App) *Matrix {
 	homeServer, ok := config["homeServer"]
-	if !ok || len(homeServer) == 0 {
+	homeServerString := fmt.Sprint(homeServer)
+	if !ok || len(homeServerString) == 0 {
 		logrus.Warnf("initializing slack with empty homeServer")
 		return nil
 	}
 
 	accessToken, ok := config["accessToken"]
-	if !ok || len(accessToken) == 0 {
+	accessTokenString := fmt.Sprint(accessToken)
+	if !ok || len(accessTokenString) == 0 {
 		logrus.Warnf("initializing slack with empty accessToken")
 		return nil
 	}
 
 	internalRoomID, ok := config["internalRoomId"]
-	if !ok || len(internalRoomID) == 0 {
+	internalRoomIDString := fmt.Sprint(internalRoomID)
+	if !ok || len(internalRoomIDString) == 0 {
 		logrus.Warnf("initializing slack with empty internalRoomId")
 		return nil
 	}
 
 	return &Matrix{
-		homeServer:     homeServer,
-		accessToken:    accessToken,
-		internalRoomID: internalRoomID,
-		title:          config["title"],
-		text:           config["text"],
+		homeServer:     homeServerString,
+		accessToken:    accessTokenString,
+		internalRoomID: internalRoomIDString,
+		title:          fmt.Sprint(config["title"]),
+		text:           fmt.Sprint(config["text"]),
 		appCfg:         appCfg,
 	}
 }
diff --git a/alertmanager/mattermost/mattermost.go b/alertmanager/mattermost/mattermost.go
index 755c64e..7bdcfb0 100644
--- a/alertmanager/mattermost/mattermost.go
+++ b/alertmanager/mattermost/mattermost.go
@@ -41,19 +41,20 @@ type mmPayload struct {
 }
 
 // NewMattermost returns new mattermost instance
-func NewMattermost(config map[string]string, appCfg *config.App) *Mattermost {
+func NewMattermost(config map[string]interface{}, appCfg *config.App) *Mattermost {
 	webhook, ok := config["webhook"]
-	if !ok || len(webhook) == 0 {
+	webhookString := fmt.Sprint(webhook)
+	if !ok || len(webhookString) == 0 {
 		logrus.Warnf("initializing mattermost with empty webhook url")
 		return nil
 	}
 
-	logrus.Infof("initializing mattermost with webhook url: %s", webhook)
+	logrus.Infof("initializing mattermost with webhook url: %s", webhookString)
 
 	return &Mattermost{
-		webhook: webhook,
-		title:   config["title"],
-		text:    config["text"],
+		webhook: webhookString,
+		title:   fmt.Sprint(config["title"]),
+		text:    fmt.Sprint(config["text"]),
 		appCfg:  appCfg,
 	}
 }
diff --git a/alertmanager/opsgenie/opsgenie.go b/alertmanager/opsgenie/opsgenie.go
index 9d91c02..73f6f01 100644
--- a/alertmanager/opsgenie/opsgenie.go
+++ b/alertmanager/opsgenie/opsgenie.go
@@ -37,9 +37,10 @@ type ogPayload struct {
 }
 
 // NewOpsgenie returns new opsgenie instance
-func NewOpsgenie(config map[string]string, appCfg *config.App) *Opsgenie {
+func NewOpsgenie(config map[string]interface{}, appCfg *config.App) *Opsgenie {
 	apiKey, ok := config["apiKey"]
-	if !ok || len(apiKey) == 0 {
+	apiKeyString := fmt.Sprint(apiKey)
+	if !ok || len(apiKeyString) == 0 {
 		logrus.Warnf("initializing opsgenie with empty webhook url")
 		return nil
 	}
@@ -47,10 +48,10 @@ func NewOpsgenie(config map[string]string, appCfg *config.App) *Opsgenie {
 	logrus.Infof("initializing opsgenie with secret apikey")
 
 	return &Opsgenie{
-		apikey: apiKey,
+		apikey: apiKeyString,
 		url:    opsgenieAPIURL,
-		title:  config["title"],
-		text:   config["text"],
+		title:  fmt.Sprint(config["title"]),
+		text:   fmt.Sprint(config["text"]),
 		appCfg: appCfg,
 	}
 }
diff --git a/alertmanager/pagerduty/pagerduty.go b/alertmanager/pagerduty/pagerduty.go
index 779279b..f3fe6c3 100644
--- a/alertmanager/pagerduty/pagerduty.go
+++ b/alertmanager/pagerduty/pagerduty.go
@@ -26,9 +26,10 @@ type Pagerduty struct {
 }
 
 // NewPagerDuty returns new PagerDuty instance
-func NewPagerDuty(config map[string]string, appCfg *config.App) *Pagerduty {
+func NewPagerDuty(config map[string]interface{}, appCfg *config.App) *Pagerduty {
 	integrationKey, ok := config["integrationKey"]
-	if !ok || len(integrationKey) == 0 {
+	integrationKeyString := fmt.Sprint(integrationKey)
+	if !ok || len(integrationKeyString) == 0 {
 		logrus.Warnf("initializing pagerduty with an empty integration key")
 		return nil
 	}
@@ -36,7 +37,7 @@ func NewPagerDuty(config map[string]string, appCfg *config.App) *Pagerduty {
 	logrus.Infof("initializing pagerduty with the provided integration key")
 
 	return &Pagerduty{
-		integrationKey: integrationKey,
+		integrationKey: integrationKeyString,
 		url:            pagerdutyAPIURL,
 		appCfg:         appCfg,
 	}
diff --git a/alertmanager/rocketchat/rocketchat.go b/alertmanager/rocketchat/rocketchat.go
index 0686a64..e7cd228 100644
--- a/alertmanager/rocketchat/rocketchat.go
+++ b/alertmanager/rocketchat/rocketchat.go
@@ -25,18 +25,19 @@ type rocketChatWebhookPayload struct {
 }
 
 // NewRocketChat returns new rocket chat instance
-func NewRocketChat(config map[string]string, appCfg *config.App) *RocketChat {
+func NewRocketChat(config map[string]interface{}, appCfg *config.App) *RocketChat {
 	webhook, ok := config["webhook"]
-	if !ok || len(webhook) == 0 {
+	webhookString := fmt.Sprint(webhook)
+	if !ok || len(webhookString) == 0 {
 		logrus.Warnf("initializing Rocket Chat with empty webhook url")
 		return nil
 	}
 
-	logrus.Infof("initializing Rocket Chat with webhook url: %s", webhook)
+	logrus.Infof("initializing Rocket Chat with webhook url: %s", webhookString)
 
 	return &RocketChat{
-		webhook: webhook,
-		text:    config["text"],
+		webhook: webhookString,
+		text:    fmt.Sprint(config["text"]),
 		appCfg:  appCfg,
 	}
 }
diff --git a/alertmanager/slack/slack.go b/alertmanager/slack/slack.go
index 10a06a2..b4d2818 100644
--- a/alertmanager/slack/slack.go
+++ b/alertmanager/slack/slack.go
@@ -32,20 +32,21 @@ type Slack struct {
 }
 
 // NewSlack returns new Slack instance
-func NewSlack(config map[string]string, appCfg *config.App) *Slack {
+func NewSlack(config map[string]interface{}, appCfg *config.App) *Slack {
 	webhook, ok := config["webhook"]
-	if !ok || len(webhook) == 0 {
+	webhookString := fmt.Sprint(webhook)
+	if !ok || len(webhookString) == 0 {
 		logrus.Warnf("initializing slack with empty webhook url")
 		return nil
 	}
 
-	logrus.Infof("initializing slack with webhook url: %s", webhook)
+	logrus.Infof("initializing slack with webhook url: %s", webhookString)
 
 	return &Slack{
-		webhook: webhook,
-		channel: config["channel"],
-		title:   config["title"],
-		text:    config["text"],
+		webhook: webhookString,
+		channel: fmt.Sprint(config["channel"]),
+		title:   fmt.Sprint(config["title"]),
+		text:    fmt.Sprint(config["text"]),
 		send:    slackClient.PostWebhook,
 		appCfg:  appCfg,
 	}
@@ -58,7 +59,7 @@ func (s *Slack) Name() string {
 
 // SendEvent sends event to the provider
 func (s *Slack) SendEvent(ev *event.Event) error {
-	logrus.Debugf("sending to slack event: %v", ev)
+	logrus.Infof("sending to slack event: %v", ev)
 
 	// use custom title if it's provided, otherwise use default
 	title := s.title
diff --git a/alertmanager/teams/teams.go b/alertmanager/teams/teams.go
index ee715a1..db5d528 100644
--- a/alertmanager/teams/teams.go
+++ b/alertmanager/teams/teams.go
@@ -32,20 +32,21 @@ type teamsWebhookPayload struct {
 }
 
 // NewTeams returns new team instance
-func NewTeams(config map[string]string, appCfg *config.App) *Teams {
+func NewTeams(config map[string]interface{}, appCfg *config.App) *Teams {
 	webhook, ok := config["webhook"]
+	webhookString := fmt.Sprint(webhook)
 
-	if !ok || len(webhook) == 0 {
+	if !ok || len(webhookString) == 0 {
 		logrus.Warnf("initializing Teams with empty webhook url")
 		return nil
 	}
 
-	logrus.Infof("initializing Teams with webhook url: %s", webhook)
+	logrus.Infof("initializing Teams with webhook url: %s", webhookString)
 
 	return &Teams{
-		webhook: webhook,
-		title:   config["title"],
-		text:    config["text"],
+		webhook: webhookString,
+		title:   fmt.Sprint(config["title"]),
+		text:    fmt.Sprint(config["text"]),
 		appCfg:  appCfg,
 	}
 }
diff --git a/alertmanager/telegram/telegram.go b/alertmanager/telegram/telegram.go
index 4f1b43c..13bd13c 100644
--- a/alertmanager/telegram/telegram.go
+++ b/alertmanager/telegram/telegram.go
@@ -25,28 +25,30 @@ type Telegram struct {
 }
 
 // NewTelegram returns a new Telegram object
-func NewTelegram(config map[string]string, appCfg *config.App) *Telegram {
+func NewTelegram(config map[string]interface{}, appCfg *config.App) *Telegram {
 	token, ok := config["token"]
-	if !ok || len(token) == 0 {
+	tokenString := fmt.Sprint(token)
+	if !ok || len(tokenString) == 0 {
 		logrus.Warnf("initializing telegram with empty token")
 		return nil
 	}
 
 	chatId, ok := config["chatId"]
-	if !ok || len(token) == 0 {
+	chatIdString := fmt.Sprint(chatId)
+	if !ok || len(chatIdString) == 0 {
 		logrus.Warnf("initializing telegram with empty chat_id")
 		return nil
 	}
 
 	logrus.Infof(
 		"initializing telegram with token  %s and chat_id %s",
-		token,
-		chatId)
+		tokenString,
+		chatIdString)
 
 	// returns a new telegram object
 	return &Telegram{
-		token:  token,
-		chatId: chatId,
+		token:  tokenString,
+		chatId: chatIdString,
 		url:    telegramAPIURL,
 		appCfg: appCfg,
 	}
diff --git a/alertmanager/webhook/webhook.go b/alertmanager/webhook/webhook.go
new file mode 100644
index 0000000..a1ac6d2
--- /dev/null
+++ b/alertmanager/webhook/webhook.go
@@ -0,0 +1,145 @@
+package webhook
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"github.com/abahmed/kwatch/config"
+	"github.com/abahmed/kwatch/event"
+	"github.com/abahmed/kwatch/util"
+	"net/http"
+	"strings"
+
+	"github.com/sirupsen/logrus"
+)
+
+type KeyValue struct {
+	Name  string `json:"name"`
+	Value string `json:"value"`
+}
+
+type Authentication struct {
+	UserName string `json:"username"`
+	Password string `json:"password"`
+}
+
+type Webhook struct {
+	webhook  string
+	headers  []KeyValue
+	username string
+	password string
+	appCfg   *config.App
+}
+
+func (w *Webhook) SendMessage(msg string) error {
+	return nil
+}
+
+// NewSlack returns new Slack instance
+func NewWebhook(config map[string]interface{}, appCfg *config.App) *Webhook {
+	url, ok := config["url"]
+	urlString := fmt.Sprint(url)
+	if !ok || len(urlString) == 0 {
+		logrus.Warnf("initializing  with empty webhook url")
+		return nil
+	}
+	rawHeaders, ok := config["headers"]
+	var headers []KeyValue
+	if ok {
+		headerArray := rawHeaders.([]interface{})
+		for _, header := range headerArray {
+			headerJson, _ := json.Marshal(header)
+			var k KeyValue
+			json.Unmarshal(headerJson, &k)
+			headers = append(headers, k)
+		}
+	}
+
+	basicAuth, ok := config["basicAuth"]
+	basicAuthJson, _ := json.Marshal(basicAuth)
+
+	var a Authentication
+	json.Unmarshal(basicAuthJson, &a)
+
+	userName := fmt.Sprint(a.UserName)
+	password := fmt.Sprint(a.Password)
+
+	logrus.Infof("initializing  with webhook url: %s with headers: %s and username: %s password: %s", url, headers, userName, password)
+
+	return &Webhook{
+		webhook:  urlString,
+		headers:  headers,
+		username: userName,
+		password: password,
+		appCfg:   appCfg,
+	}
+}
+
+// Name returns name of the provider
+func (w *Webhook) Name() string {
+	return "Webhook"
+}
+
+// SendEvent sends event to the provider
+func (w *Webhook) SendEvent(ev *event.Event) error {
+	client := &http.Client{}
+
+	reqBody := w.buildRequestBody(ev)
+	buffer := bytes.NewBuffer(reqBody)
+
+	request, err := http.NewRequest(http.MethodPost, w.webhook, buffer)
+	if err != nil {
+		return err
+	}
+
+	for _, header := range w.headers {
+		request.Header.Set(header.Name, header.Value)
+	}
+	request.SetBasicAuth(w.username, w.password)
+
+	response, err := client.Do(request)
+	if err != nil {
+		return err
+	}
+	defer response.Body.Close()
+
+	if response.StatusCode > 202 {
+		return fmt.Errorf(
+			"call to teams alert returned status code %d",
+			response.StatusCode)
+	}
+
+	return nil
+}
+
+func (w *Webhook) buildRequestBody(
+	ev *event.Event,
+) []byte {
+	eventsText := "No events captured"
+	logsText := "No logs captured"
+
+	// add events part if it exists
+	events := strings.TrimSpace(ev.Events)
+	if len(events) > 0 {
+		eventsText = util.JsonEscape(ev.Events)
+	}
+
+	// add logs part if it exists
+	logs := strings.TrimSpace(ev.Logs)
+	if len(logs) > 0 {
+		logsText = util.JsonEscape(ev.Logs)
+	}
+
+	postBody, _ := json.Marshal(map[string]interface{}{
+		"Cluster":   w.appCfg.ClusterName,
+		"Name":      ev.Name,
+		"Container": ev.Container,
+		"Namespace": ev.Namespace,
+		"Reason":    ev.Reason,
+		"Events":    eventsText,
+		"Logs":      logsText,
+		"Labels":    ev.Labels,
+	})
+
+	return postBody
+}
diff --git a/config/config.go b/config/config.go
index 6c85cab..9918594 100644
--- a/config/config.go
+++ b/config/config.go
@@ -36,7 +36,7 @@ type Config struct {
 
 	// Alert is a map contains a map of each provider configuration
 	// e.g. {"slack": {"webhook": "URL"}}
-	Alert map[string]map[string]string `yaml:"alert"`
+	Alert map[string]map[string]interface{} `yaml:"alert"`
 
 	// AllowedNamespaces, ForbiddenNamespaces are calculated internally
 	// after loading Namespaces configuration
diff --git a/controller/controller.go b/controller/controller.go
index 322304e..b7f87f2 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -246,6 +246,7 @@ func (c *Controller) processPod(key string, pod *v1.Pod) {
 			Reason:    reason,
 			Logs:      logs,
 			Events:    eventsString,
+			Labels:    pod.Labels,
 		}
 
 		// save container as it's reported to avoid duplication
diff --git a/event/event.go b/event/event.go
index 6227fa7..486259f 100644
--- a/event/event.go
+++ b/event/event.go
@@ -8,4 +8,5 @@ type Event struct {
 	Reason    string
 	Events    string
 	Logs      string
+	Labels    map[string]string
 }


Test patch:
diff --git a/alertmanager/alertmanager_test.go b/alertmanager/alertmanager_test.go
index 06259aa..f1afc14 100644
--- a/alertmanager/alertmanager_test.go
+++ b/alertmanager/alertmanager_test.go
@@ -42,7 +42,7 @@ func TestAlertManagerNoConfig(t *testing.T) {
 func TestGetProviders(t *testing.T) {
 	assert := assert.New(t)
 
-	alertMap := map[string]map[string]string{
+	alertMap := map[string]map[string]interface{}{
 		"slack": {
 			"webhook": "test",
 		},
diff --git a/alertmanager/dingtalk/dingtalk_test.go b/alertmanager/dingtalk/dingtalk_test.go
index 88d5413..5e60fe9 100644
--- a/alertmanager/dingtalk/dingtalk_test.go
+++ b/alertmanager/dingtalk/dingtalk_test.go
@@ -13,14 +13,14 @@ import (
 func TestEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewDingTalk(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewDingTalk(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestDingTalk(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c := NewDingTalk(configMap, &config.App{ClusterName: "dev"})
@@ -39,7 +39,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 		"secret":      "secret1",
 	}
@@ -60,7 +60,7 @@ func TestSendMessageInvalidBody(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c := NewDingTalk(configMap, &config.App{ClusterName: "dev"})
@@ -80,7 +80,7 @@ func TestSendMessageInvalidJson(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c := NewDingTalk(configMap, &config.App{ClusterName: "dev"})
@@ -100,7 +100,7 @@ func TestSendMessageErrorResponse(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c := NewDingTalk(configMap, &config.App{ClusterName: "dev"})
@@ -120,7 +120,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c := NewDingTalk(configMap, &config.App{ClusterName: "dev"})
@@ -142,7 +142,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c := NewDingTalk(configMap, &config.App{ClusterName: "dev"})
@@ -151,7 +151,7 @@ func TestInvaildHttpRequest(t *testing.T) {
 
 	assert.NotNil(c.SendMessage("test"))
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"accessToken": "testToken",
 	}
 	c = NewDingTalk(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/discord/discord_test.go b/alertmanager/discord/discord_test.go
index fe30cdd..0e5ea17 100644
--- a/alertmanager/discord/discord_test.go
+++ b/alertmanager/discord/discord_test.go
@@ -21,14 +21,14 @@ func mockedSend(
 func TestDiscordEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewDiscord(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewDiscord(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestDiscordInvalidConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "testtest",
 	}
 	c := NewDiscord(configMap, &config.App{ClusterName: "dev"})
@@ -38,7 +38,7 @@ func TestDiscordInvalidConfig(t *testing.T) {
 func TestDiscord(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "test/test",
 	}
 	c := NewDiscord(configMap, &config.App{ClusterName: "dev"})
@@ -50,7 +50,7 @@ func TestDiscord(t *testing.T) {
 func TestSendMessage(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "test/test",
 	}
 	c := NewDiscord(configMap, &config.App{ClusterName: "dev"})
@@ -63,7 +63,7 @@ func TestSendMessage(t *testing.T) {
 func TestSendEvent(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "test/test",
 	}
 	c := NewDiscord(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/email/email_test.go b/alertmanager/email/email_test.go
index 1b2c684..c79b941 100644
--- a/alertmanager/email/email_test.go
+++ b/alertmanager/email/email_test.go
@@ -16,27 +16,27 @@ func mockedSend(m ...*gomail.Message) error {
 func TestEmailEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewEmail(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewEmail(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestEmailInvalidConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"from": "test@test.com",
 	}
 	c := NewEmail(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"from": "test@test.com",
 		"to":   "test12@test.com",
 	}
 	c = NewEmail(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
@@ -44,7 +44,7 @@ func TestEmailInvalidConfig(t *testing.T) {
 	c = NewEmail(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
@@ -53,7 +53,7 @@ func TestEmailInvalidConfig(t *testing.T) {
 	c = NewEmail(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
@@ -63,7 +63,7 @@ func TestEmailInvalidConfig(t *testing.T) {
 	c = NewEmail(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
@@ -77,7 +77,7 @@ func TestEmailInvalidConfig(t *testing.T) {
 func TestEmail(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
@@ -93,7 +93,7 @@ func TestEmail(t *testing.T) {
 func TestSendMessage(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
@@ -110,7 +110,7 @@ func TestSendMessage(t *testing.T) {
 func TestSendEvent(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"from":     "test@test.com",
 		"to":       "test12@test.com",
 		"password": "testPassword",
diff --git a/alertmanager/feishu/feishu_test.go b/alertmanager/feishu/feishu_test.go
index 6280357..0393be3 100644
--- a/alertmanager/feishu/feishu_test.go
+++ b/alertmanager/feishu/feishu_test.go
@@ -13,14 +13,14 @@ import (
 func TestEmptyConfig(t *testing.T) {
 	assertions := assert.New(t)
 
-	c := NewFeiShu(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewFeiShu(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assertions.Nil(c)
 }
 
 func TestRocketChat(t *testing.T) {
 	assertions := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "testtest",
 	}
 	c := NewFeiShu(configMap, &config.App{ClusterName: "dev"})
@@ -38,7 +38,7 @@ func TestBuildRequestBodyFeiShu(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewFeiShu(configMap, &config.App{ClusterName: "dev"})
@@ -68,7 +68,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewFeiShu(configMap, &config.App{ClusterName: "dev"})
@@ -87,7 +87,7 @@ func TestSendMessageError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewFeiShu(configMap, &config.App{ClusterName: "dev"})
@@ -106,7 +106,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewFeiShu(configMap, &config.App{ClusterName: "dev"})
@@ -127,7 +127,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvalidHttpRequest(t *testing.T) {
 	assertions := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "h ttp://localhost",
 	}
 	c := NewFeiShu(configMap, &config.App{ClusterName: "dev"})
@@ -135,7 +135,7 @@ func TestInvalidHttpRequest(t *testing.T) {
 
 	assertions.NotNil(c.SendMessage("test"))
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"webhook": "http://localhost:132323",
 	}
 	c = NewFeiShu(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/matrix/matrix_test.go b/alertmanager/matrix/matrix_test.go
index 5972586..26e9c93 100644
--- a/alertmanager/matrix/matrix_test.go
+++ b/alertmanager/matrix/matrix_test.go
@@ -13,27 +13,27 @@ import (
 func TestEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewMatrix(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewMatrix(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestInvalidConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"homeServer": "https://matrix-client.matrix.org",
 	}
 	c := NewMatrix(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"homeServer":  "https://matrix-client.matrix.org",
 		"accessToken": "testToken",
 	}
 	c = NewMatrix(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"homeServer":     "https://matrix-client.matrix.org",
 		"accessToken":    "testToken",
 		"internalRoomId": "",
@@ -46,7 +46,7 @@ func TestInvalidConfig(t *testing.T) {
 func TestMatrix(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"homeServer":     "https://matrix-client.matrix.org",
 		"accessToken":    "testToken",
 		"internalRoomId": "room1",
@@ -67,7 +67,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"homeServer":     s.URL,
 		"accessToken":    "testToken",
 		"internalRoomId": "room1",
@@ -88,7 +88,7 @@ func TestSendMessageError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"homeServer":     s.URL,
 		"accessToken":    "testToken",
 		"internalRoomId": "room1",
@@ -109,7 +109,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"homeServer":     s.URL,
 		"accessToken":    "testToken",
 		"internalRoomId": "room1",
@@ -132,7 +132,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"homeServer":     "h ttp://localhost",
 		"accessToken":    "testToken",
 		"internalRoomId": "room1",
@@ -142,7 +142,7 @@ func TestInvaildHttpRequest(t *testing.T) {
 
 	assert.NotNil(c.SendMessage("test"))
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"homeServer":     "http://localhost:132323",
 		"accessToken":    "testToken",
 		"internalRoomId": "room1",
diff --git a/alertmanager/mattermost/mattermost_test.go b/alertmanager/mattermost/mattermost_test.go
index 982f868..c3fb1e9 100644
--- a/alertmanager/mattermost/mattermost_test.go
+++ b/alertmanager/mattermost/mattermost_test.go
@@ -13,14 +13,14 @@ import (
 func TestMattermostEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewMattermost(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewMattermost(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestMattermost(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "testtest",
 	}
 	c := NewMattermost(configMap, &config.App{ClusterName: "dev"})
@@ -39,7 +39,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewMattermost(configMap, &config.App{ClusterName: "dev"})
@@ -58,7 +58,7 @@ func TestSendMessageError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewMattermost(configMap, &config.App{ClusterName: "dev"})
@@ -77,7 +77,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewMattermost(configMap, &config.App{ClusterName: "dev"})
@@ -98,7 +98,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "h ttp://localhost",
 	}
 	c := NewMattermost(configMap, &config.App{ClusterName: "dev"})
@@ -106,7 +106,7 @@ func TestInvaildHttpRequest(t *testing.T) {
 
 	assert.NotNil(c.SendMessage("test"))
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"webhook": "http://localhost:132323",
 	}
 	c = NewMattermost(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/opsgenie/opsgenie_test.go b/alertmanager/opsgenie/opsgenie_test.go
index 0751b43..259ecf5 100644
--- a/alertmanager/opsgenie/opsgenie_test.go
+++ b/alertmanager/opsgenie/opsgenie_test.go
@@ -13,14 +13,14 @@ import (
 func TestOpsgenieEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewOpsgenie(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewOpsgenie(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestOpsgenie(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"apiKey": "testtest",
 	}
 	c := NewOpsgenie(configMap, &config.App{ClusterName: "dev"})
@@ -32,7 +32,7 @@ func TestOpsgenie(t *testing.T) {
 func TestSendMessage(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"apiKey": "test",
 	}
 	c := NewOpsgenie(configMap, &config.App{ClusterName: "dev"})
@@ -52,7 +52,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"apiKey": "test",
 	}
 	c := NewOpsgenie(configMap, &config.App{ClusterName: "dev"})
@@ -82,7 +82,7 @@ func TestSendEventError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"apiKey": "test",
 	}
 	c := NewOpsgenie(configMap, &config.App{ClusterName: "dev"})
@@ -105,7 +105,7 @@ func TestSendEventError(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"apiKey": "test",
 	}
 	c := NewOpsgenie(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/pagerduty/pagerduty_test.go b/alertmanager/pagerduty/pagerduty_test.go
index 881e112..ebba3c2 100644
--- a/alertmanager/pagerduty/pagerduty_test.go
+++ b/alertmanager/pagerduty/pagerduty_test.go
@@ -13,14 +13,14 @@ import (
 func TestPagerdutyEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewPagerDuty(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewPagerDuty(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestPagerduty(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"integrationKey": "testtest",
 	}
 	c := NewPagerDuty(configMap, &config.App{ClusterName: "dev"})
@@ -32,7 +32,7 @@ func TestPagerduty(t *testing.T) {
 func TestSendMessage(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"integrationKey": "test",
 	}
 	c := NewPagerDuty(configMap, &config.App{ClusterName: "dev"})
@@ -51,7 +51,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"integrationKey": "test",
 	}
 	c := NewPagerDuty(configMap, &config.App{ClusterName: "dev"})
@@ -80,7 +80,7 @@ func TestSendEventError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"integrationKey": "test",
 	}
 	c := NewPagerDuty(configMap, &config.App{ClusterName: "dev"})
@@ -102,7 +102,7 @@ func TestSendEventError(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"integrationKey": "test",
 	}
 	c := NewPagerDuty(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/rocketchat/rocketchat_test.go b/alertmanager/rocketchat/rocketchat_test.go
index d965e9a..68ebdca 100644
--- a/alertmanager/rocketchat/rocketchat_test.go
+++ b/alertmanager/rocketchat/rocketchat_test.go
@@ -13,14 +13,14 @@ import (
 func TestEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewRocketChat(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewRocketChat(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestRocketChat(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "testtest",
 	}
 	c := NewRocketChat(configMap, &config.App{ClusterName: "dev"})
@@ -39,7 +39,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewRocketChat(configMap, &config.App{ClusterName: "dev"})
@@ -58,7 +58,7 @@ func TestSendMessageError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewRocketChat(configMap, &config.App{ClusterName: "dev"})
@@ -77,7 +77,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewRocketChat(configMap, &config.App{ClusterName: "dev"})
@@ -98,7 +98,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "h ttp://localhost",
 	}
 	c := NewRocketChat(configMap, &config.App{ClusterName: "dev"})
@@ -106,7 +106,7 @@ func TestInvaildHttpRequest(t *testing.T) {
 
 	assert.NotNil(c.SendMessage("test"))
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"webhook": "http://localhost:132323",
 	}
 	c = NewRocketChat(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/slack/slack_test.go b/alertmanager/slack/slack_test.go
index 661f6e1..e4934c8 100644
--- a/alertmanager/slack/slack_test.go
+++ b/alertmanager/slack/slack_test.go
@@ -15,14 +15,14 @@ func mockedSend(url string, msg *slackClient.WebhookMessage) error {
 func TestSlackEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	s := NewSlack(map[string]string{}, &config.App{ClusterName: "dev"})
+	s := NewSlack(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(s)
 }
 
 func TestSlack(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "testtest",
 	}
 	s := NewSlack(configMap, &config.App{ClusterName: "dev"})
@@ -34,7 +34,7 @@ func TestSlack(t *testing.T) {
 func TestSendMessage(t *testing.T) {
 	assert := assert.New(t)
 
-	s := NewSlack(map[string]string{
+	s := NewSlack(map[string]interface{}{
 		"webhook": "testtest",
 		"channel": "test",
 	}, &config.App{ClusterName: "dev"})
@@ -47,7 +47,7 @@ func TestSendMessage(t *testing.T) {
 func TestSendEvent(t *testing.T) {
 	assert := assert.New(t)
 
-	s := NewSlack(map[string]string{
+	s := NewSlack(map[string]interface{}{
 		"webhook": "testtest",
 	}, &config.App{ClusterName: "dev"})
 	assert.NotNil(s)
diff --git a/alertmanager/teams/teams_test.go b/alertmanager/teams/teams_test.go
index d9eaa8e..4550a7f 100644
--- a/alertmanager/teams/teams_test.go
+++ b/alertmanager/teams/teams_test.go
@@ -13,14 +13,14 @@ import (
 func TestEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewTeams(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewTeams(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestTeams(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "testtest",
 	}
 	c := NewTeams(configMap, &config.App{ClusterName: "dev"})
@@ -39,7 +39,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewTeams(configMap, &config.App{ClusterName: "dev"})
@@ -58,7 +58,7 @@ func TestSendMessageError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewTeams(configMap, &config.App{ClusterName: "dev"})
@@ -77,7 +77,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": s.URL,
 	}
 	c := NewTeams(configMap, &config.App{ClusterName: "dev"})
@@ -98,7 +98,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"webhook": "h ttp://localhost",
 	}
 	c := NewTeams(configMap, &config.App{ClusterName: "dev"})
@@ -106,7 +106,7 @@ func TestInvaildHttpRequest(t *testing.T) {
 
 	assert.NotNil(c.SendMessage("test"))
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"webhook": "http://localhost:132323",
 	}
 	c = NewTeams(configMap, &config.App{ClusterName: "dev"})
diff --git a/alertmanager/telegram/telegram_test.go b/alertmanager/telegram/telegram_test.go
index 1ed0ccb..0cd22c6 100644
--- a/alertmanager/telegram/telegram_test.go
+++ b/alertmanager/telegram/telegram_test.go
@@ -13,14 +13,14 @@ import (
 func TestEmptyConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	c := NewTelegram(map[string]string{}, &config.App{ClusterName: "dev"})
+	c := NewTelegram(map[string]interface{}{}, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 }
 
 func TestTelegram(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"token":  "testtest",
 		"chatId": "tessst",
 	}
@@ -33,13 +33,13 @@ func TestTelegram(t *testing.T) {
 func TestTelegramInvalidConfig(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"token": "test",
 	}
 	c := NewTelegram(configMap, &config.App{ClusterName: "dev"})
 	assert.Nil(c)
 
-	configMap = map[string]string{
+	configMap = map[string]interface{}{
 		"chatId": "test",
 	}
 	c = NewTelegram(configMap, &config.App{ClusterName: "dev"})
@@ -56,7 +56,7 @@ func TestSendMessage(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"token":  "test",
 		"chatId": "test",
 	}
@@ -77,7 +77,7 @@ func TestSendMessageError(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"token":  "test",
 		"chatId": "test",
 	}
@@ -98,7 +98,7 @@ func TestSendEvent(t *testing.T) {
 
 	defer s.Close()
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"token":  "test",
 		"chatId": "test",
 	}
@@ -121,7 +121,7 @@ func TestSendEvent(t *testing.T) {
 func TestInvaildHttpRequest(t *testing.T) {
 	assert := assert.New(t)
 
-	configMap := map[string]string{
+	configMap := map[string]interface{}{
 		"token":  "test",
 		"chatId": "test",
 	}
diff --git a/alertmanager/webhook/webhook_test.go b/alertmanager/webhook/webhook_test.go
new file mode 100644
index 0000000..f23cefe
--- /dev/null
+++ b/alertmanager/webhook/webhook_test.go
@@ -0,0 +1,124 @@
+package webhook
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/abahmed/kwatch/config"
+	"github.com/abahmed/kwatch/event"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestEmptyConfig(t *testing.T) {
+	assert := assert.New(t)
+
+	c := NewWebhook(map[string]interface{}{}, &config.App{ClusterName: "dev"})
+	assert.Nil(c)
+}
+
+func TestWebhook(t *testing.T) {
+	assert := assert.New(t)
+
+	configMap := map[string]interface{}{
+		"url": "testtest",
+	}
+	c := NewWebhook(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+
+	assert.Equal(c.Name(), "Webhook")
+}
+
+func TestSendMessage(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Write([]byte(`{"isOk": true}`))
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"url": s.URL,
+	}
+	c := NewWebhook(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+
+	assert.Nil(c.SendMessage("test"))
+}
+
+func TestSendMessageError(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.WriteHeader(http.StatusBadGateway)
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"url": s.URL,
+	}
+	c := NewWebhook(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+
+	assert.Nil(c.SendMessage("test"))
+}
+
+func TestSendEvent(t *testing.T) {
+	assert := assert.New(t)
+
+	s := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Write([]byte(`{"isOk": true}`))
+		}))
+
+	defer s.Close()
+
+	configMap := map[string]interface{}{
+		"url": s.URL,
+	}
+	c := NewWebhook(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+
+	ev := event.Event{
+		Name:      "test-pod",
+		Container: "test-container",
+		Namespace: "default",
+		Reason:    "OOMKILLED",
+		Logs:      "test\ntestlogs",
+		Events: "event1-event2-event3-event1-event2-event3-event1-event2-" +
+			"event3\nevent5\nevent6-event8-event11-event12",
+	}
+	assert.Nil(c.SendEvent(&ev))
+}
+
+func TestInvaildHttpRequest(t *testing.T) {
+	assert := assert.New(t)
+
+	configMap := map[string]interface{}{
+		"url": "h ttp://localhost",
+	}
+	c := NewWebhook(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+
+	ev := event.Event{
+		Name:      "test-pod",
+		Container: "test-container",
+		Namespace: "default",
+		Reason:    "OOMKILLED",
+		Logs:      "test\ntestlogs",
+		Events: "event1-event2-event3-event1-event2-event3-event1-event2-" +
+			"event3\nevent5\nevent6-event8-event11-event12",
+	}
+
+	assert.NotNil(assert.NotNil(c.SendEvent(&ev)))
+
+	c = NewWebhook(configMap, &config.App{ClusterName: "dev"})
+	assert.NotNil(c)
+	c.webhook = "http://localhost:132323"
+
+	assert.NotNil(assert.NotNil(c.SendEvent(&ev)))
+}