Skip to content

Commit

Permalink
Major refactor to use filters in prometheus#633
Browse files Browse the repository at this point in the history
 * refactor alert fetch to pull filters
 * discard all multiple query logic in favor of simpler regex
 * discard all matching logic
 * cleanup a number of small things that made the code odd
 * relax the regex in pkg/parse/parse.go to allow no quote characters
  • Loading branch information
Kellen Fox committed Apr 6, 2017
1 parent fba0793 commit 32a8973
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 373 deletions.
118 changes: 46 additions & 72 deletions cli/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,33 @@ import (
"net/http"
"net/url"
"path"
"strings"
"time"

"github.com/prometheus/alertmanager/cli/format"
"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
)

type alertmanagerAlertResponse struct {
Status string `json:"status"`
Data model.Alerts `json:"data,omitempty"`
ErrorType string `json:"errorType,omitempty"`
Error string `json:"error,omitempty"`
Status string `json:"status"`
Data []*alertGroup `json:"data,omitempty"`
ErrorType string `json:"errorType,omitempty"`
Error string `json:"error,omitempty"`
}

type alertGroup struct {
Labels model.LabelSet `json:"labels"`
GroupKey uint64 `json:"groupKey"`
Blocks []*alertBlock `json:"blocks"`
}

type alertBlock struct {
RouteOpts interface{} `json:"routeOpts"`
Alerts []*dispatch.APIAlert `json:"alerts"`
}

