diff --git a/README.md b/README.md index c01a09d5..9aac3fa5 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,21 @@ If you want to enable Telegram, provide a valid token and the chat Id. | `alert.telegram.token` | Telegram token | | `alert.telegram.chatId` | Telegram chat id | +#### Microsoft Teams + +

+ +

+ +If you want to enable Microsoft Teams, provide the channel webhook. + +| Parameter | Description | +|:---------------------------------|:------------------------------------------------| +| `alert.teams.webhook` | webhook Microsoft team | +| `alert.teams.title` | Customized title in Microsoft teams message | +| `alert.teams.text` | Customized title in Microsoft teams message | + + ### Cleanup ```shell diff --git a/config.yaml b/config.yaml index 990b5183..d36df7ee 100644 --- a/config.yaml +++ b/config.yaml @@ -9,5 +9,7 @@ alert: telegram: token: "" chatId: "" + teams: + webhook: "" namespaces: - default \ No newline at end of file diff --git a/deploy/config.yaml b/deploy/config.yaml index ca2bd4b8..e45781d7 100644 --- a/deploy/config.yaml +++ b/deploy/config.yaml @@ -21,6 +21,8 @@ data: telegram: token: chatId: + teams: + webhook: namespaces: - diff --git a/provider/teams.go b/provider/teams.go new file mode 100644 index 00000000..29079284 --- /dev/null +++ b/provider/teams.go @@ -0,0 +1,119 @@ +package provider + +import ( + "bytes" + "fmt" + "github.com/abahmed/kwatch/event" + "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "io/ioutil" + "net/http" + "strings" +) + +const ( + defaultLogs = "No logs captured" + defaultEvents = "No events captured" + defaultTeamsTitle = "⛑ Kwatch detected a crash in pod" +) + +type teams struct { + webhook string +} + +// NewTeams returns new team instance +func NewTeams(url string) Provider { + if len(url) == 0 { + logrus.Warnf("initializing Teams with empty webhook url") + } else { + logrus.Infof("initializing Teams with webhook url: %s", url) + } + + return &teams{ + webhook: url, + } +} + +// Name returns name of the provider +func (t *teams) Name() string { + return "Microsoft Teams" +} + +// SendEvent sends event to the provider +func (t *teams) SendEvent(e *event.Event) error { + reqBody := buildRequestBodyTeams(e, t) + + return t.SendMessage(reqBody) +} + +// SendMessage sends text message to the provider +func (t *teams) SendMessage(msg string) error { + client := &http.Client{} + buffer := bytes.NewBuffer([]byte(msg)) + request, err := http.NewRequest(http.MethodPost, t.webhook, buffer) + + if err != nil { + return err + } + request.Header.Set("Content-Type", "application/json") + response, err := client.Do(request) + if err != nil { + return err + } + + if response.StatusCode != 399 { + body, _ := ioutil.ReadAll(response.Body) + return fmt.Errorf("call to teams alert returned status code %d: %msg", response.StatusCode, string(body)) + } + + if err != nil { + return err + } + + return err +} + +func buildRequestBodyTeams(e *event.Event, t *teams) string { + eventsText := defaultEvents + logsText := defaultLogs + + // add events part if it exists + events := strings.TrimSpace(e.Events) + if len(events) > 0 { + eventsText = e.Events + } + + // add logs part if it exists + logs := strings.TrimSpace(e.Logs) + if len(logs) > 0 { + logsText = e.Logs + } + // use custom title if it's provided, otherwise use default + title := viper.GetString("alert.teams.title") + if len(title) == 0 { + title = defaultTeamsTitle + } + + // use custom text if it's provided, otherwise use default + text := viper.GetString("alert.teams.text") + if len(text) == 0 { + text = defaultText + } + + msg := fmt.Sprintf( + "%s \n\n **Pod:** %s \n\n **Container:** %s \n\n **Namespace:** %s \n\n **Events:** \n\n ``` %s ``` \n\n **Logs:** \n\n ``` %s ``` ", + text, + e.Name, + e.Container, + e.Namespace, + eventsText, + logsText, + ) + reqBody := fmt.Sprintf(`{ + "title": "%s", + "text": "%s", + + }`, title, msg) + + return reqBody +} diff --git a/storage/storage.go b/storage/storage.go index f56d0a5c..82697a1e 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1,5 +1,6 @@ package storage +// Storage interface type Storage interface { AddPodContainer(podKey, containerKey string) DelPodContainer(podKey, containerKey string) diff --git a/util/util.go b/util/util.go index 78a1ed7f..6c556f68 100644 --- a/util/util.go +++ b/util/util.go @@ -117,6 +117,9 @@ func GetProviders() []provider.Provider { if key == "telegram" && c == "chatid" && len(strings.TrimSpace(v.(string))) > 0 { telegram[1] = true } + if key == "teams" && c == "webhook" && len(strings.TrimSpace(v.(string))) > 0 { + providers = append(providers, provider.NewTeams(viper.GetString("alert.teams.webhook"))) + } } if key == "telegram" && isListAllBool(true, telegram) { providers = append(providers, provider.NewTelegram(viper.GetString("alert.telegram.token"), viper.GetString("alert.telegram.chatId")))