mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
Add mapper module allowing to pick Alertmanager schema handlers at runtime
This modules allows to have a dedicated path for unmarshaling Alertmanager reponse per Alertmanager version. It returns data in format handled internally by unsee (which is Alertmanager data + some additional attributes)
This commit is contained in:
59
mapper/mapper.go
Normal file
59
mapper/mapper.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package mapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/unsee/models"
|
||||
)
|
||||
|
||||
var (
|
||||
alertMappers = []AlertMapper{}
|
||||
silenceMappers = []SilenceMapper{}
|
||||
)
|
||||
|
||||
// AlertMapper implements Alertmanager -> unsee alert data mapping that works
|
||||
// for a specific range of Alertmanager versions
|
||||
type AlertMapper interface {
|
||||
IsSupported(version string) bool
|
||||
GetAlerts() ([]models.UnseeAlertGroup, error)
|
||||
}
|
||||
|
||||
// RegisterAlertMapper allows to register mapper implementing alert data
|
||||
// handling for specific Alertmanager versions
|
||||
func RegisterAlertMapper(m AlertMapper) {
|
||||
alertMappers = append(alertMappers, m)
|
||||
}
|
||||
|
||||
// GetAlertMapper returns mapper for given version
|
||||
func GetAlertMapper(version string) (AlertMapper, error) {
|
||||
for _, m := range alertMappers {
|
||||
if m.IsSupported(version) {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Can't find alert mapper for Alertmanager %s", version)
|
||||
}
|
||||
|
||||
// SilenceMapper implements Alertmanager -> unsee silence data mapping that
|
||||
// works for a specific range of Alertmanager versions
|
||||
type SilenceMapper interface {
|
||||
Release() string
|
||||
IsSupported(version string) bool
|
||||
GetSilences() ([]models.UnseeSilence, error)
|
||||
}
|
||||
|
||||
// RegisterSilenceMapper allows to register mapper implementing silence data
|
||||
// handling for specific Alertmanager versions
|
||||
func RegisterSilenceMapper(m SilenceMapper) {
|
||||
silenceMappers = append(silenceMappers, m)
|
||||
}
|
||||
|
||||
// GetSilenceMapper returns mapper for given version
|
||||
func GetSilenceMapper(version string) (SilenceMapper, error) {
|
||||
for _, m := range silenceMappers {
|
||||
if m.IsSupported(version) {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Can't find silence mapper for Alertmanager %s", version)
|
||||
}
|
||||
101
mapper/v04/alerts.go
Normal file
101
mapper/v04/alerts.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// Package v04 package implements support for interacting with Alertmanager 0.4
|
||||
// Collected data will be mapped to unsee internal schema defined the
|
||||
// unsee/models package
|
||||
// This file defines Alertmanager alerts mapping
|
||||
package v04
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/cloudflare/unsee/config"
|
||||
"github.com/cloudflare/unsee/mapper"
|
||||
"github.com/cloudflare/unsee/models"
|
||||
"github.com/cloudflare/unsee/transport"
|
||||
)
|
||||
|
||||
// AlertmanagerAlert is vanilla alert object from Alertmanager 0.4
|
||||
type alert struct {
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
StartsAt time.Time `json:"startsAt"`
|
||||
EndsAt time.Time `json:"endsAt"`
|
||||
GeneratorURL string `json:"generatorURL"`
|
||||
Inhibited bool `json:"inhibited"`
|
||||
Silenced int `json:"silenced"`
|
||||
}
|
||||
|
||||
// alertsGroupsV04 is vanilla group object from Alertmanager, exposed under api/v1/alerts/groups
|
||||
type alertsGroups struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Blocks []struct {
|
||||
Alerts []alert `json:"alerts"`
|
||||
} `json:"blocks"`
|
||||
}
|
||||
|
||||
type alertsGroupsAPISchema struct {
|
||||
Status string `json:"status"`
|
||||
Groups []alertsGroups `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// AlertMapper implements Alertmanager 0.4 API schema
|
||||
type AlertMapper struct {
|
||||
mapper.AlertMapper
|
||||
}
|
||||
|
||||
// IsSupported returns true if given version string is supported
|
||||
func (m AlertMapper) IsSupported(version string) bool {
|
||||
versionRange := semver.MustParseRange(">=0.4.0 <0.5.0")
|
||||
return versionRange(semver.MustParse(version))
|
||||
}
|
||||
|
||||
// GetAlerts will make a request to Alertmanager API and parse the response
|
||||
// It will only return alerts or error (if any)
|
||||
func (m AlertMapper) GetAlerts() ([]models.UnseeAlertGroup, error) {
|
||||
groups := []models.UnseeAlertGroup{}
|
||||
resp := alertsGroupsAPISchema{}
|
||||
|
||||
url, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/alerts/groups")
|
||||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
|
||||
err = transport.GetJSONFromURL(url, config.Config.AlertmanagerTimeout, &resp)
|
||||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
|
||||
if resp.Status != "success" {
|
||||
return groups, errors.New(resp.Error)
|
||||
}
|
||||
|
||||
for _, g := range resp.Groups {
|
||||
alertList := models.UnseeAlertList{}
|
||||
for _, b := range g.Blocks {
|
||||
for _, a := range b.Alerts {
|
||||
us := models.UnseeAlert{
|
||||
AlertmanagerAlert: models.AlertmanagerAlert{
|
||||
Annotations: a.Annotations,
|
||||
Labels: a.Labels,
|
||||
StartsAt: a.StartsAt,
|
||||
EndsAt: a.EndsAt,
|
||||
GeneratorURL: a.GeneratorURL,
|
||||
Inhibited: a.Inhibited,
|
||||
},
|
||||
}
|
||||
if a.Silenced > 0 {
|
||||
us.Silenced = string(a.Silenced)
|
||||
}
|
||||
alertList = append(alertList, us)
|
||||
}
|
||||
}
|
||||
ug := models.UnseeAlertGroup{
|
||||
Labels: g.Labels,
|
||||
Alerts: alertList,
|
||||
}
|
||||
groups = append(groups, ug)
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
93
mapper/v04/silences.go
Normal file
93
mapper/v04/silences.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Package v04 package implements support for interacting with Alertmanager 0.4
|
||||
// Collected data will be mapped to unsee internal schema defined the
|
||||
// unsee/models package
|
||||
// This file defines Alertmanager silences mapping
|
||||
package v04
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/cloudflare/unsee/config"
|
||||
"github.com/cloudflare/unsee/mapper"
|
||||
"github.com/cloudflare/unsee/models"
|
||||
"github.com/cloudflare/unsee/transport"
|
||||
)
|
||||
|
||||
// Alertmanager 0.4 silence format
|
||||
type silence struct {
|
||||
ID int `json:"id"`
|
||||
Matchers []struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
IsRegex bool `json:"isRegex"`
|
||||
} `json:"matchers"`
|
||||
StartsAt time.Time `json:"startsAt"`
|
||||
EndsAt time.Time `json:"endsAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
// silenceAPIResponseV04 is what Alertmanager 0.4 API returns
|
||||
type silenceAPISchema struct {
|
||||
Status string `json:"status"`
|
||||
Data struct {
|
||||
Silences []silence `json:"silences"`
|
||||
TotalSilences int `json:"totalSilences"`
|
||||
} `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// SilenceMapper implements Alertmanager 0.4 API schema
|
||||
type SilenceMapper struct {
|
||||
mapper.SilenceMapper
|
||||
}
|
||||
|
||||
// IsSupported returns true if given version string is supported
|
||||
func (m SilenceMapper) IsSupported(version string) bool {
|
||||
versionRange := semver.MustParseRange(">=0.4.0 <0.5.0")
|
||||
return versionRange(semver.MustParse(version))
|
||||
}
|
||||
|
||||
// GetSilences will make a request to Alertmanager API and parse the response
|
||||
// It will only return silences or error (if any)
|
||||
func (m SilenceMapper) GetSilences() ([]models.UnseeSilence, error) {
|
||||
silences := []models.UnseeSilence{}
|
||||
resp := silenceAPISchema{}
|
||||
|
||||
url, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/silences")
|
||||
if err != nil {
|
||||
return silences, err
|
||||
}
|
||||
|
||||
// Alertmanager 0.4 uses pagination for silences
|
||||
url = fmt.Sprintf("%s?limit=%d", url, math.MaxUint32)
|
||||
err = transport.GetJSONFromURL(url, config.Config.AlertmanagerTimeout, &resp)
|
||||
if err != nil {
|
||||
return silences, err
|
||||
}
|
||||
|
||||
if resp.Status != "success" {
|
||||
return silences, errors.New(resp.Error)
|
||||
}
|
||||
|
||||
for _, s := range resp.Data.Silences {
|
||||
us := models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
ID: string(s.ID),
|
||||
Matchers: s.Matchers,
|
||||
StartsAt: s.StartsAt,
|
||||
EndsAt: s.EndsAt,
|
||||
CreatedAt: s.CreatedAt,
|
||||
CreatedBy: s.CreatedBy,
|
||||
Comment: s.Comment,
|
||||
},
|
||||
}
|
||||
silences = append(silences, us)
|
||||
}
|
||||
return silences, nil
|
||||
}
|
||||
97
mapper/v05/alerts.go
Normal file
97
mapper/v05/alerts.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Package v05 package implements support for interacting with Alertmanager 0.5
|
||||
// Collected data will be mapped to unsee internal schema defined the
|
||||
// unsee/models package
|
||||
// This file defines Alertmanager alerts mapping
|
||||
package v05
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/cloudflare/unsee/config"
|
||||
"github.com/cloudflare/unsee/mapper"
|
||||
"github.com/cloudflare/unsee/models"
|
||||
"github.com/cloudflare/unsee/transport"
|
||||
)
|
||||
|
||||
type alert struct {
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
StartsAt time.Time `json:"startsAt"`
|
||||
EndsAt time.Time `json:"endsAt"`
|
||||
GeneratorURL string `json:"generatorURL"`
|
||||
Inhibited bool `json:"inhibited"`
|
||||
Silenced string `json:"silenced"`
|
||||
}
|
||||
|
||||
type alertsGroups struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Blocks []struct {
|
||||
Alerts []alert `json:"alerts"`
|
||||
} `json:"blocks"`
|
||||
}
|
||||
|
||||
type alertsGroupsAPISchema struct {
|
||||
Status string `json:"status"`
|
||||
Groups []alertsGroups `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// AlertMapper implements Alertmanager 0.5 API schema
|
||||
type AlertMapper struct {
|
||||
mapper.AlertMapper
|
||||
}
|
||||
|
||||
// IsSupported returns true if given version string is supported
|
||||
func (m AlertMapper) IsSupported(version string) bool {
|
||||
versionRange := semver.MustParseRange(">=0.5.0")
|
||||
return versionRange(semver.MustParse(version))
|
||||
}
|
||||
|
||||
// GetAlerts will make a request to Alertmanager API and parse the response
|
||||
// It will only return alerts or error (if any)
|
||||
func (m AlertMapper) GetAlerts() ([]models.UnseeAlertGroup, error) {
|
||||
groups := []models.UnseeAlertGroup{}
|
||||
resp := alertsGroupsAPISchema{}
|
||||
|
||||
url, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/alerts/groups")
|
||||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
|
||||
err = transport.GetJSONFromURL(url, config.Config.AlertmanagerTimeout, &resp)
|
||||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
|
||||
if resp.Status != "success" {
|
||||
return groups, errors.New(resp.Error)
|
||||
}
|
||||
|
||||
for _, g := range resp.Groups {
|
||||
alertList := models.UnseeAlertList{}
|
||||
for _, b := range g.Blocks {
|
||||
for _, a := range b.Alerts {
|
||||
us := models.UnseeAlert{
|
||||
AlertmanagerAlert: models.AlertmanagerAlert{
|
||||
Annotations: a.Annotations,
|
||||
Labels: a.Labels,
|
||||
StartsAt: a.StartsAt,
|
||||
EndsAt: a.EndsAt,
|
||||
GeneratorURL: a.GeneratorURL,
|
||||
Inhibited: a.Inhibited,
|
||||
Silenced: a.Silenced,
|
||||
},
|
||||
}
|
||||
alertList = append(alertList, us)
|
||||
}
|
||||
}
|
||||
ug := models.UnseeAlertGroup{
|
||||
Labels: g.Labels,
|
||||
Alerts: alertList,
|
||||
}
|
||||
groups = append(groups, ug)
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
84
mapper/v05/silences.go
Normal file
84
mapper/v05/silences.go
Normal file
@@ -0,0 +1,84 @@
|
||||
// Package v05 package implements support for interacting with Alertmanager 0.5
|
||||
// Collected data will be mapped to unsee internal schema defined the
|
||||
// unsee/models package
|
||||
// This file defines Alertmanager alerts mapping
|
||||
package v05
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/cloudflare/unsee/config"
|
||||
"github.com/cloudflare/unsee/mapper"
|
||||
"github.com/cloudflare/unsee/models"
|
||||
"github.com/cloudflare/unsee/transport"
|
||||
)
|
||||
|
||||
type silence struct {
|
||||
ID string `json:"id"`
|
||||
Matchers []struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
IsRegex bool `json:"isRegex"`
|
||||
} `json:"matchers"`
|
||||
StartsAt time.Time `json:"startsAt"`
|
||||
EndsAt time.Time `json:"endsAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
type silenceAPISchema struct {
|
||||
Status string `json:"status"`
|
||||
Data []silence `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// SilenceMapper implements Alertmanager 0.4 API schema
|
||||
type SilenceMapper struct {
|
||||
mapper.SilenceMapper
|
||||
}
|
||||
|
||||
// IsSupported returns true if given version string is supported
|
||||
func (m SilenceMapper) IsSupported(version string) bool {
|
||||
versionRange := semver.MustParseRange(">=0.5.0")
|
||||
return versionRange(semver.MustParse(version))
|
||||
}
|
||||
|
||||
// GetSilences will make a request to Alertmanager API and parse the response
|
||||
// It will only return silences or error (if any)
|
||||
func (m SilenceMapper) GetSilences() ([]models.UnseeSilence, error) {
|
||||
silences := []models.UnseeSilence{}
|
||||
resp := silenceAPISchema{}
|
||||
|
||||
url, err := transport.JoinURL(config.Config.AlertmanagerURI, "api/v1/silences")
|
||||
if err != nil {
|
||||
return silences, err
|
||||
}
|
||||
|
||||
err = transport.GetJSONFromURL(url, config.Config.AlertmanagerTimeout, &resp)
|
||||
if err != nil {
|
||||
return silences, err
|
||||
}
|
||||
|
||||
if resp.Status != "success" {
|
||||
return silences, errors.New(resp.Error)
|
||||
}
|
||||
|
||||
for _, s := range resp.Data {
|
||||
us := models.UnseeSilence{
|
||||
AlertmanagerSilence: models.AlertmanagerSilence{
|
||||
ID: s.ID,
|
||||
Matchers: s.Matchers,
|
||||
StartsAt: s.StartsAt,
|
||||
EndsAt: s.EndsAt,
|
||||
CreatedAt: s.CreatedAt,
|
||||
CreatedBy: s.CreatedBy,
|
||||
Comment: s.Comment,
|
||||
},
|
||||
}
|
||||
silences = append(silences, us)
|
||||
}
|
||||
return silences, nil
|
||||
}
|
||||
Reference in New Issue
Block a user