Implement deterministic alert group order, cleanup

This commit is contained in:
Fabian Reinartz 2015-11-10 14:52:04 +01:00
parent dc656a44ea
commit ede4b63a91
5 changed files with 50 additions and 36 deletions

4
api.go
View File

@ -38,7 +38,7 @@ type API struct {
config string
uptime time.Time
groups func() []*UIGroups
groups func() AlertOverview
// context is an indirection for testing.
context func(r *http.Request) context.Context
@ -46,7 +46,7 @@ type API struct {
}
// NewAPI returns a new API.
func NewAPI(alerts provider.Alerts, silences provider.Silences, gf func() []*UIGroups) *API {
func NewAPI(alerts provider.Alerts, silences provider.Silences, gf func() AlertOverview) *API {
return &API{
context: route.Context,
alerts: alerts,

View File

@ -2,6 +2,7 @@ package main
import (
"fmt"
"sort"
"sync"
"time"
@ -59,29 +60,40 @@ func (d *Dispatcher) Run() {
close(d.done)
}
// UIGroup is the representation of a group of alerts as provided by
// the API.
type UIGroup struct {
// AlertBlock contains a list of alerts associated with a set of
// routing options.
type AlertBlock struct {
RouteOpts *RouteOpts `json:"routeOpts"`
Alerts []*UIAlert `json:"alerts"`
Alerts []*APIAlert `json:"alerts"`
}
type UIGroups struct {
Labels model.LabelSet `json:"labels"`
Groups []*UIGroup `json:"groups"`
}
type UIAlert struct {
// APIAlert is the API representation of an alert, which is a regular alert
// annotated with silencing and inhibition info.
type APIAlert struct {
*model.Alert
Inhibited bool `json:"inhibited"`
Silenced uint64 `json:"silenced,omitempty"`
}
func (d *Dispatcher) Groups() []*UIGroups {
var groups []*UIGroups
// AlertGroup is a list of alert blocks grouped by the same label set.
type AlertGroup struct {
Labels model.LabelSet `json:"labels"`
Blocks []*AlertBlock `json:"blocks"`
}
seen := map[model.Fingerprint]*UIGroups{}
// AlertOverview is a representation of all active alerts in the system.
type AlertOverview []*AlertGroup
func (ao AlertOverview) Swap(i, j int) { ao[i], ao[j] = ao[j], ao[i] }
func (ao AlertOverview) Less(i, j int) bool { return ao[i].Labels.Before(ao[j].Labels) }
func (ao AlertOverview) Len() int { return len(ao) }
// Groups populates an AlertOverview from the dispatcher's internal state.
func (d *Dispatcher) Groups() AlertOverview {
var overview AlertOverview
seen := map[model.Fingerprint]*AlertGroup{}
for route, ags := range d.aggrGroups {
for _, ag := range ags {
@ -90,39 +102,41 @@ func (d *Dispatcher) Groups() []*UIGroups {
alerts = append(alerts, a)
}
uig, ok := seen[ag.labels.Fingerprint()]
alertGroup, ok := seen[ag.labels.Fingerprint()]
if !ok {
uig = &UIGroups{Labels: ag.labels}
alertGroup = &AlertGroup{Labels: ag.labels}
seen[ag.labels.Fingerprint()] = uig
groups = append(groups, uig)
seen[ag.labels.Fingerprint()] = alertGroup
overview = append(overview, alertGroup)
}
now := time.Now()
var uiAlerts []*UIAlert
var apiAlerts []*APIAlert
for _, a := range types.Alerts(alerts...) {
if a.EndsAt.Before(now) {
if !a.EndsAt.IsZero() && a.EndsAt.Before(now) {
continue
}
sid, _ := d.marker.Silenced(a.Fingerprint())
uiAlerts = append(uiAlerts, &UIAlert{
apiAlerts = append(apiAlerts, &APIAlert{
Alert: a,
Inhibited: d.marker.Inhibited(a.Fingerprint()),
Silenced: sid,
})
}
uig.Groups = append(uig.Groups, &UIGroup{
alertGroup.Blocks = append(alertGroup.Blocks, &AlertBlock{
RouteOpts: &route.RouteOpts,
Alerts: uiAlerts,
Alerts: apiAlerts,
})
}
}
return groups
sort.Sort(overview)
return overview
}
func (d *Dispatcher) run(it provider.AlertIterator) {

View File

@ -69,7 +69,7 @@ func main() {
)
defer disp.Stop()
api := NewAPI(alerts, silences, func() []*UIGroups {
api := NewAPI(alerts, silences, func() AlertOverview {
return disp.Groups()
})

View File

@ -174,9 +174,9 @@ angular.module('am.controllers').controller('AlertsCtrl',
$scope.notEmpty = function(group) {
var l = 0;
angular.forEach(group.groups, function(g) {
if ($scope.receiver.indexOf(g.routeOpts.receiver) >= 0) {
l += g.alerts.length;
angular.forEach(group.blocks, function(blk) {
if ($scope.receivers.indexOf(blk.routeOpts.receiver) >= 0) {
l += blk.alerts.length || 0;
}
});
@ -190,9 +190,9 @@ angular.module('am.controllers').controller('AlertsCtrl',
$scope.allReceivers = [];
angular.forEach($scope.groups, function(group) {
angular.forEach(group.groups, function(g) {
if ($scope.allReceivers.indexOf(g.routeOpts.receiver) < 0) {
$scope.allReceivers.push(g.routeOpts.receiver);
angular.forEach(group.blocks, function(blk) {
if ($scope.allReceivers.indexOf(blk.routeOpts.receiver) < 0) {
$scope.allReceivers.push(blk.routeOpts.receiver);
}
})
});

View File

@ -13,9 +13,9 @@
</span>
</div>
<div ng-repeat="g in group.groups">
<div ng-show="receivers.indexOf(g.routeOpts.receiver) >= 0" ng-show"g.alerts">
<div ng-repeat="a in g.alerts">
<div ng-repeat="blk in group.blocks">
<div ng-show="receivers.indexOf(blk.routeOpts.receiver) >= 0" ng-show"blk.alerts">
<div ng-repeat="a in blk.alerts">
<alert class="list-item" alert="a" group="group.labels"></alert>
</div>
</div>