var alertFlags *flag.FlagSet
Expand Down Expand Up @@ -52,17 +64,6 @@ var alertCmd = &cobra.Command{
As well as direct equality, regex matching is also supported. The '=~' syntax
(similar to prometheus) is used to represent a regex match. Regex matching
can be used in combination with a direct match.
amtool alert query alertname=foo node={bar,baz}
This query will match all alerts with the alertname=foo label value pair
and EITHER node=bar or node=baz.
amtool alert query alertname=foo{a,b} node={bar,baz}
Similar to the previous example this query will match all alerts with any
combination of alertname=fooa or alertname=foob AND node=bar or node=baz.
`,
RunE: queryAlerts,
}
Expand All @@ -74,54 +75,47 @@ func init() {
alertFlags = alertCmd.Flags()
}

func fetchAlerts(filter *[]labels.Matcher) (model.Alerts, error) {
func fetchAlerts(filter string) ([]*dispatch.APIAlert, error) {
alertResponse := alertmanagerAlertResponse{}

u, err := GetAlertmanagerURL()
if err != nil {
return model.Alerts{}, err
}

u.Path = path.Join(u.Path, "/api/v1/alerts")
if filter != nil {
u.RawQuery = url.QueryEscape(MatchersToString(*filter))
return []*dispatch.APIAlert{}, err
}

fmt.Println(u.String())
u.Path = path.Join(u.Path, "/api/v1/alerts/groups")
u.RawQuery = "filter=" + url.QueryEscape(filter)

res, err := http.Get(u.String())
if err != nil {
return model.Alerts{}, err
return []*dispatch.APIAlert{}, err
}

defer res.Body.Close()
decoder := json.NewDecoder(res.Body)

err = decoder.Decode(&alertResponse)
err = json.NewDecoder(res.Body).Decode(&alertResponse)
if err != nil {
return model.Alerts{}, errors.New("Unable to decode json response")
return []*dispatch.APIAlert{}, errors.New("Unable to decode json response")
}
return alertResponse.Data, nil
}

func queryAlerts(cmd *cobra.Command, args []string) error {
silences, err := fetchSilences(nil)
if err != nil {
return err
if alertResponse.Status != "success" {
return []*dispatch.APIAlert{}, fmt.Errorf("[%s] %s", alertResponse.ErrorType, alertResponse.Error)
}

var groups [][]labels.Matcher
if len(args) > 0 {
matchers, err := parseMatchers(args)
if err != nil {
return err
}
groups = parseMatcherGroups(matchers)
if err != nil {
return err
return flattenAlertOverview(alertResponse.Data), nil
}

func flattenAlertOverview(overview []*alertGroup) []*dispatch.APIAlert {
alerts := []*dispatch.APIAlert{}
for _, group := range overview {
for _, block := range group.Blocks {
alerts = append(alerts, block.Alerts...)
}
}
return alerts
}

func queryAlerts(cmd *cobra.Command, args []string) error {
expired, err := alertFlags.GetBool("expired")
if err != nil {
return err
Expand All @@ -132,25 +126,17 @@ func queryAlerts(cmd *cobra.Command, args []string) error {
return err
}

fetchedAlerts := model.Alerts{}
if len(groups) < 1 {
fmt.Println("No specified matchers")
fetchedAlerts, err = fetchAlerts(nil)
if err != nil {
return err
}
var filterString = ""
if len(args) > 0 {
filterString = fmt.Sprintf("{%s}", strings.Join(args, ","))
}

// Fetch all alerts that match each group of matchers
for _, matchers := range groups {
alerts, err := fetchAlerts(&matchers)
if err != nil {
return err
}
fetchedAlerts = append(fetchedAlerts, alerts...)
fetchedAlerts, err := fetchAlerts(filterString)
if err != nil {
return err
}

displayAlerts := model.Alerts{}
displayAlerts := []*dispatch.APIAlert{}
for _, alert := range fetchedAlerts {
// If we are only returning current alerts and this one has already expired skip it
if !expired {
Expand All @@ -161,23 +147,11 @@ func queryAlerts(cmd *cobra.Command, args []string) error {

if !showSilenced {
// If any silence mutes this alert don't show it
silenced := false
for _, silence := range silences {
// Need to call Init before Mutes
err = silence.Init()
if err != nil {
return err
}

if silence.Mutes(alert.Labels) {
silenced = true
break
}
}
if silenced {
if alert.Silenced != "" {
continue
}
}

displayAlerts = append(displayAlerts, alert)
}

Expand Down
4 changes: 2 additions & 2 deletions cli/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"time"

"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
"github.com/spf13/viper"
)

Expand All @@ -25,7 +25,7 @@ type Config struct {
type Formatter interface {
SetOutput(io.Writer)
FormatSilences([]types.Silence) error
FormatAlerts(model.Alerts) error
FormatAlerts([]*dispatch.APIAlert) error
FormatConfig(Config) error
}

Expand Down
13 changes: 8 additions & 5 deletions cli/format/format_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"
"text/tabwriter"

"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
)
Expand All @@ -29,7 +30,9 @@ func (formatter *ExtendedFormatter) FormatSilences(silences []types.Silence) err
sort.Sort(ByEndAt(silences))
fmt.Fprintln(w, "ID\tMatchers\tStarts At\tEnds At\tUpdated At\tCreated By\tComment\t")
for _, silence := range silences {
line := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t",
fmt.Fprintf(
w,
"%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
silence.ID,
extendedFormatMatchers(silence.Matchers),
FormatDate(silence.StartsAt),
Expand All @@ -38,25 +41,25 @@ func (formatter *ExtendedFormatter) FormatSilences(silences []types.Silence) err
silence.CreatedBy,
silence.Comment,
)
fmt.Fprintln(w, line)
}
w.Flush()
return nil
}

func (formatter *ExtendedFormatter) FormatAlerts(alerts model.Alerts) error {
func (formatter *ExtendedFormatter) FormatAlerts(alerts []*dispatch.APIAlert) error {
w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0)
sort.Sort(ByStartsAt(alerts))
fmt.Fprintln(w, "Labels\tAnnotations\tStarts At\tEnds At\tGenerator URL\t")
for _, alert := range alerts {
line := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t",
fmt.Fprintf(
w,
"%s\t%s\t%s\t%s\t%s\t\n",
extendedFormatLabels(alert.Labels),
extendedFormatAnnotations(alert.Annotations),
FormatDate(alert.StartsAt),
FormatDate(alert.EndsAt),
alert.GeneratorURL,
)
fmt.Fprintln(w, line)
}
w.Flush()
return nil
Expand Down
4 changes: 2 additions & 2 deletions cli/format/format_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"io"
"os"

"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
)

type JsonFormatter struct {
Expand All @@ -26,7 +26,7 @@ func (formatter *JsonFormatter) FormatSilences(silences []types.Silence) error {
return enc.Encode(silences)
}

func (formatter *JsonFormatter) FormatAlerts(alerts model.Alerts) error {
func (formatter *JsonFormatter) FormatAlerts(alerts []*dispatch.APIAlert) error {
enc := json.NewEncoder(formatter.writer)
return enc.Encode(alerts)
}
Expand Down
16 changes: 8 additions & 8 deletions cli/format/format_simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"strings"
"text/tabwriter"

"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
)

type SimpleFormatter struct {
Expand All @@ -29,32 +29,32 @@ func (formatter *SimpleFormatter) FormatSilences(silences []types.Silence) error
sort.Sort(ByEndAt(silences))
fmt.Fprintln(w, "ID\tMatchers\tEnds At\tCreated By\tComment\t")
for _, silence := range silences {

line := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t",
fmt.Fprintf(
w,
"%s\t%s\t%s\t%s\t%s\t\n",
silence.ID,
simpleFormatMatchers(silence.Matchers),
FormatDate(silence.EndsAt),
silence.CreatedBy,
silence.Comment,
)
fmt.Fprintln(w, line)
}
w.Flush()
return nil
}

func (formatter *SimpleFormatter) FormatAlerts(alerts model.Alerts) error {
func (formatter *SimpleFormatter) FormatAlerts(alerts []*dispatch.APIAlert) error {
w := tabwriter.NewWriter(formatter.writer, 0, 0, 2, ' ', 0)
sort.Sort(ByStartsAt(alerts))
//alerts = UniqueAlerts(alerts)
fmt.Fprintln(w, "Alertname\tStarts At\tSummary\t")
for _, alert := range alerts {
line := fmt.Sprintf("%s\t%s\t%s\t",
fmt.Fprintf(
w,
"%s\t%s\t%s\t\n",
alert.Labels["alertname"],
FormatDate(alert.StartsAt),
alert.Annotations["summary"],
)
fmt.Fprintln(w, line)
}
w.Flush()
return nil
Expand Down
22 changes: 2 additions & 20 deletions cli/format/sort.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package format

import (
"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
)

type ByEndAt []types.Silence
Expand All @@ -11,26 +11,8 @@ func (s ByEndAt) Len() int { return len(s) }
func (s ByEndAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ByEndAt) Less(i, j int) bool { return s[i].EndsAt.Before(s[j].EndsAt) }

type ByStartsAt model.Alerts
type ByStartsAt []*dispatch.APIAlert

func (s ByStartsAt) Len() int { return len(s) }
func (s ByStartsAt) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ByStartsAt) Less(i, j int) bool { return s[i].StartsAt.Before(s[j].StartsAt) }

func UniqueAlerts(alerts model.Alerts) model.Alerts {
output := model.Alerts{}
for i := 0; i < len(alerts); i++ {
exists := false
for j := 0; j < i; j++ {
if alerts[j] == alerts[i] {
exists = true
break
}
}
if !exists {
output = append(output, alerts[i])
}
}

return output
}
Loading

0 comments on commit 32a8973

Please sign in to comment.