mirror of
https://github.com/zulip/zulip.git
synced 2025-11-16 03:41:58 +00:00
integrations: Add an incoming webhook for Grafana.
Tweaked by tabbott to use formatted suggested in one of the various duplicate PRs for this issue, showing the rule name clearly. Fixes #12951.
This commit is contained in:
BIN
static/images/integrations/grafana/001.png
Normal file
BIN
static/images/integrations/grafana/001.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
BIN
static/images/integrations/logos/grafana.png
Normal file
BIN
static/images/integrations/logos/grafana.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
@@ -325,6 +325,7 @@ WEBHOOK_INTEGRATIONS: List[WebhookIntegration] = [
|
|||||||
WebhookIntegration('gocd', ['continuous-integration'], display_name='GoCD'),
|
WebhookIntegration('gocd', ['continuous-integration'], display_name='GoCD'),
|
||||||
WebhookIntegration('gogs', ['version-control'], stream_name='commits'),
|
WebhookIntegration('gogs', ['version-control'], stream_name='commits'),
|
||||||
WebhookIntegration('gosquared', ['marketing'], display_name='GoSquared'),
|
WebhookIntegration('gosquared', ['marketing'], display_name='GoSquared'),
|
||||||
|
WebhookIntegration('grafana', ['monitoring'], display_name='Grafana'),
|
||||||
WebhookIntegration('greenhouse', ['hr'], display_name='Greenhouse'),
|
WebhookIntegration('greenhouse', ['hr'], display_name='Greenhouse'),
|
||||||
WebhookIntegration('groove', ['customer-support'], display_name='Groove'),
|
WebhookIntegration('groove', ['customer-support'], display_name='Groove'),
|
||||||
WebhookIntegration('harbor', ['deployment', 'productivity'], display_name='Harbor'),
|
WebhookIntegration('harbor', ['deployment', 'productivity'], display_name='Harbor'),
|
||||||
|
|||||||
0
zerver/webhooks/grafana/__init__.py
Normal file
0
zerver/webhooks/grafana/__init__.py
Normal file
23
zerver/webhooks/grafana/doc.md
Normal file
23
zerver/webhooks/grafana/doc.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
See your Grafana dashboard alerts in Zulip!
|
||||||
|
|
||||||
|
1. {!create-stream.md!}
|
||||||
|
|
||||||
|
1. {!create-bot-construct-url-indented.md!}
|
||||||
|
|
||||||
|
1. In Grafana, go to **Alerting**. Click on **Notification channels**.
|
||||||
|
Configure **Edit Notification Channel** as appropriate for your
|
||||||
|
alert notification. Set the name. Under **Type**, choose **webhook**.
|
||||||
|
In **Webhook Settings**, set **URL** to the URL constructed above.
|
||||||
|
Under **HTTP method**, choose **POST**. Click Save.
|
||||||
|
|
||||||
|
1. Create an alert. Within your new alert rule, scroll down
|
||||||
|
to the **Notifications** section. Click on the button next to **Send to**
|
||||||
|
and select the webhook notification channel you just made. You can also
|
||||||
|
choose to write a message, which will be included in the Zulip notifications.
|
||||||
|
|
||||||
|
1. Return to **Notification channels**. You may now click **Send Test** and
|
||||||
|
you will see a Grafana test alert notification in Zulip.
|
||||||
|
|
||||||
|
{!congrats.md!}
|
||||||
|
|
||||||
|

|
||||||
25
zerver/webhooks/grafana/fixtures/alert.json
Normal file
25
zerver/webhooks/grafana/fixtures/alert.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"dashboardId": 1,
|
||||||
|
"evalMatches": [
|
||||||
|
{
|
||||||
|
"value": 100,
|
||||||
|
"metric": "High value",
|
||||||
|
"tags": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 200,
|
||||||
|
"metric": "Higher Value",
|
||||||
|
"tags": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"imageUrl": "https://grafana.com/assets/img/blog/mixed_styles.png",
|
||||||
|
"message": "Someone is testing the alert notification within grafana.",
|
||||||
|
"orgId": 0,
|
||||||
|
"panelId": 1,
|
||||||
|
"ruleId": 0,
|
||||||
|
"ruleName": "Test rule",
|
||||||
|
"ruleUrl": "http://localhost:3000/",
|
||||||
|
"state": "alerting",
|
||||||
|
"tags": {},
|
||||||
|
"title": "[Alerting] Test notification"
|
||||||
|
}
|
||||||
13
zerver/webhooks/grafana/fixtures/no_data_alert.json
Normal file
13
zerver/webhooks/grafana/fixtures/no_data_alert.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"dashboardId": 2,
|
||||||
|
"evalMatches": [],
|
||||||
|
"message": "The panel has no data.",
|
||||||
|
"orgId": 1,
|
||||||
|
"panelId": 6,
|
||||||
|
"ruleId": 3,
|
||||||
|
"ruleName": "No Data alert",
|
||||||
|
"ruleUrl": "http://localhost:3000/d/GG2qhR3Wz/alerttest?fullscreen&edit&tab=alert&panelId=6&orgId=1",
|
||||||
|
"state": "alerting",
|
||||||
|
"tags": {},
|
||||||
|
"title": "[Alerting] No Data alert"
|
||||||
|
}
|
||||||
18
zerver/webhooks/grafana/fixtures/no_message_alert.json
Normal file
18
zerver/webhooks/grafana/fixtures/no_message_alert.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"dashboardId": 2,
|
||||||
|
"evalMatches": [
|
||||||
|
{
|
||||||
|
"value": 21.573108436586445,
|
||||||
|
"metric": "A-series",
|
||||||
|
"tags": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orgId": 1,
|
||||||
|
"panelId": 8,
|
||||||
|
"ruleId": 4,
|
||||||
|
"ruleName": "No Message alert",
|
||||||
|
"ruleUrl": "http://localhost:3000/d/GG2qhR3Wz/alerttest?fullscreen&edit&tab=alert&panelId=8&orgId=1",
|
||||||
|
"state": "alerting",
|
||||||
|
"tags": {},
|
||||||
|
"title": "[Alerting] No Message alert"
|
||||||
|
}
|
||||||
54
zerver/webhooks/grafana/tests.py
Normal file
54
zerver/webhooks/grafana/tests.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from zerver.lib.test_classes import WebhookTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class GrafanaHookTests(WebhookTestCase):
|
||||||
|
STREAM_NAME = 'grafana'
|
||||||
|
URL_TEMPLATE = "/api/v1/external/grafana?&api_key={api_key}&stream={stream}"
|
||||||
|
FIXTURE_DIR_NAME = 'grafana'
|
||||||
|
|
||||||
|
# Note: Include a test function per each distinct message condition your integration supports
|
||||||
|
def test_alert(self) -> None:
|
||||||
|
expected_topic = "[Alerting] Test notification"
|
||||||
|
expected_message = """
|
||||||
|
[Test rule](http://localhost:3000/)
|
||||||
|
|
||||||
|
Someone is testing the alert notification within grafana.
|
||||||
|
|
||||||
|
**High value:** 100
|
||||||
|
**Higher Value:** 200
|
||||||
|
|
||||||
|
[Click to view visualization](https://grafana.com/assets/img/blog/mixed_styles.png)
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
# use fixture named helloworld_hello
|
||||||
|
self.send_and_test_stream_message('alert', expected_topic, expected_message,
|
||||||
|
content_type="application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
def test_no_data_alert(self) -> None:
|
||||||
|
expected_topic = "[Alerting] No Data alert"
|
||||||
|
expected_message = """
|
||||||
|
[No Data alert](http://localhost:3000/d/GG2qhR3Wz/alerttest?fullscreen&edit&tab=alert&panelId=6&orgId=1)
|
||||||
|
|
||||||
|
The panel has no data.
|
||||||
|
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
# use fixture named helloworld_hello
|
||||||
|
self.send_and_test_stream_message('no_data_alert', expected_topic, expected_message,
|
||||||
|
content_type="application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
def test_no_message_alert(self) -> None:
|
||||||
|
expected_topic = "[Alerting] No Message alert"
|
||||||
|
expected_message = """
|
||||||
|
[No Message alert](http://localhost:3000/d/GG2qhR3Wz/alerttest?fullscreen&edit&tab=alert&panelId=8&orgId=1)
|
||||||
|
|
||||||
|
**A-series:** 21.573108436586445
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
# use fixture named helloworld_hello
|
||||||
|
self.send_and_test_stream_message('no_message_alert', expected_topic, expected_message,
|
||||||
|
content_type="application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
def get_body(self, fixture_name: str) -> str:
|
||||||
|
return self.webhook_fixture_data("grafana", fixture_name, file_type="json")
|
||||||
47
zerver/webhooks/grafana/view.py
Normal file
47
zerver/webhooks/grafana/view.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from django.http import HttpRequest, HttpResponse
|
||||||
|
|
||||||
|
from zerver.decorator import api_key_only_webhook_view
|
||||||
|
from zerver.lib.webhooks.common import check_send_webhook_message
|
||||||
|
from zerver.lib.request import REQ, has_request_variables
|
||||||
|
from zerver.lib.response import json_success
|
||||||
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
GRAFANA_TOPIC_TEMPLATE = u'{alert_title}'
|
||||||
|
|
||||||
|
GRAFANA_MESSAGE_TEMPLATE = u'[{rule_name}]({rule_url})\n\n{alert_message}{eval_matches}'
|
||||||
|
|
||||||
|
@api_key_only_webhook_view('Grafana')
|
||||||
|
@has_request_variables
|
||||||
|
def api_grafana_webhook(
|
||||||
|
request: HttpRequest, user_profile: UserProfile,
|
||||||
|
payload: Dict[str, Any]=REQ(argument_type='body')
|
||||||
|
) -> HttpResponse:
|
||||||
|
|
||||||
|
topic = GRAFANA_TOPIC_TEMPLATE.format(alert_title=payload['title'])
|
||||||
|
|
||||||
|
eval_matches_text = ''
|
||||||
|
eval_matches = payload.get('evalMatches')
|
||||||
|
if eval_matches is not None:
|
||||||
|
for match in eval_matches:
|
||||||
|
eval_matches_text += '**{}:** {}\n'.format(match['metric'], match['value'])
|
||||||
|
|
||||||
|
message_text = ''
|
||||||
|
if payload.get('message') is not None:
|
||||||
|
message_text = payload['message'] + "\n\n"
|
||||||
|
|
||||||
|
body = GRAFANA_MESSAGE_TEMPLATE.format(alert_message=message_text,
|
||||||
|
rule_name=payload['ruleName'],
|
||||||
|
rule_url=payload['ruleUrl'],
|
||||||
|
eval_matches=eval_matches_text)
|
||||||
|
|
||||||
|
if payload.get('imageUrl') is not None:
|
||||||
|
body += "\n[Click to view visualization]({visualization})".format(visualization=payload['imageUrl'])
|
||||||
|
|
||||||
|
body = body.strip()
|
||||||
|
|
||||||
|
# send the message
|
||||||
|
check_send_webhook_message(request, user_profile, topic, body)
|
||||||
|
|
||||||
|
return json_success()
|
||||||
Reference in New Issue
Block a user