Skip to content

Commit

Permalink
feat: list silences
Browse files Browse the repository at this point in the history
  • Loading branch information
freak12techno committed Mar 26, 2022
1 parent 04e4d01 commit 9d89f57
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 4 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

grafana-interacter is a tool to interact with your Grafana instance via a Telegram bot. Here's the list of currently supported commands:
- `/render [<opts>] <panel name>` - renders the panel and sends it as image. If there are multiple panels with the same name (for example, you have a `dashboard1` and `dashboard2` both containing panel with name `panel`), it will render the first panel it will find. For specifying it, you may add the dashboard name as a prefix to your query (like `/render dashboard1 panel`). You can also provide options in a `key=value` format, which will be internally passed to a `/render` query to Grafana. Some examples are `from`, `to`, `width`, `height` (the command would look something like `/render from=now-14d to=now-7d width=100 height=100 dashboard1 panel`). By default, the params are: `width=1000&height=500&from=now-30m&to=now&tz=Europe/Moscow`.
- `/dashboards` - will list Grafana dashboards and links to them
- `/dashboard <name>` - will return a link to a dashboard and its panels
- `/datasources` - will return Grafana datasources
- `/dashboards` - will list Grafana dashboards and links to them.
- `/dashboard <name>` - will return a link to a dashboard and its panels.
- `/datasources` - will return Grafana datasources.
- `/alerts` - will list both Grafana alerts and Prometheus alerts from all Prometheus datasources, if any
- `/silence <duration> <params>` - creates a silence for Grafana alert. You need to pass a duration (like `/silence 2h test alert`) and some params for matching alerts to silence. You may use `=` for matching the value exactly (example: `/silence 2h host=localhost`), `!=` for matching everything except this value (example: `/silence 2h host!=localhost`), `=~` for matching everything that matches the regexp (example: `/silence 2h host=~local`), , `!~` for matching everything that doesn't the regexp (example: `/silence 2h host!~local`), or just provide a string that will be treated as an alert name (example: `/silence 2h test alert`).
- `/silences` - list silences (both active and expired).

## How can I set it up?

Expand Down
7 changes: 7 additions & 0 deletions grafana.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ func (g *GrafanaStruct) CreateSilence(silence Silence) error {
return err
}

func (g *GrafanaStruct) GetSilences() ([]Silence, error) {
silences := []Silence{}
url := g.RelativeLink("/api/alertmanager/grafana/api/v2/silences")
err := g.QueryAndDecode(url, &silences)
return silences, err
}

func (g *GrafanaStruct) Query(url string) (io.ReadCloser, error) {
client := &http.Client{}
req, _ := http.NewRequest("GET", url, nil)
Expand Down
24 changes: 23 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func Execute(cmd *cobra.Command, args []string) {
b.Handle("/datasources", HandleListDatasources)
b.Handle("/alerts", HandleListAlerts)
b.Handle("/alert", HandleSingleAlert)
b.Handle("/silences", HandleListSilences)
b.Handle("/silence", HandleNewSilence)

log.Info().Msg("Telegram bot listening")
Expand Down Expand Up @@ -301,7 +302,28 @@ func HandleNewSilence(c tele.Context) error {
return c.Reply(fmt.Sprintf("Error creating silence: %s", silenceErr))
}

return c.Reply("Silence created.")
return BotReply(c, "Silence created.")
}

func HandleListSilences(c tele.Context) error {
log.Info().
Str("sender", c.Sender().Username).
Str("text", c.Text()).
Msg("Got list silence query")

silences, err := Grafana.GetSilences()
if err != nil {
return c.Reply(fmt.Sprintf("Error listing silence: %s", err))
}

var sb strings.Builder
sb.WriteString(fmt.Sprintf("<strong>Silences</strong>\n"))

for _, silence := range silences {
sb.WriteString(silence.Serialize() + "\n\n")
}

return BotReply(c, sb.String())
}

func main() {
Expand Down
34 changes: 34 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"strings"
"time"
)

Expand Down Expand Up @@ -103,6 +104,7 @@ type Silence struct {
EndsAt time.Time `json:"endsAt"`
ID string `json:"id,omitempty"`
Matchers []SilenceMatcher `json:"matchers"`
Status SilenceStatus `json:"status,omitempty"`
}

type SilenceMatcher struct {
Expand All @@ -112,10 +114,42 @@ type SilenceMatcher struct {
Value string `json:"value"`
}

type SilenceStatus struct {
State string `json:"state"`
}

func (rule *GrafanaAlertRule) Serialize(groupName string) string {
return fmt.Sprintf("- %s %s -> %s\n", GetEmojiByStatus(rule.State), groupName, rule.Name)
}

func (alert *GrafanaAlert) Serialize() string {
return fmt.Sprintf("- %s <pre>%s</pre>", GetEmojiByStatus(alert.State), SerializeAlertLabels(alert.Labels))
}

func (silence *Silence) Serialize() string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("Starts at: <pre>%s</pre>\n", silence.StartsAt.String()))
sb.WriteString(fmt.Sprintf("Ends at: <pre>%s</pre>\n", silence.EndsAt.String()))
sb.WriteString(fmt.Sprintf("Created by: <pre>%s</pre>\n", silence.CreatedBy))
sb.WriteString(fmt.Sprintf("Comment: <pre>%s</pre>\n", silence.Comment))
sb.WriteString(fmt.Sprintf("Status: <pre>%s</pre>\n", silence.Status.State))
sb.WriteString("Matchers: ")

for _, matcher := range silence.Matchers {
sb.WriteString("<pre>" + matcher.Serialize() + "</pre> ")
}

return sb.String()
}

func (matcher *SilenceMatcher) Serialize() string {
if matcher.IsEqual && matcher.IsRegex {
return fmt.Sprintf("%s ~= %s", matcher.Name, matcher.Value)
} else if matcher.IsEqual && !matcher.IsRegex {
return fmt.Sprintf("%s = %s", matcher.Name, matcher.Value)
} else if !matcher.IsEqual && matcher.IsRegex {
return fmt.Sprintf("%s !~ %s", matcher.Name, matcher.Value)
} else {
return fmt.Sprintf("%s != %s", matcher.Name, matcher.Value)
}
}

0 comments on commit 9d89f57

Please sign in to comment.