Skip to content

Instantly share code, notes, and snippets.

@slok
Last active November 11, 2021 06:10
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slok/314d591df5539905689a5ed32548537e to your computer and use it in GitHub Desktop.
Save slok/314d591df5539905689a5ed32548537e to your computer and use it in GitHub Desktop.
Alertmanager alarm tester. Fire a custom alert to your alertmanager: https://github.com/prometheus/alertmanager
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"regexp"
"strings"
"time"
)
const (
// alertmanager alerts api path.
amAPIPath = "api/v1/alerts"
)
var (
// label regular expression.
labelRgx = regexp.MustCompile("[^=]+=[^=]+")
)
// labelMap is the type to pass multiple labels as flags.
type labelMap map[string]string
// String implements flag.Value interface.
func (l *labelMap) String() string {
return fmt.Sprintf("%v", *l)
}
// Set implements flag.Value interface.
func (l *labelMap) Set(value string) error {
// Check label.
if !labelRgx.Match([]byte(value)) {
return fmt.Errorf("label %s is not in key value format of 'xxx=yyyy'", value)
}
// set the label.
spl := strings.Split(value, "=")
(*l)[spl[0]] = spl[1] // We tust our regexp
return nil
}
// Parses a url to check if it's correct and sets the alertmanagers alerts API path.
func parseURL(checkURL string) (*url.URL, error) {
// Check if is a valid url.
amURL, err := url.ParseRequestURI(checkURL)
if err != nil {
return nil, fmt.Errorf("wrong alertmanager url: %s", checkURL)
}
amURL.Path = amAPIPath
return amURL, nil
}
// amFirer fires alerts to alertmanager.
type amFirer struct {
amURL *url.URL // amURL is the alertmanager url.
}
// fire fires an alert.
func (a *amFirer) fire(alertName string, labels map[string]string) error {
// Add the alertname.
labels["alertname"] = alertName
// Create the alarm body.
value := []struct {
Status string `json:"status"`
Labels map[string]string `json:"labels"`
}{
{
Status: "firing",
Labels: labels,
},
}
body, err := json.Marshal(&value)
if err != nil {
return fmt.Errorf("error generating alert body: %s", err)
}
// Send the alarm.
if _, err := http.Post(a.amURL.String(), "application/json", bytes.NewBuffer(body)); err != nil {
return fmt.Errorf("failed seding the alert: %v", err)
}
return nil
}
// main will trigger a new alarm on the desired alertmanager, this could be used with test purpouses
// or a quick & easy way of manualy triggering an alarm. (https://github.com/prometheus/alertmanager)
// build:
// go build -o amfirer amfirer.go
// Usage examples:
// amfirer -l severity="warning"
// amfirer -l email="true" -address="http://alerts.mycompany.io"
// amfirer -l label_team="team-marketplace" -l severity="pager" -alertname="ClusterIsOnFireOMG"
func main() {
// flags stuff.
var (
amAddr string
labels = labelMap{}
alertName string
)
defAlertName := fmt.Sprintf("test_%d", time.Now().UTC().Unix())
flag.StringVar(&amAddr, "address", "http://127.0.0.1:9093", "alertmanager address")
flag.Var(&labels, "l", "label for the alert in key value format: 'xxx=yyy'")
flag.StringVar(&alertName, "alertname", defAlertName, "the name of the alert")
flag.Parse()
// Set correct url.
amURL, error := parseURL(amAddr)
if error != nil {
log.Fatalf("[ERROR] error parsing alertmanager url: %v", error)
}
f := &amFirer{amURL: amURL}
// Fire the alert.
if err := f.fire(alertName, map[string]string(labels)); err != nil {
log.Fatalf("[ERROR] error firing the alert: %v", err)
}
log.Printf("[OK] Alert %s sent to %s", alertName, f.amURL.String())
}
@slok
Copy link
Author

slok commented Nov 11, 2021

Thanks for the info @till! I did this a long time ago 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment