This PR add labels for webhooks.
1. Make webhook "labelable" with ability to filter by labels.
2. Add labels to the webhook payload. It contain new field webhook with
it's name, id and labels. Field integration and alert_group has a
corresponding label field as well. See example of a new payload below:
```
{
"event": {
"type": "escalation"
},
"user": null,
"alert_group": {
"id": "IRFN6ZD31N31B",
"integration_id": "CTWM7U4A2QG97",
"route_id": "RUE7U7Z46SKGY",
"alerts_count": 1,
"state": "firing",
"created_at": "2023-11-22T08:54:55.178243Z",
"resolved_at": null,
"acknowledged_at": null,
"title": "Incident",
"permalinks": {
"slack": null,
"telegram": null,
"web": "http://grafana:3000/a/grafana-oncall-app/alert-groups/IRFN6ZD31N31B"
},
"labels": {
"severity": "critical"
}
},
"alert_group_id": "IRFN6ZD31N31B",
"alert_payload": {
"message": "This alert was sent by user for demonstration purposes"
},
"integration": {
"id": "CTWM7U4A2QG97",
"type": "webhook",
"name": "hi - Webhook",
"team": null,
"labels": {
"hello": "world",
"severity": "critical"
}
},
"notified_users": [],
"users_to_be_notified": [],
"webhook": {
"id": "WHAXK4BTC7TAEQ",
"name": "test",
"labels": {
"hello": "kesha"
}
}
}
```
I feel that there is an opportunity to make code cleaner - remove all
label logic from serializers, views and utils to models or dedicated
LabelerService and introduce Labelable interface with something like
label_verbal, update_labels methods. However, I don't want to tie
webhook labels with a refactoring.
---------
Co-authored-by: Dominik <dominik.broj@grafana.com>
92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
import logging
|
|
import typing
|
|
|
|
from celery.utils.log import get_task_logger
|
|
from django.conf import settings
|
|
from django.utils import timezone
|
|
|
|
from apps.labels.client import LabelsAPIClient
|
|
from apps.labels.utils import (
|
|
LABEL_OUTDATED_TIMEOUT_MINUTES,
|
|
LabelKeyData,
|
|
LabelsData,
|
|
ValueData,
|
|
get_associating_label_model,
|
|
)
|
|
from apps.user_management.models import Organization
|
|
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
|
|
|
|
logger = get_task_logger(__name__)
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
|
|
def unify_labels_data(labels_data: LabelsData | LabelKeyData) -> typing.Dict[str, ValueData]:
|
|
values_data: typing.Dict[str, ValueData]
|
|
if isinstance(labels_data, list): # LabelsData
|
|
values_data = {
|
|
label["value"]["id"]: {"value_name": label["value"]["name"], "key_name": label["key"]["name"]}
|
|
for label in labels_data
|
|
}
|
|
else: # LabelKeyData
|
|
values_data = {
|
|
label["id"]: {"value_name": label["name"], "key_name": labels_data["key"]["name"]}
|
|
for label in labels_data["values"]
|
|
}
|
|
return values_data
|
|
|
|
|
|
@shared_dedicated_queue_retry_task(
|
|
autoretry_for=(Exception,), retry_backoff=True, max_retries=1 if settings.DEBUG else None
|
|
)
|
|
def update_labels_cache(labels_data: LabelsData | LabelKeyData):
|
|
from apps.labels.models import LabelKeyCache, LabelValueCache
|
|
|
|
values_data: typing.Dict[str, ValueData] = unify_labels_data(labels_data)
|
|
values = LabelValueCache.objects.filter(id__in=values_data).select_related("key")
|
|
now = timezone.now()
|
|
|
|
if not values:
|
|
return
|
|
|
|
keys_to_update = set()
|
|
|
|
for value in values:
|
|
if value.name != values_data[value.id]["value_name"]:
|
|
value.name = values_data[value.id]["value_name"]
|
|
value.last_synced = now
|
|
|
|
if value.key.name != values_data[value.id]["key_name"]:
|
|
value.key.name = values_data[value.id]["key_name"]
|
|
value.key.last_synced = now
|
|
keys_to_update.add(value.key)
|
|
|
|
LabelKeyCache.objects.bulk_update(keys_to_update, fields=["name", "last_synced"])
|
|
LabelValueCache.objects.bulk_update(values, fields=["name", "last_synced"])
|
|
|
|
|
|
@shared_dedicated_queue_retry_task(
|
|
autoretry_for=(Exception,), retry_backoff=True, max_retries=1 if settings.DEBUG else 10
|
|
)
|
|
def update_instances_labels_cache(organization_id: int, instance_ids: typing.List[int], instance_model_name: str):
|
|
from apps.labels.models import LabelValueCache
|
|
|
|
now = timezone.now()
|
|
organization = Organization.objects.get(id=organization_id)
|
|
|
|
model = get_associating_label_model(instance_model_name)
|
|
field_name = model.get_associating_label_field_name()
|
|
associated_instances = {f"{field_name}_id__in": instance_ids}
|
|
values_ids = model.objects.filter(**associated_instances).values_list("value_id", flat=True)
|
|
outdated_last_synced = now - timezone.timedelta(minutes=LABEL_OUTDATED_TIMEOUT_MINUTES)
|
|
values = LabelValueCache.objects.filter(id__in=values_ids, last_synced__lte=outdated_last_synced)
|
|
|
|
if not values:
|
|
return
|
|
|
|
keys_ids = set(value.key_id for value in values)
|
|
|
|
client = LabelsAPIClient(organization.grafana_url, organization.api_token)
|
|
for key_id in keys_ids:
|
|
label_data, _ = client.get_values(key_id)
|
|
if label_data:
|
|
update_labels_cache.apply_async((label_data,))
|