Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sorting alerts by group name in /alerts #5448

Merged
merged 7 commits into from May 14, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 35 additions & 2 deletions rules/manager.go
Expand Up @@ -18,6 +18,7 @@ import (
"errors"
"math"
"net/url"
"path/filepath"
"sort"
"sync"
"time"
Expand Down Expand Up @@ -267,7 +268,7 @@ func NewGroup(name, file string, interval time.Duration, rules []Rule, shouldRes
func (g *Group) Name() string { return g.name }

// File returns the group's file.
func (g *Group) File() string { return g.file }
func (g *Group) File() string { return filepath.Base(g.file) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's ok to preserve the full path here (this function is already used by /rules as well, where it already shows the whole path, see e.g. http://demo.robustperception.io:9090/rules).


// Rules returns the group's rules.
func (g *Group) Rules() []Rule { return g.rules }
Expand Down Expand Up @@ -361,6 +362,38 @@ func (g *Group) hash() uint64 {
return l.Hash()
}

// AlertingRules returns the list of the group's alerting rules.
func (g *Group) AlertingRules() []*AlertingRule {
g.mtx.Lock()
defer g.mtx.Unlock()

var alerts []*AlertingRule
for _, rule := range g.rules {
if alertingRule, ok := rule.(*AlertingRule); ok {
alerts = append(alerts, alertingRule)
}
}
sort.Slice(alerts, func(i, j int) bool {
return alerts[i].State() > alerts[j].State() ||
(alerts[i].State() == alerts[j].State() &&
alerts[i].Name() < alerts[j].Name())
})
return alerts
}

// HasAlertingRules returns true if the group contains at least one AlertingRule.
func (g *Group) HasAlertingRules() bool {
g.mtx.Lock()
defer g.mtx.Unlock()

for _, rule := range g.rules {
if _, ok := rule.(*AlertingRule); ok {
return true
}
}
return false
}

// GetEvaluationDuration returns the time in seconds it took to evaluate the rule group.
func (g *Group) GetEvaluationDuration() time.Duration {
g.mtx.Lock()
Expand Down Expand Up @@ -871,7 +904,6 @@ func (m *Manager) RuleGroups() []*Group {
rgs = append(rgs, g)
}

// Sort rule groups by file, then by name.
sort.Slice(rgs, func(i, j int) bool {
if rgs[i].file != rgs[j].file {
return rgs[i].file < rgs[j].file
Expand Down Expand Up @@ -906,6 +938,7 @@ func (m *Manager) AlertingRules() []*AlertingRule {
alerts = append(alerts, alertingRule)
}
}

return alerts
}

Expand Down
4 changes: 2 additions & 2 deletions web/ui/assets_vfsdata.go
Expand Up @@ -633,9 +633,9 @@ var Assets = func() http.FileSystem {
"/templates/alerts.html": &vfsgen۰CompressedFileInfo{
name: "alerts.html",
modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC),
uncompressedSize: 2698,
uncompressedSize: 3065,

compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x56\xcd\x8e\xdb\x36\x10\xbe\xef\x53\x0c\x94\x1c\x5a\xa0\xb2\xb0\xd8\xe4\x50\x9b\x56\xb1\xc8\xa5\x87\x24\x28\xb2\xe9\x5e\x17\x14\x39\xb6\x18\x73\x49\x81\xa4\xbd\x76\x59\xbd\x7b\x41\x52\xf2\xca\xb2\xd4\xa4\x40\x61\x40\x20\x87\xc3\x6f\xbe\xf9\xe1\x8c\xbd\xe7\xb8\x11\x0a\x21\xab\x91\xf2\xac\x6d\x6f\x00\x88\x14\x6a\x07\xee\xd4\xe0\x3a\x73\x78\x74\x05\xb3\x36\x03\x83\x72\x9d\x59\x77\x92\x68\x6b\x44\x97\x41\x6d\x70\xb3\xce\xbc\x87\x86\xba\xfa\x0f\x83\x1b\x71\x84\xb6\x2d\xac\xa3\x4e\xb0\x70\xa7\xa0\x12\x8d\xb3\x0b\x66\xed\x6f\x87\xb5\xf7\x50\xed\x85\xe4\x8f\x68\xac\xd0\x0a\xda\x36\x2b\x83\x31\xcb\x8c\x68\x1c\x58\xc3\xe6\xc1\xbe\x9d\xb1\xbe\xcd\x41\x91\x22\x01\x95\x37\xde\xa3\xe2\x6d\x7b\x73\xf3\xea\x1b\xd3\xca\xa1\x72\xc1\x3d\xc2\xc5\x01\x98\xa4\xd6\xae\xa3\x98\x0a\x85\x26\xdf\xc8\xbd\xe0\x89\x4f\x7d\x5b\xde\x47\x5b\xa4\xa8\x6f\xa3\x64\x70\xc3\xd6\xfa\x25\xa7\x4a\xe9\xc0\x4b\x2b\x1b\xaf\x00\x00\x11\xbd\xc6\x56\x9e\x9a\x5a\x30\xad\xe0\xbc\xca\xf7\x8a\xd5\xc8\x76\xc8\x03\x4d\xd1\x5d\x01\x20\xd5\xde\x39\xad\xba\x48\xa7\x4d\x36\x6b\x09\x9c\x70\x12\xd3\x01\x5c\x50\x78\x18\x49\x48\x91\xb0\x22\xf9\x82\x8b\x43\x5c\x38\x5a\x49\xec\xd1\xd3\x26\x7e\xf3\x4a\x1b\x8e\x06\x79\xb7\x65\x5a\x4a\xda\x58\xe4\x9d\x6f\xc4\x55\x9a\x9f\xd2\xda\xfb\xb7\x31\x0f\x0f\x8e\x3a\xfc\xaa\xbf\xe8\x97\x0f\x01\x0f\x96\x6b\x58\xdc\x4f\x1c\xc4\x72\x0a\xd7\x0c\x55\x5b\xec\x74\x84\xda\x7e\xd9\x4b\xec\x0f\x13\x2a\x73\xe2\x80\x29\xee\x09\x6d\x20\x38\x2b\x12\x67\x7a\x07\x22\x0d\x88\xdf\xdc\x7b\xa1\x38\x1e\x61\x9a\xdb\x22\x0a\xda\x36\x29\x3f\x85\x32\x47\x93\x9d\x93\x00\xc4\xf1\xf2\x35\x7d\x31\x5f\xac\xc6\x83\xd1\x2a\xe7\xfa\x45\xa5\x94\x01\xa9\x4a\xef\x17\x9f\xe9\x33\xb6\x2d\x29\xaa\x12\x7e\xf2\x5e\xa2\x82\x0b\xe6\xc1\x48\xdc\xfe\x4c\x0a\xc7\x7b\x13\xa4\x70\xa6\x9c\xf1\xe0\x89\xa3\xa3\x42\xda\x11\x9f\xf3\x26\x55\xdf\x70\x0f\x40\x1a\x83\x10\x1f\xe2\x3a\xe3\xc2\x36\x92\x9e\x96\x95\xd4\x6c\xb7\x82\x86\x72\x2e\xd4\x76\xf9\xeb\xe2\x7d\x73\x5c\xc1\x46\x2b\x97\x5b\xf1\x17\x2e\x6f\xef\xc2\x9e\x69\xa9\xcd\xf2\xcd\xdd\xdd\xdd\x0a\x5e\xb4\xe1\x79\x65\x90\xee\x96\xf1\x9b\x53\x29\x57\x50\x51\xb6\xdb\x1a\xbd\x57\x3c\xef\x94\x37\xef\xc3\x6f\x05\xa9\x4a\x96\xb7\xcd\x11\xac\x96\x82\xc3\x1b\xc6\x58\x2f\xce\x0d\xe5\x62\x6f\x97\xef\x9a\xe3\x2a\x83\x92\x30\xcd\x31\xc4\xeb\xf7\xaf\x9f\x3e\x3e\x28\xd1\x34\xe8\x06\x0f\x3b\x44\x30\x6a\x90\xa2\x31\x78\xe1\x6c\x31\xf2\xd6\x7b\xb1\x19\xc7\x78\xa8\xff\xa3\x45\x5d\xeb\x03\x9a\x6e\x6d\x9f\xbb\x52\x40\x89\xcf\xa8\x9c\x7d\x8a\xf2\x6c\x14\xe5\xd7\x4c\x8d\x4e\xc2\x59\x5d\x7e\xa4\x15\x4a\x4b\x0a\x57\x4f\x9d\xc6\x9a\x9b\x3b\x4c\xb5\x0d\x0f\x42\xb1\x59\x9d\x47\x2a\xf7\x13\x87\xc3\x5a\xea\x23\x94\xde\xd6\x7c\x90\xa2\x2f\xd7\x36\xf8\x58\x34\xc0\x92\xc1\xb9\x5f\xe0\xed\x21\xb0\x88\xef\x31\xb9\xbb\xf8\x44\x9b\x11\x76\x07\x67\x1b\xaa\xfa\x78\x55\x94\x6f\x11\xe2\x37\x6f\x8c\x78\xa6\xe6\x94\x95\xde\x27\xd4\xb6\x0d\x6d\x3e\x21\xb7\x6d\x46\x8a\x70\x73\x8a\x4a\x6a\xe2\x23\x33\xc5\x35\xed\xf8\x7e\x87\xe6\x2f\x5b\x43\xf7\xfe\xe1\x6f\x18\x76\x87\xd4\x1a\xda\x16\xc2\x80\xc1\x27\xa1\xb8\x60\xd4\x69\x03\x61\xde\xe5\xfb\xa6\x41\xc3\xa8\xc5\x40\xbb\xef\x1f\x1d\xd3\x39\x0a\xde\xf7\x3d\xcb\x2d\xfe\xfc\xfa\x21\xe8\xcf\x2a\x3e\x26\xe7\xaf\x35\xa6\xd2\x0b\x62\x03\x8b\xfb\xd7\xee\x3e\x91\x83\x50\xab\xa3\x86\xa0\xb4\xc2\xec\xb2\xd5\x4c\x8c\xad\x01\x42\x1d\xba\x43\xf0\x70\x9d\xbd\xcb\xca\xfb\xe1\x34\xf9\x7e\x11\xfe\x1f\x04\xf8\x05\x81\xab\x82\x20\x5c\xfe\xa7\x82\xfd\xf7\x88\xf5\x98\x6e\x50\x97\xa4\xe0\xee\xda\x44\xd0\x0a\x49\xeb\x2b\x96\x14\x7c\xf2\xe5\x4c\x95\x6b\x6c\x68\x57\xb4\x7f\x2c\xed\xd7\x78\xd7\x32\x52\xc4\xce\x75\xd9\x30\x2f\x95\xa6\x07\x91\xf7\x28\x2d\x0e\xe7\xea\xec\xfc\x01\xf8\xac\xd3\xd3\x11\x6a\x0b\x26\x0c\x6e\x48\x7f\xab\xf8\xf7\x8d\x9c\xa9\x90\xe2\xfc\x2f\xe2\x4c\xba\x6b\xf6\xbd\xda\x3f\x01\x00\x00\xff\xff\x90\x89\x38\x20\x8a\x0a\x00\x00"),
compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x56\xd1\x6f\xdb\xb6\x13\x7e\xcf\x5f\x71\x50\xfb\xf0\xfb\x01\x93\x85\x2c\xed\xc3\x6c\x5a\x43\x50\x60\xdb\x43\x5b\x0c\x4d\x97\xd7\x80\x22\xcf\x16\x1b\x86\x14\x48\xda\xb1\xc7\xe9\x7f\x1f\x48\x4a\x8e\x2c\xcb\x6e\x0a\x0c\x06\x0c\xf1\x74\x77\xfc\xbe\xe3\xc7\x3b\x79\xcf\x71\x25\x14\x42\x56\x23\xe5\x59\xdb\x5e\x01\x10\x29\xd4\x23\xb8\x7d\x83\xcb\xcc\xe1\xce\x15\xcc\xda\x0c\x0c\xca\x65\x66\xdd\x5e\xa2\xad\x11\x5d\x06\xb5\xc1\xd5\x32\xf3\x1e\x1a\xea\xea\x3f\x0d\xae\xc4\x0e\xda\xb6\xb0\x8e\x3a\xc1\x42\x4c\x41\x25\x1a\x67\x67\xcc\xda\x5f\xb7\x4b\xef\xa1\xda\x08\xc9\xef\xd1\x58\xa1\x15\xb4\x6d\x56\x86\xcd\x2c\x33\xa2\x71\x60\x0d\x3b\x9f\xec\xdb\x21\xd7\xb7\x73\xa9\x48\x91\x12\x95\x57\xde\xa3\xe2\x6d\x7b\x75\xf5\xc2\x8d\x69\xe5\x50\xb9\x40\x8f\x70\xb1\x05\x26\xa9\xb5\xcb\x68\xa6\x42\xa1\xc9\x57\x72\x23\x78\xc2\x53\x5f\x97\xb7\x71\x2f\x52\xd4\xd7\xd1\x32\x88\xb0\xb5\x7e\xce\xa9\x52\x3a\xe0\xd2\xca\xc6\x10\x00\x20\xa2\xf7\x58\xcb\x7d\x53\x0b\xa6\x15\x1c\x9e\xf2\x8d\x62\x35\xb2\x47\xe4\x01\xa6\xe8\x42\x00\x48\xb5\x71\x4e\xab\xae\xd2\x69\x91\x9d\xdd\x09\x9c\x70\x12\xd3\x0b\x38\x82\x70\x37\xb2\x90\x22\xe5\x8a\xe0\x0b\x2e\xb6\xf1\xc1\xd1\x4a\x62\x9f\x3d\x2d\xe2\x7f\x5e\x69\xc3\xd1\x20\xef\x96\x4c\x4b\x49\x1b\x8b\xbc\xe3\x46\x5c\xa5\xf9\xbe\x07\xed\xfd\xdb\x78\x12\x77\x8e\x3a\xfc\xaa\xbf\xe8\xe7\x0f\x21\x23\xcc\x97\x30\xbb\x9d\x78\x11\x05\x95\x02\x0d\x55\x6b\x84\xd9\xef\x46\x6f\x9a\x17\x3b\x71\xe6\x50\x10\x20\x8e\x43\x94\xd8\x32\x6b\x28\xe7\x42\xad\xe7\xf0\x73\xb3\xcb\x5e\x3c\x42\xa2\xd9\x6f\x42\x62\xdb\x42\x19\x9e\x3f\xd3\x27\x3c\x24\x0b\x7c\x1d\xef\xbd\x49\x31\xcc\x7d\x00\x10\x61\x0a\xb5\xfe\xb2\x91\x68\x07\xa1\x89\x1c\x73\x62\x8b\x49\x00\x89\xd4\xc0\x70\xe4\x4c\x9c\xe9\xab\x19\x2b\x02\xf1\x3f\xf7\x5e\x28\x8e\x3b\x98\x2e\xd3\x2c\x1a\xda\x36\x39\x3f\x84\x3b\x87\xe6\x88\x5e\x57\x86\xf2\x45\x51\x51\x42\xac\xc6\xad\xd1\x2a\xe7\xfa\x59\x25\x15\x01\xa9\xca\x03\x7f\x52\x54\x25\xfc\xcf\x7b\x89\x0a\x8e\x38\x84\xad\xe2\xf2\xff\xc3\xca\x9c\x56\xe7\x94\xcf\x03\x47\x47\x85\xb4\x23\x74\xe4\x38\x4b\x34\x75\x12\x1b\x59\x1b\x83\xfd\x61\x72\x61\x1b\x49\xf7\xf3\x4a\x6a\xf6\xb8\x80\xfe\x6c\x7f\x99\xbd\x6f\x76\x0b\x58\x69\xe5\x72\x2b\xfe\xc6\xf9\xf5\x4d\x58\x33\x2d\xb5\x99\xbf\xb9\xb9\xb9\x59\xc0\xb3\x36\x3c\xaf\x0c\xd2\xc7\x79\xfc\xcf\xa9\x94\x0b\xa8\x28\x7b\x5c\x1b\xbd\x51\x3c\xef\x9c\x57\xef\xc3\x6f\x01\x49\xcc\xf3\xeb\x66\x07\x56\x4b\xc1\xe1\x0d\x63\xac\x37\xe7\x86\x72\xb1\xb1\xf3\x77\xcd\x6e\x91\x41\x49\x98\xe6\x18\x6a\xf8\xc7\xd7\x4f\x1f\xef\x94\x68\x1a\x74\x83\xfe\x13\xaa\x1a\x3d\x48\xd1\x18\x3c\x21\x5d\x4c\xb0\xf6\x5e\xac\xc6\xf5\x3f\x39\xdb\x57\xde\xc3\x5a\x6f\xd1\x74\xcf\xf6\xa9\x13\x0c\x4a\x7c\x42\xe5\xec\x43\xb4\x9f\x08\xe7\xe8\x14\x27\xde\x86\xf7\x75\xf9\x91\x56\x28\x2d\x29\x5c\x7d\xce\x23\xaa\xf4\x92\x43\xba\x15\x70\x27\x14\xbb\xe8\x77\x4f\xe5\xe6\x8c\xc3\x58\x7f\x7d\x05\xd3\x2d\xbd\x5c\xc4\x51\xdb\x18\x9a\x4f\xd4\x39\xca\x2b\x03\xf9\x9f\xe0\xed\x36\x20\x8b\x37\x3c\x95\x63\xf6\x89\x36\x13\xfb\x74\x69\x6d\x43\x55\x5f\xd7\x8a\xf2\x35\x42\xfc\xcf\x1b\x23\x9e\xa8\xd9\x67\xa5\xf7\x29\x73\xdb\x86\x29\x96\xb2\xb7\x6d\x46\x8a\x10\x79\x0e\x52\x9a\x53\x13\xdb\x15\xd3\x34\x62\x5f\x18\x42\x39\x6e\x3c\x5d\x77\x81\x7f\x60\xd8\x7b\x52\xe3\x69\x5b\x08\xb3\x14\x1f\x84\xe2\x82\x51\xa7\x0d\x84\xd1\x9e\x6f\x9a\x06\x0d\xa3\x16\x03\x85\xbe\x3b\x75\xa8\x2f\xc1\xf0\xbe\xef\x8c\x6e\xf6\xd7\xd7\x0f\x21\xe6\xa2\xf3\x7d\x2a\xc8\xb4\xd7\x39\x29\x80\x58\xc1\xec\xf6\x65\xb0\x9d\x39\xa3\xa0\xf9\x51\xa3\x51\x5a\x61\x76\xdc\xce\x26\xa6\xf6\x28\x4b\x1d\x3a\x4f\x60\xbe\xcc\xde\x65\xe5\xed\x70\xa0\xbe\x5e\xc0\xff\x15\x18\x7e\x04\x66\x52\x40\x84\xcb\x1f\x16\xfb\xf7\xab\xd9\xe7\x76\x03\x4d\x93\x82\xbb\xe9\xad\x82\x67\x38\xe0\x5e\xf1\xa4\xe0\x67\x6f\xe0\x39\xb9\xc7\x66\x3a\x49\xe5\xc7\xe4\x32\x9d\x7f\xda\x4e\x8a\xd8\x41\x4f\x1b\xf8\xa9\xf3\xe5\xc1\x39\x8e\xf0\x1e\xa5\xc5\xf1\x97\xc2\x77\x67\x28\xc0\x67\x9d\xae\xad\x50\x6b\x30\xe1\xd3\x04\xd2\xd7\x2b\x7f\x25\x98\x21\x10\x52\x1c\x3e\xda\x0e\x44\xbb\x81\xd5\xbb\xfd\x1b\x00\x00\xff\xff\x71\xca\xa7\x74\xf9\x0b\x00\x00"),
},
"/templates/config.html": &vfsgen۰CompressedFileInfo{
name: "config.html",
Expand Down
113 changes: 60 additions & 53 deletions web/ui/templates/alerts.html
Expand Up @@ -12,62 +12,69 @@ <h1>Alerts</h1>
</div>
<table class="table table-bordered table-collapsed">
<tbody>
{{$alertStateToRowClass := .AlertStateToRowClass}}
{{range .AlertingRules}}
{{$activeAlerts := .ActiveAlerts}}
<tr class="alert alert-{{index $alertStateToRowClass .State}} alert_header">
<td><i class="icon-chevron-down"></i> <b>{{.Name}}</b> ({{len $activeAlerts}} active)</td>
</tr>
<tr class="alert_details">
<td>
<div>
<pre style="display:block; padding:9.5px; font-size:13px; color:#333; word-break:break-all; background-color:#f5f5f5; border:1px solid #ccc; border-radius:4px;" ><code>{{.HTMLSnippet pathPrefix}}</code></pre>
</div>
{{if $activeAlerts}}
<table class="table table-bordered table-hover table-sm alert_elements_table">
<tr class="">
<th>Labels</th>
<th>State</th>
<th>Active Since</th>
<th>Value</th>
</tr>
{{range $activeAlerts}}
<tr>
<td>
{{range $label, $value := .Labels.Map}}
<span class="badge badge-primary">{{$label}}="{{$value}}"</span>
{{end}}
</td>
<td><span class="alert alert-{{ .State | alertStateToClass }} state_indicator text-uppercase">{{.State}}</span></td>
<td>{{.ActiveAt.UTC}}</td>
<td>{{.Value}}</td>
</tr>
{{ if .Annotations.Map}}
<tr style="display:none" class="alert_annotations">
<th colspan="4">Annotations</th>
</tr>
<tr style="display:none" class="alert_annotations">
<td colspan="4">
<dl>
{{range $label, $value := .Annotations.Map}}
<dt>{{$label}}</dt>
<dd>{{$value}}</dd>
{{end}}
</dl>
</td>
</tr>
{{end}}
{{end}}
</table>
{{end}}
</td>
</tr>
{{else}}
{{$alertStateToRowClass := .AlertStateToRowClass}}
{{range .Groups}}
<tr>
<td>
No alerting rules defined
<td style="padding: 2px">
{{.File}} > {{.Name}}
</td>
</tr>
{{range .AlertingRules}}
{{$activeAlerts := .ActiveAlerts}}
<tr class="alert alert-{{index $alertStateToRowClass .State}} alert_header">
<td><i class="icon-chevron-down"></i> <b>{{.Name}}</b> ({{len $activeAlerts}} active)</td>
</tr>
<tr class="alert_details">
<td>
<div>
<pre style="display:block; padding:9.5px; font-size:13px; color:#333; word-break:break-all; background-color:#f5f5f5; border:1px solid #ccc; border-radius:4px;" ><code>{{.HTMLSnippet pathPrefix}}</code></pre>
</div>
{{if $activeAlerts}}
<table class="table table-bordered table-hover table-sm alert_elements_table">
<tr class="">
<th>Labels</th>
<th>State</th>
<th>Active Since</th>
<th>Value</th>
</tr>
{{range $activeAlerts}}
<tr>
<td>
{{range $label, $value := .Labels.Map}}
<span class="badge badge-primary">{{$label}}="{{$value}}"</span>
{{end}}
</td>
<td><span class="alert alert-{{ .State | alertStateToClass }} state_indicator text-uppercase">{{.State}}</span></td>
<td>{{.ActiveAt.UTC}}</td>
<td>{{.Value}}</td>
</tr>
{{ if .Annotations.Map}}
<tr style="display:none" class="alert_annotations">
<th colspan="4">Annotations</th>
</tr>
<tr style="display:none" class="alert_annotations">
<td colspan="4">
<dl>
{{range $label, $value := .Annotations.Map}}
<dt>{{$label}}</dt>
<dd>{{$value}}</dd>
{{end}}
</dl>
</td>
</tr>
{{end}}
{{end}}
</table>
{{end}}
</td>
</tr>
{{end}}
{{else}}
<tr>
<td>
No alerting rules defined
</td>
</tr>
{{end}}
</tbody>
</table>
Expand Down
32 changes: 9 additions & 23 deletions web/web.go
Expand Up @@ -499,12 +499,16 @@ func (h *Handler) Run(ctx context.Context) error {
}

func (h *Handler) alerts(w http.ResponseWriter, r *http.Request) {
alerts := h.ruleManager.AlertingRules()
alertsSorter := byAlertStateAndNameSorter{alerts: alerts}
sort.Sort(alertsSorter)

var groups []*rules.Group
for _, group := range h.ruleManager.RuleGroups() {
if group.HasAlertingRules() {
groups = append(groups, group)
}
}

alertStatus := AlertStatus{
AlertingRules: alertsSorter.alerts,
Groups: groups,
AlertStateToRowClass: map[rules.AlertState]string{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll probably want to keep the AlertStatus struct as the top-level value that is passed into the HTML template, but change the type to take alerting groups instead of individual alerting rules. In any case, AlertStateToRowClass should only be instantiated once, in this top-level field of that struct.

rules.StateInactive: "success",
rules.StatePending: "warning",
Expand Down Expand Up @@ -926,24 +930,6 @@ func (h *Handler) executeTemplate(w http.ResponseWriter, name string, data inter

// AlertStatus bundles alerting rules and the mapping of alert states to row classes.
type AlertStatus struct {
AlertingRules []*rules.AlertingRule
Groups []*rules.Group
AlertStateToRowClass map[rules.AlertState]string
}

type byAlertStateAndNameSorter struct {
alerts []*rules.AlertingRule
}

func (s byAlertStateAndNameSorter) Len() int {
return len(s.alerts)
}

func (s byAlertStateAndNameSorter) Less(i, j int) bool {
return s.alerts[i].State() > s.alerts[j].State() ||
(s.alerts[i].State() == s.alerts[j].State() &&
s.alerts[i].Name() < s.alerts[j].Name())
}

func (s byAlertStateAndNameSorter) Swap(i, j int) {
s.alerts[i], s.alerts[j] = s.alerts[j], s.alerts[i]
}