Skip to content

Commit

Permalink
feat: add paginated firing alerts
Browse files Browse the repository at this point in the history
  • Loading branch information
freak12techno committed Nov 8, 2024
1 parent 83341c9 commit de95fd6
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 236 deletions.
5 changes: 5 additions & 0 deletions pkg/alert_source/alert_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package alert_source

import "main/pkg/types"

type Prefixes struct {
PaginatedFiringAlerts string
}

type AlertSource interface {
Enabled() bool
GetAlertingRules() (types.GrafanaAlertGroups, error)
Name() string
Prefixes() Prefixes
}
7 changes: 7 additions & 0 deletions pkg/alert_source/grafana.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package alert_source
import (
"fmt"
"main/pkg/config"
"main/pkg/constants"
"main/pkg/http"
"main/pkg/types"

Expand Down Expand Up @@ -43,6 +44,12 @@ func (g *Grafana) GetAuth() *http.Auth {
}
}

func (g *Grafana) Prefixes() Prefixes {
return Prefixes{
PaginatedFiringAlerts: constants.GrafanaPaginatedFiringAlertsList,
}
}

func (g *Grafana) RelativeLink(url string) string {
return fmt.Sprintf("%s%s", g.Config.URL, url)
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/alert_source/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package alert_source

import (
"main/pkg/config"
"main/pkg/constants"
"main/pkg/http"
"main/pkg/types"

Expand Down Expand Up @@ -30,6 +31,12 @@ func (p *Prometheus) Name() string {
return "Prometheus"
}

func (g *Prometheus) Prefixes() Prefixes {
return Prefixes{
PaginatedFiringAlerts: constants.PrometheusPaginatedFiringAlertsList,
}
}

func (p *Prometheus) GetAuth() *http.Auth {
if p.Config == nil || p.Config.User == "" || p.Config.Password == "" {
return nil
Expand Down
241 changes: 140 additions & 101 deletions pkg/app/alerts_firing.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,145 +2,184 @@ package app

import (
"fmt"
"main/pkg/alert_source"
"main/pkg/constants"
"main/pkg/silence_manager"
"main/pkg/types"
"main/pkg/types/render"
"main/pkg/utils/generic"
"strconv"

tele "gopkg.in/telebot.v3"
)

func (a *App) HandleListFiringAlerts(c tele.Context) error {
func (a *App) HandleChooseAlertSourceForListAlerts(c tele.Context) error {
a.Logger.Info().
Str("sender", c.Sender().Username).
Str("text", c.Text()).
Msg("Got firing alerts query")
Msg("Got choosing a datasource for firing alerts query")

// TODO: fix
grafanaGroups, err := a.AlertSources[0].GetAlertingRules()
if err != nil {
return c.Reply(fmt.Sprintf("Error querying alerts: %s", err))
alertSources := generic.Filter(a.AlertSourcesWithSilenceManager, func(a AlertSourceWithSilenceManager) bool {
return a.AlertSource.Enabled()
})

if len(alertSources) == 0 {
return a.BotReply(c, "No alert sources configured!")
}

prometheusGroups, err := a.AlertSources[1].GetAlertingRules()
if err != nil {
return c.Reply(fmt.Sprintf("Error querying alerts: %s", err))
if len(alertSources) == 1 {
return a.HandleListFiringAlertsWithPagination(
c,
alertSources[0].AlertSource,
alertSources[0].SilenceManager,
0,
false,
)
}

grafanaGroups = grafanaGroups.FilterFiringOrPendingAlertGroups()
prometheusGroups = prometheusGroups.FilterFiringOrPendingAlertGroups()
menu := &tele.ReplyMarkup{ResizeKeyboard: true}
rows := make([]tele.Row, 0)
index := 0

batches := []types.FiringAlertsListStruct{}
batchToAdd := types.FiringAlertsListStruct{
GrafanaAlerts: make([]types.FiringAlert, 0),
PrometheusAlerts: make([]types.FiringAlert, 0),
GrafanaAlertsCount: len(grafanaGroups),
PrometheusAlertsCount: len(prometheusGroups),
ShowGrafanaHeader: true,
for _, source := range alertSources {
if !source.AlertSource.Enabled() {
continue
}

button := menu.Data(
source.AlertSource.Name(),
source.AlertSource.Prefixes().PaginatedFiringAlerts,
"0", // page
)

rows = append(rows, menu.Row(button))
index += 1
}
batchIndex := 0

for _, grafanaGroup := range grafanaGroups {
for ruleIndex, grafanaRule := range grafanaGroup.Rules {
for _, grafanaAlert := range grafanaRule.Alerts {
batchToAdd.GrafanaAlerts = append(batchToAdd.GrafanaAlerts, types.FiringAlert{
GroupName: grafanaGroup.Name,
GroupAlertsCount: len(grafanaGroup.Rules),
AlertName: grafanaRule.Name,
Alert: grafanaAlert,
ShowAlertName: ruleIndex == 0,
})
batchIndex++

if len(batchToAdd.GrafanaAlerts) >= constants.AlertsInOneMessage {
batches = append(batches, batchToAdd)
batchToAdd = types.FiringAlertsListStruct{
GrafanaAlerts: make([]types.FiringAlert, 0),
PrometheusAlerts: make([]types.FiringAlert, 0),
GrafanaAlertsCount: len(grafanaGroups),
PrometheusAlertsCount: len(prometheusGroups),
}
batchIndex = 0
}
}

menu.Inline(rows...)

return a.BotReply(c, "Choose an alert source to get alerts from:", menu)
}

func (a *App) HandleListFiringAlertsFromCallback(
alertSource alert_source.AlertSource,
silenceManager silence_manager.SilenceManager,
) func(c tele.Context) error {
return func(c tele.Context) error {
callback := c.Callback()

a.Logger.Info().
Str("sender", c.Sender().Username).
Str("alert_source", alertSource.Name()).
Str("data", callback.Data).
Msg("Got list firing alerts query via callback")

page, err := strconv.Atoi(callback.Data)
if err != nil {
return c.Reply("Failed to parse page number from callback!")
}

return a.HandleListFiringAlertsWithPagination(c, alertSource, silenceManager, page, true)
}
}

func (a *App) HandleListFiringAlertsWithPagination(
c tele.Context,
alertSource alert_source.AlertSource,
silenceManager silence_manager.SilenceManager,
page int,
editPrevious bool,
) error {
if !alertSource.Enabled() {
return c.Reply(alertSource.Name() + " is disabled.")
}

batchToAdd.ShowPrometheusHeader = true

for _, prometheusGroup := range prometheusGroups {
for _, prometheusRule := range prometheusGroup.Rules {
for alertIndex, prometheusAlert := range prometheusRule.Alerts {
batchToAdd.PrometheusAlerts = append(batchToAdd.PrometheusAlerts, types.FiringAlert{
GroupName: prometheusGroup.Name,
GroupAlertsCount: len(prometheusGroup.Rules),
AlertName: prometheusRule.Name,
Alert: prometheusAlert,
ShowAlertName: alertIndex == 0,
alerts, err := alertSource.GetAlertingRules()
if err != nil {
return c.Reply(fmt.Sprintf("Error fetching alerts: %s!\n", err))
}

alerts = alerts.FilterFiringOrPendingAlertGroups()

firingAlerts := make([]types.FiringAlert, 0)

for _, alertGroup := range alerts {
for _, alertRule := range alertGroup.Rules {
for _, alert := range alertRule.Alerts {
firingAlerts = append(firingAlerts, types.FiringAlert{
GroupName: alertGroup.Name,
Alert: alert,
AlertRuleName: alertRule.Name,
})
batchIndex++

if len(batchToAdd.PrometheusAlerts) >= constants.AlertsInOneMessage {
batches = append(batches, batchToAdd)
batchToAdd = types.FiringAlertsListStruct{
GrafanaAlerts: make([]types.FiringAlert, 0),
PrometheusAlerts: make([]types.FiringAlert, 0),
GrafanaAlertsCount: len(grafanaGroups),
PrometheusAlertsCount: len(prometheusGroups),
}
batchIndex = 0
}
}
}
}

if len(batches) == 0 {
batches = append(batches, batchToAdd)
alertsGrouped := generic.SplitArrayIntoChunks(firingAlerts, constants.AlertsInOneMessage)
if len(alertsGrouped) == 0 {
alertsGrouped = [][]types.FiringAlert{{}}
}

for _, batch := range batches {
template, renderErr := a.TemplateManager.Render("alerts_firing", render.RenderStruct{
Grafana: a.Grafana,
Data: batch,
})
if renderErr != nil {
a.Logger.Error().Err(renderErr).Msg("Error rendering alerts_firing template")
return c.Reply(fmt.Sprintf("Error rendering template: %s", renderErr))
}
chunk := []types.FiringAlert{}
if page < len(alertsGrouped) {
chunk = alertsGrouped[page]
}

menu := &tele.ReplyMarkup{ResizeKeyboard: true}
menu := &tele.ReplyMarkup{ResizeKeyboard: true}

rows := make([]tele.Row, 0)
rows := make([]tele.Row, 0)
index := 0

index := 0
for _, alert := range chunk {
button := menu.Data(
fmt.Sprintf("🔇Silence alert #%d", index+1),
silenceManager.GetPrepareSilencePrefix(),
alert.Alert.GetCallbackHash(),
)

for _, alert := range batch.GrafanaAlerts {
button := menu.Data(
fmt.Sprintf("🔇Silence alert #%d", index+1),
constants.AlertmanagerPrepareSilencePrefix,
alert.Alert.GetCallbackHash(),
)
rows = append(rows, menu.Row(button))
index += 1
}

rows = append(rows, menu.Row(button))
index += 1
if len(chunk) > 0 {
buttons := []tele.Btn{}
if page >= 1 {
buttons = append(buttons, menu.Data(
fmt.Sprintf("⬅️Page %d", page),
alertSource.Prefixes().PaginatedFiringAlerts,
strconv.Itoa(page-1),
))
}

for _, alert := range batch.PrometheusAlerts {
button := menu.Data(
fmt.Sprintf("🔇Silence alert #%d", index+1),
constants.AlertmanagerPrepareSilencePrefix,
alert.Alert.GetCallbackHash(),
)
if page < len(alertsGrouped)-1 {
buttons = append(buttons, menu.Data(
fmt.Sprintf("➡️Page %d", page+2),
alertSource.Prefixes().PaginatedFiringAlerts,
strconv.Itoa(page+1),
))
}

rows = append(rows, menu.Row(button))
index += 1
if len(buttons) > 0 {
rows = append(rows, menu.Row(buttons...))
}
}

menu.Inline(rows...)
menu.Inline(rows...)

templateData := render.RenderStruct{
Grafana: a.Grafana,
Data: types.FiringAlertsListStruct{
AlertSourceName: alertSource.Name(),
Alerts: chunk,
AlertsCount: len(firingAlerts),
Start: page*constants.AlertsInOneMessage + 1,
End: page*constants.AlertsInOneMessage + len(chunk),
},
}

if sendErr := a.BotReply(c, template, menu); sendErr != nil {
return err
}
if editPrevious {
return a.EditRenderWithMarkup(c, "alerts_firing", templateData, menu)
}

return nil
return a.ReplyRenderWithMarkup(c, "alerts_firing", templateData, menu)
}
13 changes: 4 additions & 9 deletions pkg/app/alerts_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,22 @@ func (a *App) HandleListAlerts(c tele.Context) error {
Str("text", c.Text()).
Msg("Got alerts query")

grafanaGroups, err := a.AlertSources[0].GetAlertingRules()
// TODO: fix
grafanaGroups, err := a.AlertSourcesWithSilenceManager[0].AlertSource.GetAlertingRules()
if err != nil {
return c.Reply(fmt.Sprintf("Error querying alerts: %s", err))
}

prometheusGroups, err := a.AlertSources[1].GetAlertingRules()
prometheusGroups, err := a.AlertSourcesWithSilenceManager[1].AlertSource.GetAlertingRules()
if err != nil {
return c.Reply(fmt.Sprintf("Error querying alerts: %s", err))
}

template, err := a.TemplateManager.Render("alerts_list", render.RenderStruct{
return a.ReplyRender(c, "alerts_list", render.RenderStruct{
Grafana: a.Grafana,
Data: types.AlertsListStruct{
GrafanaGroups: grafanaGroups,
PrometheusGroups: prometheusGroups,
},
})
if err != nil {
a.Logger.Error().Err(err).Msg("Error rendering alerts_list template")
return c.Reply(fmt.Sprintf("Error rendering template: %s", err))
}

return a.BotReply(c, template)
}
Loading

0 comments on commit de95fd6

Please sign in to comment.