Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add grafana v10 AlertRule support #645

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 68 additions & 6 deletions grafanalib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def to_json_data(self):
EXP_TYPE_REDUCE = 'reduce'
EXP_TYPE_RESAMPLE = 'resample'
EXP_TYPE_MATH = 'math'
EXP_TYPE_THRESHOLD = 'threshold'

# Alert Expression Reducer Function
EXP_REDUCER_FUNC_MIN = 'min'
Expand Down Expand Up @@ -1418,6 +1419,23 @@ def to_json_data(self):

return expression

@attr.s
class AlertExpressionv10(AlertExpression):
"""
Specific to Grafana v10.x, adds support for threshold expression type
"""

expressionType = attr.ib(
default=EXP_TYPE_CLASSIC,
validator=in_([
EXP_TYPE_CLASSIC,
EXP_TYPE_REDUCE,
EXP_TYPE_RESAMPLE,
EXP_TYPE_MATH,
EXP_TYPE_THRESHOLD
])
)


@attr.s
class Alert(object):
Expand Down Expand Up @@ -1510,6 +1528,16 @@ def is_valid_triggersv9(instance, attribute, value):
is_valid_target(instance, "alert trigger target", trigger)


def is_valid_triggersv10(instance, attribute, value):
"""Validator for AlertRule triggers for Grafana v9"""
for trigger in value:
if not (isinstance(trigger, Target) or isinstance(trigger, AlertExpressionv10)):
raise ValueError(f"{attribute.name} must either be a Target or AlertExpressionV10")

if isinstance(trigger, Target):
is_valid_target(instance, "alert trigger target", trigger)


@attr.s
class AlertRulev8(object):
"""
Expand Down Expand Up @@ -1620,10 +1648,8 @@ def to_json_data(self):


@attr.s
class AlertRulev9(object):
class _BaseAlertRule(object):
"""
Create a Grafana 9.x+ Alert Rule

:param title: The alert's title, must be unique per folder
:param triggers: A list of Targets and AlertConditions.
The Target specifies the query, and the AlertCondition specifies how this is used to alert.
Expand All @@ -1650,7 +1676,6 @@ class AlertRulev9(object):
"""

title = attr.ib()
triggers = attr.ib(factory=list, validator=is_valid_triggersv9)
annotations = attr.ib(factory=dict, validator=instance_of(dict))
labels = attr.ib(factory=dict, validator=instance_of(dict))

Expand Down Expand Up @@ -1678,7 +1703,7 @@ class AlertRulev9(object):
dashboard_uid = attr.ib(default="", validator=instance_of(str))
panel_id = attr.ib(default=0, validator=instance_of(int))

def to_json_data(self):
def _render_triggers(self):
data = []

for trigger in self.triggers:
Expand All @@ -1696,6 +1721,21 @@ def to_json_data(self):
else:
data += [trigger.to_json_data()]

return data

def to_json_data(self):
pass


@attr.s
class AlertRulev9(_BaseAlertRule):
"""
Create a Grafana 9.x+ Alert Rule
"""

triggers = attr.ib(factory=list, validator=is_valid_triggersv9)

def to_json_data(self):
return {
"uid": self.uid,
"for": self.evaluateFor,
Expand All @@ -1704,13 +1744,35 @@ def to_json_data(self):
"grafana_alert": {
"title": self.title,
"condition": self.condition,
"data": data,
"data": self._render_triggers(),
"no_data_state": self.noDataAlertState,
"exec_err_state": self.errorAlertState,
},
}


@attr.s
class AlertRulev10(_BaseAlertRule):
"""
Create a Grafana 10.x+ Alert Rule
"""

triggers = attr.ib(factory=list, validator=is_valid_triggersv10)

def to_json_data(self):
return {
"uid": self.uid,
"for": self.evaluateFor,
"labels": self.labels,
"annotations": self.annotations,
"title": self.title,
"condition": self.condition,
"data": self._render_triggers(),
"noDataState": self.noDataAlertState,
"execErrState": self.errorAlertState,
}


@attr.s
class AlertFileBasedProvisioning(object):
"""
Expand Down
41 changes: 41 additions & 0 deletions grafanalib/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,47 @@ def test_alertrulev9():
assert data['grafana_alert']['condition'] == condition


def test_alertrulev10():
title = "My Important Alert!"
annotations = {"summary": "this alert fires when prod is down!!!"}
labels = {"severity": "serious"}
condition = 'C'
rule = G.AlertRulev10(
title=title,
uid='alert1',
condition=condition,
triggers=[
G.Target(
expr='query',
refId='A',
datasource='Prometheus',
),
G.AlertExpression(
refId='B',
expressionType=G.EXP_TYPE_CLASSIC,
expression='A',
conditions=[
G.AlertCondition(
evaluator=G.GreaterThan(3),
operator=G.OP_AND,
reducerType=G.RTYPE_LAST
)
]
),
],
annotations=annotations,
labels=labels,
evaluateFor="3m",
)

data = rule.to_json_data()
assert data['annotations'] == annotations
assert data['labels'] == labels
assert data['for'] == "3m"
assert data['title'] == title
assert data['condition'] == condition


def test_alertexpression():
refId = 'D'
expression = 'C'
Expand Down