Skip to content

Commit ecde9eb

Browse files
committed
Add card feature to msteamsv2_config
Signed-off-by: jverger <[email protected]>
1 parent b5d1a64 commit ecde9eb

File tree

4 files changed

+129
-33
lines changed

4 files changed

+129
-33
lines changed

config/notifiers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ type MSTeamsV2Config struct {
872872

873873
Title string `yaml:"title,omitempty" json:"title,omitempty"`
874874
Text string `yaml:"text,omitempty" json:"text,omitempty"`
875+
Card string `yaml:"card,omitempty" json:"card,omitempty"`
875876
}
876877

877878
func (c *MSTeamsV2Config) UnmarshalYAML(unmarshal func(interface{}) error) error {

docs/configuration.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,11 @@ Microsoft Teams v2 notifications using the new message format with adaptive card
992992
# Message body template.
993993
[ text: <tmpl_string> | default = '{{ template "msteamsv2.default.text" . }}' ]
994994
995+
# Message body card.
996+
# If not null, it will override title and text values (no need to configure these values)
997+
# You can find a complete sample file template here 'examples/msteamsv2/card.tmpl' and provide '{{ template "msteams.card" . }}' in the card value to test.
998+
[ card: <tmpl_string> ]
999+
9951000
# The HTTP client's configuration.
9961001
[ http_config: <http_config> | default = global.http_config ]
9971002
```

examples/msteamsv2/card.tmpl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{{ define "msteams.card" }}
2+
{
3+
"Type": "message",
4+
"Attachments": [
5+
{
6+
"contentType": "application/vnd.microsoft.card.adaptive",
7+
"content": {
8+
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
9+
"type": "AdaptiveCard",
10+
"version": "1.4",
11+
"msteams": {
12+
"width": "Full"
13+
},
14+
"body": [
15+
{
16+
"type": "ColumnSet",
17+
"style": "{{ if eq .Status "firing" }}attention{{ else if eq .Status "resolved" }}good{{ else }}warning{{ end }}",
18+
"columns": [
19+
{
20+
"type": "Column",
21+
"width": "stretch",
22+
"items": [
23+
{
24+
"type": "TextBlock",
25+
"weight": "Bolder",
26+
"size": "ExtraLarge",
27+
"color": "{{ if eq .Status "firing" }}attention{{ else if eq .Status "resolved" }}good{{ else }}warning{{ end }}",
28+
"text": "{{ if eq .Status "firing" }}🔥{{ else if eq .Status "resolved" }}✅{{ else }}⚠️{{ end }} Prometheus alert {{ if eq .Status "resolved" }}(Resolved){{ else if eq .Status "firing" }}(Firing){{ else if eq .Status "unknown" }}(Unknown){{ else }}(Warning){{ end }}"
29+
},
30+
{
31+
"type": "TextBlock",
32+
"weight": "Bolder",
33+
"size": "ExtraLarge",
34+
"text": "{{ if eq .Status "resolved" }}(Resolved) {{ end }}{{ .CommonAnnotations.summary }}",
35+
"wrap": true
36+
}
37+
]
38+
}
39+
]
40+
},
41+
{
42+
"type": "FactSet",
43+
"facts": [
44+
{ "title": "Status", "value": "{{ .Status }} {{ if eq .Status "firing" }}🔥{{ else if eq .Status "resolved" }}✅{{ else }}⚠️{{ end }}" }
45+
{{ if .CommonLabels.alertname }}, { "title": "Alert", "value": "{{ .CommonLabels.alertname }}" }{{ end }}
46+
{{ if .CommonLabels.instance }}, { "title": "In host", "value": "{{ .CommonLabels.instance }}" }{{ end }}
47+
{{ if .CommonLabels.severity }}, { "title": "Severity", "value": "{{ .CommonLabels.severity }} {{ if eq .CommonLabels.severity "critical" }}❌{{ else if eq .CommonLabels.severity "error" }}❗️{{ else if eq .CommonLabels.severity "warning" }}⚠️{{ else if eq .CommonLabels.severity "info" }}ℹ️{{ else }}❓{{ end }}" }{{ end }}
48+
{{ if .CommonAnnotations.description }}, { "title": "Description", "value": "{{ .CommonAnnotations.description }}" }{{ end }}
49+
{{- range $key, $value := .CommonLabels }}
50+
{{- if and (ne $key "alertname") (ne $key "instance") (ne $key "severity") }}
51+
, { "title": "{{ $key }}", "value": "{{ $value }}" }
52+
{{- end }}
53+
{{- end }}
54+
{{- range $key, $value := .CommonAnnotations }}
55+
{{- if and (ne $key "summary") (ne $key "description") }}
56+
, { "title": "{{ $key }}", "value": "{{ $value }}" }
57+
{{- end }}
58+
{{- end }}
59+
]
60+
}
61+
]
62+
{{ if .CommonAnnotations.runbook_url }},
63+
"actions": [
64+
{
65+
"type": "Action.OpenUrl",
66+
"title": "View details",
67+
"url": "{{ .CommonAnnotations.runbook_url }}"
68+
}
69+
]
70+
{{ end }}
71+
}
72+
}
73+
]
74+
}
75+
{{ end }}

notify/msteamsv2/msteamsv2.go

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
125125
if err != nil {
126126
return false, err
127127
}
128+
card := tmpl(n.conf.Card)
129+
if err != nil {
130+
return false, err
131+
}
128132

129133
alerts := types.Alerts(as...)
130134
color := colorGrey
@@ -146,44 +150,55 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
146150
url = strings.TrimSpace(string(content))
147151
}
148152

149-
// A message as referenced in https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1%2Cdotnet#request-body-schema
150-
t := teamsMessage{
151-
Type: "message",
152-
Attachments: []Attachment{
153-
{
154-
ContentType: "application/vnd.microsoft.card.adaptive",
155-
ContentURL: nil,
156-
Content: Content{
157-
Schema: "http://adaptivecards.io/schemas/adaptive-card.json",
158-
Type: "AdaptiveCard",
159-
Version: "1.2",
160-
Body: []Body{
161-
{
162-
Type: "TextBlock",
163-
Text: title,
164-
Weight: "Bolder",
165-
Size: "Medium",
166-
Wrap: true,
167-
Style: "heading",
168-
Color: color,
153+
// If the card is empty, use title and text otherwise use card.
154+
var payload bytes.Buffer
155+
if card == "" {
156+
// A message as referenced in https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1%2Cdotnet#request-body-schema
157+
t := teamsMessage{
158+
Type: "message",
159+
Attachments: []Attachment{
160+
{
161+
ContentType: "application/vnd.microsoft.card.adaptive",
162+
ContentURL: nil,
163+
Content: Content{
164+
Schema: "http://adaptivecards.io/schemas/adaptive-card.json",
165+
Type: "AdaptiveCard",
166+
Version: "1.2",
167+
Body: []Body{
168+
{
169+
Type: "TextBlock",
170+
Text: title,
171+
Weight: "Bolder",
172+
Size: "Medium",
173+
Wrap: true,
174+
Style: "heading",
175+
Color: color,
176+
},
177+
{
178+
Type: "TextBlock",
179+
Text: text,
180+
},
169181
},
170-
{
171-
Type: "TextBlock",
172-
Text: text,
173-
Wrap: true,
182+
Msteams: Msteams{
183+
Width: "full",
174184
},
175185
},
176-
Msteams: Msteams{
177-
Width: "full",
178-
},
179186
},
180187
},
181-
},
182-
}
183-
184-
var payload bytes.Buffer
185-
if err = json.NewEncoder(&payload).Encode(t); err != nil {
186-
return false, err
188+
}
189+
190+
if err = json.NewEncoder(&payload).Encode(t); err != nil {
191+
return false, err
192+
}
193+
} else {
194+
// Transform card string into object
195+
var jsonMap map[string]interface{}
196+
json.Unmarshal([]byte(card), &jsonMap)
197+
n.logger.Debug("jsonMap", "jsonMap", jsonMap)
198+
199+
if err = json.NewEncoder(&payload).Encode(jsonMap); err != nil {
200+
return false, err
201+
}
187202
}
188203

189204
resp, err := n.postJSONFunc(ctx, n.client, url, &payload)

0 commit comments

Comments
 (0)