Files
karma/internal/models/silence.go
2026-03-11 13:10:15 +00:00

144 lines
3.1 KiB
Go

package models
import (
"regexp"
"time"
"github.com/go-json-experiment/json/jsontext"
"github.com/prymitive/karma/internal/regex"
)
type SilenceMatcher struct {
re *regexp.Regexp
Name string `json:"name"`
Value string `json:"value"`
IsRegex bool `json:"isRegex"`
IsEqual bool `json:"isEqual"`
}
func NewSilenceMatcher(name, value string, isRegexp, isEqual bool) SilenceMatcher {
sm := SilenceMatcher{
Name: name,
Value: value,
IsRegex: isRegexp,
IsEqual: isEqual,
}
if sm.IsRegex {
sm.re = regex.MustCompileAnchored(sm.Value)
}
return sm
}
func (sm SilenceMatcher) MarshalJSONTo(enc *jsontext.Encoder) error {
w := jsonWriter{enc: enc}
sm.marshalTo(&w)
return w.err
}
func (sm *SilenceMatcher) marshalTo(w *jsonWriter) {
w.beginObject()
w.key("name")
w.str(sm.Name)
w.key("value")
w.str(sm.Value)
w.key("isRegex")
w.boolean(sm.IsRegex)
w.key("isEqual")
w.boolean(sm.IsEqual)
w.endObject()
}
func (sm SilenceMatcher) IsMatch(labels map[string]string) bool {
v, ok := labels[sm.Name]
if !ok {
return !sm.IsEqual
}
if sm.IsRegex {
return sm.IsEqual == sm.re.MatchString(v)
}
return sm.IsEqual == (sm.Value == v)
}
// Silence is vanilla silence + some additional attributes
// karma adds JIRA support, it can extract JIRA IDs from comments
// extracted ID is used to generate link to JIRA issue
// this means karma needs to store additional fields for each silence
type Silence struct {
StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"`
CreatedAt time.Time `json:"createdAt"`
ID string `json:"id"`
CreatedBy string `json:"createdBy"`
Comment string `json:"comment"`
TicketID string `json:"ticketID"`
TicketURL string `json:"ticketURL"`
Matchers []SilenceMatcher `json:"matchers"`
}
func (s Silence) MarshalJSONTo(enc *jsontext.Encoder) error {
w := jsonWriter{enc: enc}
s.marshalTo(&w)
return w.err
}
func (s *Silence) marshalTo(w *jsonWriter) {
w.beginObject()
w.key("startsAt")
w.time(s.StartsAt)
w.key("endsAt")
w.time(s.EndsAt)
w.key("createdAt")
w.time(s.CreatedAt)
w.key("id")
w.str(s.ID)
w.key("createdBy")
w.str(s.CreatedBy)
w.key("comment")
w.str(s.Comment)
w.key("ticketID")
w.str(s.TicketID)
w.key("ticketURL")
w.str(s.TicketURL)
w.key("matchers")
w.beginArray()
for i := range s.Matchers {
s.Matchers[i].marshalTo(w)
}
w.endArray()
w.endObject()
}
func (s Silence) IsMatch(labels map[string]string) bool {
for _, m := range s.Matchers {
if !m.IsMatch(labels) {
return false
}
}
return true
}
// ManagedSilence is a standalone silence detached from any alert
type ManagedSilence struct {
Cluster string `json:"cluster"`
Silence Silence `json:"silence"`
AlertCount int `json:"alertCount"`
IsExpired bool `json:"isExpired"`
}
func (ms ManagedSilence) MarshalJSONTo(enc *jsontext.Encoder) error {
w := jsonWriter{enc: enc}
w.beginObject()
w.key("cluster")
w.str(ms.Cluster)
w.key("silence")
ms.Silence.marshalTo(&w)
w.key("alertCount")
w.integer(ms.AlertCount)
w.key("isExpired")
w.boolean(ms.IsExpired)
w.endObject()
return w.err
}