mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
Use new alertmanager package methods for fetching AM data
Change to using mapper module, remove old code
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
package alertmanager
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/unsee/config"
|
||||
"github.com/cloudflare/unsee/models"
|
||||
"github.com/cloudflare/unsee/transport"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// AlertGroupsAPIResponse is the schema of API response for /api/v1/alerts/groups
|
||||
type AlertGroupsAPIResponse struct {
|
||||
Status string `json:"status"`
|
||||
Groups []models.AlertmanagerAlertGroup `json:"data"`
|
||||
ErrorType string `json:"errorType"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// Get response from Alertmanager /api/v1/alerts/groups
|
||||
func (response *AlertGroupsAPIResponse) Get() error {
|
||||
start := time.Now()
|
||||
|
||||
url, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/alerts/groups")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = transport.GetJSONFromURL(url, config.Config.AlertmanagerTimeout, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Status != "success" {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
|
||||
log.Infof("Got %d alert group(s) in %s", len(response.Groups), time.Since(start))
|
||||
return nil
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package alertmanager_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/cloudflare/unsee/alertmanager"
|
||||
httpmock "gopkg.in/jarcoal/httpmock.v1"
|
||||
)
|
||||
|
||||
func TestAlertGroupsAPIResponseGet(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard) // disable logging to console
|
||||
httpmock.Activate()
|
||||
defer httpmock.DeactivateAndReset()
|
||||
mockJSON, err := ioutil.ReadFile("../mock/api/v1/alerts/groups")
|
||||
if err != nil {
|
||||
t.Errorf("Can't open mock 'alerts/groups' file: %s", err.Error())
|
||||
}
|
||||
httpmock.RegisterResponder("GET", "api/v1/alerts/groups", httpmock.NewBytesResponder(200, mockJSON))
|
||||
|
||||
response := alertmanager.AlertGroupsAPIResponse{}
|
||||
err = response.Get()
|
||||
if err != nil {
|
||||
t.Errorf("AlertGroupsAPIResponse.Get() failed: %s", err.Error())
|
||||
}
|
||||
if response.Status != "success" {
|
||||
t.Errorf("Invalid AlertGroupsAPIResponse status: %s", response.Status)
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package alertmanager
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/unsee/config"
|
||||
"github.com/cloudflare/unsee/models"
|
||||
"github.com/cloudflare/unsee/transport"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// SilenceAPIResponse is what Alertmanager API returns
|
||||
type SilenceAPIResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data []models.AlertmanagerSilence `json:"data"`
|
||||
ErrorType string `json:"errorType"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// Get will return fresh data from Alertmanager API
|
||||
func (response *SilenceAPIResponse) Get() error {
|
||||
start := time.Now()
|
||||
|
||||
url, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/silences")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
url = fmt.Sprintf("%s?limit=%d", url, math.MaxUint32)
|
||||
|
||||
err = transport.GetJSONFromURL(url, config.Config.AlertmanagerTimeout, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Status != "success" {
|
||||
return errors.New(response.Error)
|
||||
}
|
||||
|
||||
log.Infof("Got %d silences(s) in %s", len(response.Data), time.Since(start))
|
||||
return nil
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package alertmanager_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/cloudflare/unsee/alertmanager"
|
||||
httpmock "gopkg.in/jarcoal/httpmock.v1"
|
||||
)
|
||||
|
||||
func TestSilenceAPIResponseGet(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard) // disable logging to console
|
||||
httpmock.Activate()
|
||||
defer httpmock.DeactivateAndReset()
|
||||
mockJSON, err := ioutil.ReadFile("../mock/api/v1/silences")
|
||||
if err != nil {
|
||||
t.Errorf("Can't open mock 'silences' file: %s", err.Error())
|
||||
}
|
||||
httpmock.RegisterResponder("GET", "api/v1/silences", httpmock.NewBytesResponder(200, mockJSON))
|
||||
|
||||
response := alertmanager.SilenceAPIResponse{}
|
||||
err = response.Get()
|
||||
if err != nil {
|
||||
t.Errorf("SilenceAPIResponse.Get() failed: %s", err.Error())
|
||||
}
|
||||
if response.Status != "success" {
|
||||
t.Errorf("Invalid SilenceAPIResponse status: %s", response.Status)
|
||||
}
|
||||
}
|
||||
67
timer.go
67
timer.go
@@ -22,9 +22,9 @@ import (
|
||||
// from Alertmanager API, it's called by Ticker timer
|
||||
func PullFromAlertmanager() {
|
||||
log.Info("Pulling latest alerts and silences from Alertmanager")
|
||||
v := alertmanager.GetVersion()
|
||||
|
||||
silenceResponse := alertmanager.SilenceAPIResponse{}
|
||||
err := silenceResponse.Get()
|
||||
silences, err := alertmanager.GetSilences(v)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
errorLock.Lock()
|
||||
@@ -34,8 +34,7 @@ func PullFromAlertmanager() {
|
||||
return
|
||||
}
|
||||
|
||||
alertGroups := alertmanager.AlertGroupsAPIResponse{}
|
||||
err = alertGroups.Get()
|
||||
alertGroups, err := alertmanager.GetAlerts(v)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
errorLock.Lock()
|
||||
@@ -46,13 +45,9 @@ func PullFromAlertmanager() {
|
||||
}
|
||||
|
||||
silenceStore := make(map[string]models.UnseeSilence)
|
||||
for _, silence := range silenceResponse.Data {
|
||||
jiraID, jiraLink := transform.DetectJIRAs(&silence)
|
||||
silenceStore[silence.ID] = models.UnseeSilence{
|
||||
AlertmanagerSilence: silence,
|
||||
JiraID: jiraID,
|
||||
JiraURL: jiraLink,
|
||||
}
|
||||
for _, silence := range silences {
|
||||
silence.JiraID, silence.JiraURL = transform.DetectJIRAs(&silence)
|
||||
silenceStore[silence.ID] = silence
|
||||
}
|
||||
|
||||
store.Store.SetSilences(silenceStore)
|
||||
@@ -66,20 +61,10 @@ func PullFromAlertmanager() {
|
||||
var counterAlertsSilenced float64
|
||||
var counterAlertsUnsilenced float64
|
||||
|
||||
for _, alertGroup := range alertGroups.Groups {
|
||||
if len(alertGroup.Blocks) == 0 {
|
||||
// skip groups with empty blocks
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ag := range alertGroups {
|
||||
// used to generate group content hash
|
||||
agHasher := sha1.New()
|
||||
|
||||
ag := models.UnseeAlertGroup{
|
||||
Labels: alertGroup.Labels,
|
||||
Alerts: []models.UnseeAlert{},
|
||||
}
|
||||
|
||||
alerts := map[string]models.UnseeAlert{}
|
||||
|
||||
ignoredLabels := []string{}
|
||||
@@ -87,28 +72,28 @@ func PullFromAlertmanager() {
|
||||
ignoredLabels = append(ignoredLabels, il)
|
||||
}
|
||||
|
||||
for _, alertBlock := range alertGroup.Blocks {
|
||||
for _, alert := range alertBlock.Alerts {
|
||||
apiAlert := models.UnseeAlert{AlertmanagerAlert: alert}
|
||||
|
||||
apiAlert.Annotations, apiAlert.Links = transform.DetectLinks(apiAlert.Annotations)
|
||||
|
||||
apiAlert.Labels = transform.StripLables(ignoredLabels, apiAlert.Labels)
|
||||
|
||||
apiAlert.Fingerprint = fmt.Sprintf("%x", structhash.Sha1(apiAlert, 1))
|
||||
|
||||
// add alert to map if not yet present
|
||||
if _, found := alerts[apiAlert.Fingerprint]; !found {
|
||||
alerts[apiAlert.Fingerprint] = apiAlert
|
||||
io.WriteString(agHasher, apiAlert.Fingerprint) // alert group hasher
|
||||
}
|
||||
|
||||
for k, v := range alert.Labels {
|
||||
transform.ColorLabel(colorStore, k, v)
|
||||
}
|
||||
for _, alert := range ag.Alerts {
|
||||
// skip duplicated alerts
|
||||
if _, found := alerts[alert.Fingerprint]; found {
|
||||
continue
|
||||
}
|
||||
|
||||
alert.Annotations, alert.Links = transform.DetectLinks(alert.Annotations)
|
||||
alert.Labels = transform.StripLables(ignoredLabels, alert.Labels)
|
||||
alert.Fingerprint = fmt.Sprintf("%x", structhash.Sha1(alert, 1))
|
||||
|
||||
alerts[alert.Fingerprint] = alert
|
||||
|
||||
io.WriteString(agHasher, alert.Fingerprint) // alert group hasher
|
||||
|
||||
for k, v := range alert.Labels {
|
||||
transform.ColorLabel(colorStore, k, v)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reset alerts, we need to deduplicate
|
||||
ag.Alerts = []models.UnseeAlert{}
|
||||
for _, alert := range alerts {
|
||||
ag.Alerts = append(ag.Alerts, alert)
|
||||
if alert.Silenced != "" {
|
||||
|
||||
@@ -37,7 +37,7 @@ func ParseRules(rules []string) {
|
||||
// DetectJIRAs will try to find JIRA links in Alertmanager silence objects
|
||||
// using regexp rules from configuration that were parsed and populated
|
||||
// by ParseRules call
|
||||
func DetectJIRAs(silence *models.AlertmanagerSilence) (jiraID, jiraLink string) {
|
||||
func DetectJIRAs(silence *models.UnseeSilence) (jiraID, jiraLink string) {
|
||||
for _, jdr := range jiraDetectRules {
|
||||
jiraID := jdr.Regexp.FindString(silence.Comment)
|
||||
if jiraID != "" {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
type jiraTest struct {
|
||||
silence models.AlertmanagerSilence
|
||||
silence models.UnseeSilence
|
||||
jiraID string
|
||||
jiraLink string
|
||||
}
|
||||
@@ -20,54 +20,72 @@ var jiraRules = []string{
|
||||
|
||||
var jiraTests = []jiraTest{
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "Lorem ipsum dolor sit amet",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "Lorem ipsum dolor sit amet",
|
||||
},
|
||||
},
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "DVOPS-123",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "DVOPS-123",
|
||||
},
|
||||
},
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "DEVOPS team",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "DEVOPS team",
|
||||
},
|
||||
},
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "a project-1 b",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "a project-1 b",
|
||||
},
|
||||
},
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "a PROJECT- b",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "a PROJECT- b",
|
||||
},
|
||||
},
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "DEVOPS-1",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "DEVOPS-1",
|
||||
},
|
||||
},
|
||||
jiraID: "DEVOPS-1",
|
||||
jiraLink: "https://jira.example.com/browse/DEVOPS-1",
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "DEVOPS-123",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "DEVOPS-123",
|
||||
},
|
||||
},
|
||||
jiraID: "DEVOPS-123",
|
||||
jiraLink: "https://jira.example.com/browse/DEVOPS-123",
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "a DEVOPS-1 b",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "a DEVOPS-1 b",
|
||||
},
|
||||
},
|
||||
jiraID: "DEVOPS-1",
|
||||
jiraLink: "https://jira.example.com/browse/DEVOPS-1",
|
||||
},
|
||||
jiraTest{
|
||||
silence: models.AlertmanagerSilence{
|
||||
Comment: "PROJECT-9",
|
||||
silence: models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
Comment: "PROJECT-9",
|
||||
},
|
||||
},
|
||||
jiraID: "PROJECT-9",
|
||||
jiraLink: "https://example.com/browse/PROJECT-9",
|
||||
|
||||
Reference in New Issue
Block a user