From 88b0af1186baaa38adb44640bddf7c4b1d2d4ebf Mon Sep 17 00:00:00 2001 From: Jonathan-Eng Date: Sun, 27 Oct 2024 12:41:46 +0200 Subject: [PATCH 1/3] add support for ms teams workflows --- pkg/api/alerting.go | 2 +- .../definitions/provisioning_contactpoints.go | 2 + .../ngalert/notifier/available_channels.go | 22 +++ .../ngalert/notifier/channels/factory.go | 7 +- .../notifier/channels/teams_workflows.go | 146 ++++++++++++++++++ 5 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 pkg/services/ngalert/notifier/channels/teams_workflows.go diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 58dd69336ae0c..3f0f299f46f63 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -202,7 +202,7 @@ func (hs *HTTPServer) GetAlertNotifiers(ngalertEnabled bool) func(*models.ReqCon availableNotifier := notifier.GetAvailableNotifiers() allowedNotifiers := []alerting.NotifierPlugin{} isAllowedNotifier := func(t string) bool { - allowedTypes := []string{"slack", "email", "opsgenie", "victorops", "teams", "webhook", "pagerduty", "logzio_opsgenie", "googlechat"} // LOGZ.IO GRAFANA CHANGE :: DEV-35483 - Allow type for Opsgenie Logzio intergration + allowedTypes := []string{"slack", "email", "opsgenie", "victorops", "teams", "webhook", "pagerduty", "logzio_opsgenie", "googlechat", "teams_workflows"} // LOGZ.IO GRAFANA CHANGE :: DEV-35483 - Allow type for Opsgenie Logzio intergration isAllowedNotifier := false for _, allowedType := range allowedTypes { if allowedType == t { diff --git a/pkg/services/ngalert/api/tooling/definitions/provisioning_contactpoints.go b/pkg/services/ngalert/api/tooling/definitions/provisioning_contactpoints.go index 896c3dfe47e30..95dc98afb965c 100644 --- a/pkg/services/ngalert/api/tooling/definitions/provisioning_contactpoints.go +++ b/pkg/services/ngalert/api/tooling/definitions/provisioning_contactpoints.go @@ -137,6 +137,8 @@ func (e *EmbeddedContactPoint) SecretKeys() ([]string, error) { return []string{"url", "token"}, nil case "teams": return []string{}, nil + case "teams_workflows": + return []string{}, nil case "telegram": return []string{"bottoken"}, nil case "threema": diff --git a/pkg/services/ngalert/notifier/available_channels.go b/pkg/services/ngalert/notifier/available_channels.go index a219e94979a69..e11d2a018976f 100644 --- a/pkg/services/ngalert/notifier/available_channels.go +++ b/pkg/services/ngalert/notifier/available_channels.go @@ -572,6 +572,28 @@ func GetAvailableNotifiers() []*alerting.NotifierPlugin { }, }, }, + { + Type: "teams_workflows", + Name: "Microsoft Teams Workflows", + Description: "Sends notifications using Incoming Webhook connector to Microsoft Teams Workflows", + Heading: "Teams Workflows settings", + Options: []alerting.NotifierOption{ + { + Label: "URL", + Element: alerting.ElementTypeInput, + InputType: alerting.InputTypeText, + Placeholder: "Teams Workflows incoming webhook url", + PropertyName: "url", + Required: true, + }, + { // New in 8.0. + Label: "Message", + Element: alerting.ElementTypeTextArea, + Placeholder: `{{ template "default.message" . }}`, + PropertyName: "message", + }, + }, + }, { Type: "telegram", Name: "Telegram", diff --git a/pkg/services/ngalert/notifier/channels/factory.go b/pkg/services/ngalert/notifier/channels/factory.go index 005bbad352389..eade7129f3146 100644 --- a/pkg/services/ngalert/notifier/channels/factory.go +++ b/pkg/services/ngalert/notifier/channels/factory.go @@ -35,7 +35,7 @@ func NewFactoryConfig(config *NotificationChannelConfig, notificationService not }, nil } -//LOGZ.IO GRAFANA CHANGE :: DEV-32721 - Remove upsupported contact points +// LOGZ.IO GRAFANA CHANGE :: DEV-32721 - Remove upsupported contact points var receiverFactories = map[string]func(FactoryConfig) (NotificationChannel, error){ //"prometheus-alertmanager": AlertmanagerFactory, //"dingding": DingDingFactory, @@ -49,8 +49,9 @@ var receiverFactories = map[string]func(FactoryConfig) (NotificationChannel, err "pagerduty": PagerdutyFactory, //"pushover": PushoverFactory, //"sensugo": SensuGoFactory, - "slack": SlackFactory, - "teams": TeamsFactory, + "slack": SlackFactory, + "teams": TeamsFactory, + "teams_workflows": TeamsWorkflowsFactory, //"telegram": TelegramFactory, //"threema": ThreemaFactory, "victorops": VictorOpsFactory, diff --git a/pkg/services/ngalert/notifier/channels/teams_workflows.go b/pkg/services/ngalert/notifier/channels/teams_workflows.go new file mode 100644 index 0000000000000..ce5a21790641b --- /dev/null +++ b/pkg/services/ngalert/notifier/channels/teams_workflows.go @@ -0,0 +1,146 @@ +package channels + +import ( + "context" + "encoding/json" + "github.com/pkg/errors" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" + + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/notifications" +) + +// TeamsWorkflowsNotifier is responsible for sending +// alert notifications to Microsoft teams with workflows. +type TeamsWorkflowsNotifier struct { + *Base + URL string + Message string + tmpl *template.Template + log log.Logger + ns notifications.WebhookSender +} + +type TeamsWorkflowsConfig struct { + *NotificationChannelConfig + URL string + Message string +} + +func TeamsWorkflowsFactory(fc FactoryConfig) (NotificationChannel, error) { + cfg, err := NewTeamsWorkflowsConfig(fc.Config) + if err != nil { + return nil, receiverInitError{ + Reason: err.Error(), + Cfg: *fc.Config, + } + } + return NewTeamsWorkflowsNotifier(cfg, fc.NotificationService, fc.Template), nil +} + +func NewTeamsWorkflowsConfig(config *NotificationChannelConfig) (*TeamsWorkflowsConfig, error) { + URL := config.Settings.Get("url").MustString() + if URL == "" { + return nil, errors.New("could not find url property in settings") + } + return &TeamsWorkflowsConfig{ + NotificationChannelConfig: config, + URL: URL, + Message: config.Settings.Get("message").MustString(`{{ template "teams.default.message" .}}`), + }, nil +} + +// NewTeamsWorkflowsNotifier is the constructor for TeamsWorkflows notifier. +func NewTeamsWorkflowsNotifier(config *TeamsWorkflowsConfig, ns notifications.WebhookSender, t *template.Template) *TeamsWorkflowsNotifier { + return &TeamsWorkflowsNotifier{ + Base: NewBase(&models.AlertNotification{ + Uid: config.UID, + Name: config.Name, + Type: config.Type, + DisableResolveMessage: config.DisableResolveMessage, + Settings: config.Settings, + }), + URL: config.URL, + Message: config.Message, + log: log.New("alerting.notifier.teams"), + ns: ns, + tmpl: t, + } +} + +// Notify send an alert notification to Microsoft Teams. +func (tn *TeamsWorkflowsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { + var tmplErr error + tmpl, _ := TmplText(ctx, tn.tmpl, as, tn.log, &tmplErr) + + basePath := ToBasePathWithAccountRedirect(tn.tmpl.ExternalURL, types.Alerts(as...)) //LOGZ.IO GRAFANA CHANGE :: DEV-37746: Add switch to account query param + ruleURL := ToLogzioAppPath(joinUrlPath(basePath, "/alerting/list", tn.log)) // LOGZ.IO GRAFANA CHANGE :: DEV-31554 - Set APP url to logzio grafana for alert notification URLs + + title := tmpl(DefaultMessageTitleEmbed) + + body := map[string]interface{}{ + "type": "message", + "attachments": []map[string]interface{}{ + { + "contentType": "application/vnd.microsoft.card.adaptive", // Adaptive Card content type + "content": map[string]interface{}{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.2", + "body": []map[string]interface{}{ + { + "type": "TextBlock", + "text": title, + "weight": "bolder", + "size": "medium", + }, + { + "type": "TextBlock", + "text": tmpl(tn.Message), + "wrap": true, + }, + }, + "actions": []map[string]interface{}{ + { + "type": "Action.OpenUrl", + "title": "View Rule", + "url": ruleURL, + }, + }, + "backgroundImage": map[string]interface{}{ + "color": getAlertStatusColor(types.Alerts(as...).Status()), // Use the theme color as the background + }, + }, + }, + }, + } + + if tmplErr != nil { + tn.log.Warn("failed to template Teams message", "err", tmplErr.Error()) + tmplErr = nil + } + + u := tmpl(tn.URL) + if tmplErr != nil { + tn.log.Warn("failed to template Teams URL", "err", tmplErr.Error(), "fallback", tn.URL) + u = tn.URL + } + + b, err := json.Marshal(&body) + if err != nil { + return false, errors.Wrap(err, "marshal json") + } + cmd := &models.SendWebhookSync{Url: u, Body: string(b)} + + if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil { + return false, errors.Wrap(err, "send notification to Teams") + } + + return true, nil +} + +func (tn *TeamsWorkflowsNotifier) SendResolved() bool { + return !tn.GetDisableResolveMessage() +} From e327397b58bbe5e116a406d8ad56c084283751ff Mon Sep 17 00:00:00 2001 From: Jonathan-Eng Date: Wed, 30 Oct 2024 14:50:04 +0200 Subject: [PATCH 2/3] change to workflows jargon and remove comments --- pkg/services/ngalert/notifier/available_channels.go | 2 +- .../ngalert/notifier/channels/teams_workflows.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/services/ngalert/notifier/available_channels.go b/pkg/services/ngalert/notifier/available_channels.go index e11d2a018976f..7c3714486a63b 100644 --- a/pkg/services/ngalert/notifier/available_channels.go +++ b/pkg/services/ngalert/notifier/available_channels.go @@ -575,7 +575,7 @@ func GetAvailableNotifiers() []*alerting.NotifierPlugin { { Type: "teams_workflows", Name: "Microsoft Teams Workflows", - Description: "Sends notifications using Incoming Webhook connector to Microsoft Teams Workflows", + Description: "Sends notifications using Workflows to Microsoft Teams", Heading: "Teams Workflows settings", Options: []alerting.NotifierOption{ { diff --git a/pkg/services/ngalert/notifier/channels/teams_workflows.go b/pkg/services/ngalert/notifier/channels/teams_workflows.go index ce5a21790641b..40bd8f19e468c 100644 --- a/pkg/services/ngalert/notifier/channels/teams_workflows.go +++ b/pkg/services/ngalert/notifier/channels/teams_workflows.go @@ -84,7 +84,7 @@ func (tn *TeamsWorkflowsNotifier) Notify(ctx context.Context, as ...*types.Alert "type": "message", "attachments": []map[string]interface{}{ { - "contentType": "application/vnd.microsoft.card.adaptive", // Adaptive Card content type + "contentType": "application/vnd.microsoft.card.adaptive", "content": map[string]interface{}{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", @@ -96,6 +96,12 @@ func (tn *TeamsWorkflowsNotifier) Notify(ctx context.Context, as ...*types.Alert "weight": "bolder", "size": "medium", }, + { + "type": "TextBlock", + "text": "Details", + "weight": "bolder", + "spacing": "medium", + }, { "type": "TextBlock", "text": tmpl(tn.Message), @@ -110,7 +116,7 @@ func (tn *TeamsWorkflowsNotifier) Notify(ctx context.Context, as ...*types.Alert }, }, "backgroundImage": map[string]interface{}{ - "color": getAlertStatusColor(types.Alerts(as...).Status()), // Use the theme color as the background + "color": getAlertStatusColor(types.Alerts(as...).Status()), }, }, }, From eff7fc300e9238b8a5f8d870a99006135a496ab8 Mon Sep 17 00:00:00 2001 From: Jonathan-Eng Date: Wed, 30 Oct 2024 16:36:29 +0200 Subject: [PATCH 3/3] revert Details text block --- pkg/services/ngalert/notifier/channels/teams_workflows.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/services/ngalert/notifier/channels/teams_workflows.go b/pkg/services/ngalert/notifier/channels/teams_workflows.go index 40bd8f19e468c..5703d7e501f70 100644 --- a/pkg/services/ngalert/notifier/channels/teams_workflows.go +++ b/pkg/services/ngalert/notifier/channels/teams_workflows.go @@ -96,12 +96,6 @@ func (tn *TeamsWorkflowsNotifier) Notify(ctx context.Context, as ...*types.Alert "weight": "bolder", "size": "medium", }, - { - "type": "TextBlock", - "text": "Details", - "weight": "bolder", - "spacing": "medium", - }, { "type": "TextBlock", "text": tmpl(tn.Message),