Merge pull request #2842 from Gouthamve/rulegroups

Rulegroups
This commit is contained in:
Fabian Reinartz 2017-06-19 13:27:37 +02:00 committed by GitHub
commit ab1bc9bcdd
15 changed files with 589 additions and 99 deletions

View file

@ -20,8 +20,12 @@ import (
"path/filepath"
"strings"
yaml "gopkg.in/yaml.v2"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/pkg/rulefmt"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/util/cli"
"github.com/prometheus/prometheus/util/promlint"
@ -148,8 +152,11 @@ func CheckRulesCmd(t cli.Term, args ...string) int {
failed := false
for _, arg := range args {
if n, err := checkRules(t, arg); err != nil {
t.Errorf(" FAILED: %s", err)
if n, errs := checkRules(t, arg); errs != nil {
t.Errorf(" FAILED:")
for _, e := range errs {
t.Errorf(e.Error())
}
failed = true
} else {
t.Infof(" SUCCESS: %d rules found", n)
@ -162,25 +169,104 @@ func CheckRulesCmd(t cli.Term, args ...string) int {
return 0
}
func checkRules(t cli.Term, filename string) (int, error) {
func checkRules(t cli.Term, filename string) (int, []error) {
t.Infof("Checking %s", filename)
if stat, err := os.Stat(filename); err != nil {
return 0, fmt.Errorf("cannot get file info")
return 0, []error{fmt.Errorf("cannot get file info")}
} else if stat.IsDir() {
return 0, fmt.Errorf("is a directory")
return 0, []error{fmt.Errorf("is a directory")}
}
rgs, errs := rulefmt.ParseFile(filename)
if errs != nil {
return 0, errs
}
numRules := 0
for _, rg := range rgs.Groups {
numRules += len(rg.Rules)
}
return numRules, nil
}
// UpdateRulesCmd updates the rule files.
func UpdateRulesCmd(t cli.Term, args ...string) int {
if len(args) == 0 {
t.Infof("usage: promtool update-rules <files>")
return 2
}
failed := false
for _, arg := range args {
if err := updateRules(t, arg); err != nil {
t.Errorf(" FAILED: %s", err)
failed = true
}
}
if failed {
return 1
}
return 0
}
func updateRules(t cli.Term, filename string) error {
t.Infof("Updating %s", filename)
if stat, err := os.Stat(filename); err != nil {
return fmt.Errorf("cannot get file info")
} else if stat.IsDir() {
return fmt.Errorf("is a directory")
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return 0, err
return err
}
rules, err := promql.ParseStmts(string(content))
if err != nil {
return 0, err
return err
}
return len(rules), nil
yamlRG := &rulefmt.RuleGroups{
Groups: []rulefmt.RuleGroup{{
Name: filename,
}},
}
yamlRules := make([]rulefmt.Rule, 0, len(rules))
for _, rule := range rules {
switch r := rule.(type) {
case *promql.AlertStmt:
yamlRules = append(yamlRules, rulefmt.Rule{
Alert: r.Name,
Expr: r.Expr.String(),
For: model.Duration(r.Duration),
Labels: r.Labels.Map(),
Annotations: r.Annotations.Map(),
})
case *promql.RecordStmt:
yamlRules = append(yamlRules, rulefmt.Rule{
Record: r.Name,
Expr: r.Expr.String(),
Labels: r.Labels.Map(),
})
default:
panic("unknown statement type")
}
}
yamlRG.Groups[0].Rules = yamlRules
y, err := yaml.Marshal(yamlRG)
if err != nil {
return err
}
return ioutil.WriteFile(filename+".yaml", y, 0666)
}
var checkMetricsUsage = strings.TrimSpace(`
@ -243,6 +329,11 @@ func main() {
Run: CheckMetricsCmd,
})
app.Register("update-rules", &cli.Command{
Desc: "update rules to the new YAML format",
Run: UpdateRulesCmd,
})
app.Register("version", &cli.Command{
Desc: "print the version of this binary",
Run: VersionCmd,

177
pkg/rulefmt/rulefmt.go Normal file
View file

@ -0,0 +1,177 @@
// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rulefmt
import (
"fmt"
"io/ioutil"
"strings"
"github.com/pkg/errors"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/promql"
yaml "gopkg.in/yaml.v2"
)
// Error represents semantical errors on parsing rule groups.
type Error struct {
Group string
Rule int
Err error
}
func (err *Error) Error() string {
return errors.Wrapf(err.Err, "group %q, rule %d", err.Group, err.Rule).Error()
}
// RuleGroups is a set of rule groups that are typically exposed in a file.
type RuleGroups struct {
Groups []RuleGroup `yaml:"groups"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
// Validate validates all rules in the rule groups.
func (g *RuleGroups) Validate() (errs []error) {
set := map[string]struct{}{}
for _, g := range g.Groups {
if g.Name == "" {
errs = append(errs, errors.Errorf("Groupname should not be empty"))
}
if _, ok := set[g.Name]; ok {
errs = append(
errs,
errors.Errorf("groupname: \"%s\" is repeated in the same file", g.Name),
)
}
if err := checkOverflow(g.XXX, "rule_group"); err != nil {
errs = append(errs, errors.Wrapf(err, "Group: %s", g.Name))
}
set[g.Name] = struct{}{}
for i, r := range g.Rules {
for _, err := range r.Validate() {
errs = append(errs, &Error{
Group: g.Name,
Rule: i,
Err: err,
})
}
}
}
if err := checkOverflow(g.XXX, "config_file"); err != nil {
errs = append(errs, err)
}
return errs
}
// RuleGroup is a list of sequentially evaluated recording and alerting rules.
type RuleGroup struct {
Name string `yaml:"name"`
Interval model.Duration `yaml:"interval,omitempty"`
Rules []Rule `yaml:"rules"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
// Rule describes an alerting or recording rule.
type Rule struct {
Record string `yaml:"record,omitempty"`
Alert string `yaml:"alert,omitempty"`
Expr string `yaml:"expr"`
For model.Duration `yaml:"for,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
// Validate the rule and return a list of encountered errors.
func (r *Rule) Validate() (errs []error) {
if r.Record != "" && r.Alert != "" {
errs = append(errs, errors.Errorf("only one of 'record' and 'alert' must be set"))
}
if r.Record == "" && r.Alert == "" {
errs = append(errs, errors.Errorf("one of 'record' or 'alert' must be set"))
}
if r.Expr == "" {
errs = append(errs, errors.Errorf("field 'expr' must be set in rule"))
} else if _, err := promql.ParseExpr(r.Expr); err != nil {
errs = append(errs, errors.Errorf("could not parse expression: %s", err))
}
if r.Record != "" {
if len(r.Annotations) > 0 {
errs = append(errs, errors.Errorf("invalid field 'annotations' in recording rule"))
}
if r.For != 0 {
errs = append(errs, errors.Errorf("invalid field 'for' in recording rule"))
}
}
for k, v := range r.Labels {
if !model.LabelName(k).IsValid() {
errs = append(errs, errors.Errorf("invalid label name: %s", k))
}
if !model.LabelValue(v).IsValid() {
errs = append(errs, errors.Errorf("invalid label value: %s", v))
}
}
for k := range r.Annotations {
if !model.LabelName(k).IsValid() {
errs = append(errs, errors.Errorf("invalid annotation name: %s", k))
}
}
if err := checkOverflow(r.XXX, "rule"); err != nil {
errs = append(errs, err)
}
return errs
}
func checkOverflow(m map[string]interface{}, ctx string) error {
if len(m) > 0 {
var keys []string
for k := range m {
keys = append(keys, k)
}
return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", "))
}
return nil
}
// ParseFile parses the rule file and validates it.
func ParseFile(file string) (*RuleGroups, []error) {
b, err := ioutil.ReadFile(file)
if err != nil {
return nil, []error{err}
}
var groups RuleGroups
if err := yaml.Unmarshal(b, &groups); err != nil {
return nil, []error{err}
}
return &groups, groups.Validate()
}

View file

@ -0,0 +1,77 @@
// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rulefmt
import (
"path/filepath"
"strings"
"testing"
)
func TestParseFileSuccess(t *testing.T) {
if _, errs := ParseFile("testdata/test.yaml"); len(errs) > 0 {
t.Errorf("unexpected errors parsing file")
for _, err := range errs {
t.Error(err)
}
}
}
func TestParseFileFailure(t *testing.T) {
table := []struct {
filename string
errMsg string
}{
{
filename: "duplicate_grp.bad.yaml",
errMsg: "groupname: \"yolo\" is repeated in the same file",
},
{
filename: "bad_expr.bad.yaml",
errMsg: "parse error",
},
{
filename: "record_and_alert.bad.yaml",
errMsg: "only one of 'record' and 'alert' must be set",
},
{
filename: "no_rec_alert.bad.yaml",
errMsg: "one of 'record' or 'alert' must be set",
},
{
filename: "noexpr.bad.yaml",
errMsg: "field 'expr' must be set in rule",
},
{
filename: "bad_lname.bad.yaml",
errMsg: "invalid label name",
},
{
filename: "bad_annotation.bad.yaml",
errMsg: "invalid annotation name",
},
}
for _, c := range table {
_, errs := ParseFile(filepath.Join("testdata", c.filename))
if errs == nil {
t.Errorf("Expected error parsing %s but got none", c.filename)
continue
}
if !strings.Contains(errs[0].Error(), c.errMsg) {
t.Errorf("Expected error for %s to contain %q but got: %s", c.filename, c.errMsg, errs)
}
}
}

View file

@ -0,0 +1,7 @@
groups:
- name: yolo
rules:
- alert: hola
expr: 1
annotations:
ins-tance: localhost

View file

@ -0,0 +1,5 @@
groups:
- name: yolo
rules:
- record: yolo
expr: rate(hi)

View file

@ -0,0 +1,7 @@
groups:
- name: yolo
rules:
- record: hola
expr: 1
labels:
ins-tance: localhost

View file

@ -0,0 +1,3 @@
groups:
- name: yolo
- name: yolo

View file

@ -0,0 +1,4 @@
groups:
- name: yolo
rules:
- expr: 1

4
pkg/rulefmt/testdata/noexpr.bad.yaml vendored Normal file
View file

@ -0,0 +1,4 @@
groups:
- name: yolo
rules:
- record: ylo

View file

@ -0,0 +1,6 @@
groups:
- name: yolo
rules:
- record: Hi
alert: Hello
expr: 1

64
pkg/rulefmt/testdata/test.yaml vendored Normal file
View file

@ -0,0 +1,64 @@
groups:
- name: my-group-name
interval: 30s # defaults to global interval
rules:
- alert: HighErrors
expr: |
sum without(instance) (rate(errors_total[5m]))
/
sum without(instance) (rate(requests_total[5m]))
for: 5m
labels:
severity: critical
annotations:
description: "stuff's happening with {{ $.labels.service }}"
# Mix recording rules in the same list
- record: "new_metric"
expr: |
sum without(instance) (rate(errors_total[5m]))
/
sum without(instance) (rate(requests_total[5m]))
labels:
abc: edf
uvw: xyz
- alert: HighErrors
expr: |
sum without(instance) (rate(errors_total[5m]))
/
sum without(instance) (rate(requests_total[5m]))
for: 5m
labels:
severity: critical
annotations:
description: "stuff's happening with {{ $.labels.service }}"
- name: my-another-name
interval: 30s # defaults to global interval
rules:
- alert: HighErrors
expr: |
sum without(instance) (rate(errors_total[5m]))
/
sum without(instance) (rate(requests_total[5m]))
for: 5m
labels:
severity: critical
- record: "new_metric"
expr: |
sum without(instance) (rate(errors_total[5m]))
/
sum without(instance) (rate(requests_total[5m]))
- alert: HighErrors
expr: |
sum without(instance) (rate(errors_total[5m]))
/
sum without(instance) (rate(requests_total[5m]))
for: 5m
labels:
severity: critical
annotations:
description: "stuff's happening with {{ $.labels.service }}"

View file

@ -14,11 +14,12 @@
package rules
import (
"errors"
"fmt"
"io/ioutil"
"math"
"net/url"
"path/filepath"
"sort"
"sync"
"time"
@ -26,12 +27,12 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
"github.com/prometheus/common/model"
"golang.org/x/net/context"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/notifier"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/rulefmt"
"github.com/prometheus/prometheus/pkg/timestamp"
"github.com/prometheus/prometheus/pkg/value"
"github.com/prometheus/prometheus/promql"
@ -127,6 +128,7 @@ type Rule interface {
// Group is a set of rules that have a logical relation.
type Group struct {
name string
file string
interval time.Duration
rules []Rule
seriesInPreviousEval []map[string]labels.Labels // One per Rule.
@ -139,9 +141,10 @@ type Group struct {
}
// NewGroup makes a new Group with the given name, options, and rules.
func NewGroup(name string, interval time.Duration, rules []Rule, opts *ManagerOptions) *Group {
func NewGroup(name, file string, interval time.Duration, rules []Rule, opts *ManagerOptions) *Group {
return &Group{
name: name,
file: file,
interval: interval,
rules: rules,
opts: opts,
@ -152,6 +155,15 @@ func NewGroup(name string, interval time.Duration, rules []Rule, opts *ManagerOp
}
}
// Name returns the group name.
func (g *Group) Name() string { return g.name }
// File returns the group's file.
func (g *Group) File() string { return g.file }
// Rules returns the group's rules.
func (g *Group) Rules() []Rule { return g.rules }
func (g *Group) run() {
defer close(g.terminated)
@ -202,9 +214,13 @@ func (g *Group) stop() {
<-g.terminated
}
func (g *Group) fingerprint() model.Fingerprint {
l := model.LabelSet{"name": model.LabelValue(g.name)}
return l.Fingerprint()
func (g *Group) hash() uint64 {
l := labels.New(
labels.Label{"name", g.name},
labels.Label{"file", g.file},
)
return l.Hash()
}
// offset returns until the next consistently slotted evaluation interval.
@ -213,7 +229,7 @@ func (g *Group) offset() time.Duration {
var (
base = now - (now % int64(g.interval))
offset = uint64(g.fingerprint()) % uint64(g.interval)
offset = g.hash() % uint64(g.interval)
next = base + int64(offset)
)
@ -269,22 +285,18 @@ func typeForRule(r Rule) ruleType {
panic(fmt.Errorf("unknown rule type: %T", r))
}
// Eval runs a single evaluation cycle in which all rules are evaluated in parallel.
// In the future a single group will be evaluated sequentially to properly handle
// rule dependency.
// Eval runs a single evaluation cycle in which all rules are evaluated sequentially.
func (g *Group) Eval(ts time.Time) {
var (
wg sync.WaitGroup
)
for i, rule := range g.rules {
select {
case <-g.done:
return
default:
}
rtyp := string(typeForRule(rule))
wg.Add(1)
// BUG(julius): Look at fixing thundering herd.
go func(i int, rule Rule) {
defer wg.Done()
func(i int, rule Rule) {
defer func(t time.Time) {
evalDuration.WithLabelValues(rtyp).Observe(time.Since(t).Seconds())
}(time.Now())
@ -361,7 +373,6 @@ func (g *Group) Eval(ts time.Time) {
}
}(i, rule)
}
wg.Wait()
}
// sendAlerts sends alert notifications for the given rule.
@ -404,6 +415,7 @@ type Manager struct {
logger log.Logger
}
// Appendable returns an Appender.
type Appendable interface {
Appender() (storage.Appender, error)
}
@ -466,9 +478,12 @@ func (m *Manager) ApplyConfig(conf *config.Config) error {
}
// To be replaced with a configurable per-group interval.
groups, err := m.loadGroups(time.Duration(conf.GlobalConfig.EvaluationInterval), files...)
if err != nil {
return fmt.Errorf("error loading rules, previous rule set restored: %s", err)
groups, errs := m.loadGroups(time.Duration(conf.GlobalConfig.EvaluationInterval), files...)
if errs != nil {
for _, e := range errs {
m.logger.Errorln(e)
}
return errors.New("error loading rules, previous rule set restored")
}
var wg sync.WaitGroup
@ -511,42 +526,71 @@ func (m *Manager) ApplyConfig(conf *config.Config) error {
// loadGroups reads groups from a list of files.
// As there's currently no group syntax a single group named "default" containing
// all rules will be returned.
func (m *Manager) loadGroups(interval time.Duration, filenames ...string) (map[string]*Group, error) {
rules := []Rule{}
func (m *Manager) loadGroups(interval time.Duration, filenames ...string) (map[string]*Group, []error) {
groups := make(map[string]*Group)
for _, fn := range filenames {
content, err := ioutil.ReadFile(fn)
if err != nil {
return nil, err
}
stmts, err := promql.ParseStmts(string(content))
if err != nil {
return nil, fmt.Errorf("error parsing %s: %s", fn, err)
rgs, errs := rulefmt.ParseFile(fn)
if errs != nil {
return nil, errs
}
for _, stmt := range stmts {
var rule Rule
switch r := stmt.(type) {
case *promql.AlertStmt:
rule = NewAlertingRule(r.Name, r.Expr, r.Duration, r.Labels, r.Annotations, m.logger)
case *promql.RecordStmt:
rule = NewRecordingRule(r.Name, r.Expr, r.Labels)
default:
panic("retrieval.Manager.LoadRuleFiles: unknown statement type")
for _, rg := range rgs.Groups {
itv := interval
if rg.Interval != 0 {
itv = time.Duration(rg.Interval)
}
rules = append(rules, rule)
rules := make([]Rule, 0, len(rg.Rules))
for _, r := range rg.Rules {
expr, err := promql.ParseExpr(r.Expr)
if err != nil {
return nil, []error{err}
}
if r.Alert != "" {
rules = append(rules, NewAlertingRule(
r.Alert,
expr,
time.Duration(r.For),
labels.FromMap(r.Labels),
labels.FromMap(r.Annotations),
m.logger,
))
continue
}
rules = append(rules, NewRecordingRule(
r.Record,
expr,
labels.FromMap(r.Labels),
))
}
// Group names need not be unique across filenames.
groups[rg.Name+";"+fn] = NewGroup(rg.Name, fn, itv, rules, m.opts)
}
}
// Currently there is no group syntax implemented. Thus all rules
// are read into a single default group.
g := NewGroup("default", interval, rules, m.opts)
groups := map[string]*Group{g.name: g}
return groups, nil
}
// RuleGroups returns the list of manager's rule groups.
func (m *Manager) RuleGroups() []*Group {
m.mtx.RLock()
defer m.mtx.RUnlock()
rgs := make([]*Group, 0, len(m.groups))
for _, g := range m.groups {
rgs = append(rgs, g)
}
sort.Slice(rgs, func(i, j int) bool {
return rgs[i].file < rgs[j].file && rgs[i].name < rgs[j].name
})
return rgs
}
// Rules returns the list of the manager's rules.
func (m *Manager) Rules() []Rule {
m.mtx.RLock()

View file

@ -175,7 +175,7 @@ func TestStaleness(t *testing.T) {
t.Fatal(err)
}
rule := NewRecordingRule("a_plus_one", expr, labels.Labels{})
group := NewGroup("default", time.Second, []Rule{rule}, opts)
group := NewGroup("default", "", time.Second, []Rule{rule}, opts)
// A time series that has two samples and then goes stale.
app, _ := storage.Appender()

View file

@ -121,7 +121,7 @@ func webUiTemplates_baseHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/_base.html", size: 2858, mode: os.FileMode(420), modTime: time.Unix(1495630073, 0)}
info := bindataFileInfo{name: "web/ui/templates/_base.html", size: 2858, mode: os.FileMode(420), modTime: time.Unix(1491024130, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -141,7 +141,7 @@ func webUiTemplatesAlertsHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/alerts.html", size: 1832, mode: os.FileMode(420), modTime: time.Unix(1496733728, 0)}
info := bindataFileInfo{name: "web/ui/templates/alerts.html", size: 1832, mode: os.FileMode(420), modTime: time.Unix(1497004733, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -161,7 +161,7 @@ func webUiTemplatesConfigHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/config.html", size: 175, mode: os.FileMode(420), modTime: time.Unix(1464223038, 0)}
info := bindataFileInfo{name: "web/ui/templates/config.html", size: 175, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -181,7 +181,7 @@ func webUiTemplatesFlagsHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/flags.html", size: 433, mode: os.FileMode(420), modTime: time.Unix(1464223038, 0)}
info := bindataFileInfo{name: "web/ui/templates/flags.html", size: 433, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -201,12 +201,12 @@ func webUiTemplatesGraphHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/graph.html", size: 1941, mode: os.FileMode(420), modTime: time.Unix(1495630073, 0)}
info := bindataFileInfo{name: "web/ui/templates/graph.html", size: 1941, mode: os.FileMode(420), modTime: time.Unix(1491024130, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _webUiTemplatesRulesHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\xce\xc1\x4a\xc7\x30\x0c\xc7\xf1\x7b\x9f\x22\xf6\xde\x7f\x61\xe7\xae\x67\x0f\x0a\xa2\xbe\x40\x5d\x33\x1b\x18\xb1\x74\xdd\x18\x84\xbc\xbb\x6c\x38\x3c\xe5\xf0\xcd\x0f\x3e\x22\x19\x67\x62\x04\x5b\x30\x65\xab\x1a\x9e\x9c\x03\xa6\x03\x9c\x8b\x22\xc8\x59\xd5\x98\xff\xaf\xe9\x87\x3b\x72\xb7\xaa\x06\x20\x64\xda\x61\x5a\xd2\xba\x8e\x57\x48\xc4\xd8\xdc\xbc\x6c\x94\x6d\x34\x00\x00\xa1\x0c\x40\x79\xb4\x6d\x5b\x70\xb5\xf1\xfd\x3c\xc1\x97\xe1\xaf\xd6\x86\x51\xa4\x25\xfe\x46\x78\x5c\x51\x55\xe4\xf1\xfc\xf9\xfa\xf2\xc1\x54\x2b\x76\xa8\xa9\x97\xb7\x86\x33\x1d\xaa\xe1\xab\xf9\x1b\x15\xfc\x39\x3e\x11\x3e\xd3\x1e\xcd\x6d\xfd\x0d\x00\x00\xff\xff\x04\x2c\xc2\x57\xd1\x00\x00\x00")
var _webUiTemplatesRulesHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x8f\xc1\x4a\x03\x31\x10\x86\xef\x79\x8a\x31\xf7\xec\x42\x8f\x9a\xe6\xa8\x1e\x54\x44\x7d\x81\xd8\xcc\xba\x03\xdb\x69\x48\xb2\xa5\x30\xcc\xbb\x4b\x6a\x8b\xf4\xf4\x0f\xf3\xcd\xc0\xff\x89\x24\x9c\x88\x11\xec\x8c\x31\x59\x55\x7f\xe7\x1c\x30\x9d\xc0\xb9\x20\x82\x9c\x54\x8d\xf9\xbf\xda\x1d\xb8\x21\x37\xab\x6a\x00\x7c\xa2\x23\xec\x96\x58\xeb\xf6\x0c\x22\x31\x16\x37\x2d\x2b\x25\x1b\x0c\x00\x80\x9f\x37\x40\x69\x6b\xcb\xba\x60\xb5\xe1\xa3\x87\x1f\xe7\xcd\x85\xe6\x82\x41\xa4\x44\xfe\x41\x18\x3a\x7c\x2a\x87\x35\x57\xd5\x47\x5a\xf0\x1e\x44\x86\x3e\xa8\x3e\xc0\x19\x00\xc7\xfd\xdf\xfa\x2d\xee\x51\xd5\xdc\xfc\x56\x55\x91\xe1\xf9\xeb\xf5\xe5\x93\x29\x67\x6c\x90\x63\x9b\xdf\x0b\x4e\x74\x52\x05\x3f\x7e\x97\xab\xd1\x25\xfc\xd8\x0b\x74\x91\x31\xd1\x31\x98\xab\xef\x6f\x00\x00\x00\xff\xff\xb9\xb8\xbf\x79\x15\x01\x00\x00")
func webUiTemplatesRulesHtmlBytes() ([]byte, error) {
return bindataRead(
@ -221,7 +221,7 @@ func webUiTemplatesRulesHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/rules.html", size: 209, mode: os.FileMode(420), modTime: time.Unix(1464223038, 0)}
info := bindataFileInfo{name: "web/ui/templates/rules.html", size: 277, mode: os.FileMode(420), modTime: time.Unix(1497436569, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -241,7 +241,7 @@ func webUiTemplatesStatusHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/status.html", size: 1756, mode: os.FileMode(420), modTime: time.Unix(1495630073, 0)}
info := bindataFileInfo{name: "web/ui/templates/status.html", size: 1756, mode: os.FileMode(420), modTime: time.Unix(1494943211, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -261,7 +261,7 @@ func webUiTemplatesTargetsHtml() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/templates/targets.html", size: 2275, mode: os.FileMode(420), modTime: time.Unix(1496733728, 0)}
info := bindataFileInfo{name: "web/ui/templates/targets.html", size: 2275, mode: os.FileMode(420), modTime: time.Unix(1497004733, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -281,7 +281,7 @@ func webUiStaticCssAlertsCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/css/alerts.css", size: 74, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/css/alerts.css", size: 74, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -301,7 +301,7 @@ func webUiStaticCssGraphCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/css/graph.css", size: 2710, mode: os.FileMode(420), modTime: time.Unix(1495630073, 0)}
info := bindataFileInfo{name: "web/ui/static/css/graph.css", size: 2710, mode: os.FileMode(420), modTime: time.Unix(1494943211, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -321,7 +321,7 @@ func webUiStaticCssProm_consoleCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/css/prom_console.css", size: 2883, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/css/prom_console.css", size: 2883, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -341,7 +341,7 @@ func webUiStaticCssPrometheusCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/css/prometheus.css", size: 405, mode: os.FileMode(420), modTime: time.Unix(1462274250, 0)}
info := bindataFileInfo{name: "web/ui/static/css/prometheus.css", size: 405, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -361,7 +361,7 @@ func webUiStaticImgAjaxLoaderGif() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/img/ajax-loader.gif", size: 847, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/img/ajax-loader.gif", size: 847, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -381,7 +381,7 @@ func webUiStaticImgFaviconIco() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/img/favicon.ico", size: 15086, mode: os.FileMode(420), modTime: time.Unix(1485793847, 0)}
info := bindataFileInfo{name: "web/ui/static/img/favicon.ico", size: 15086, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -401,7 +401,7 @@ func webUiStaticJsAlertsJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/js/alerts.js", size: 445, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/js/alerts.js", size: 445, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -421,7 +421,7 @@ func webUiStaticJsGraphJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/js/graph.js", size: 27415, mode: os.FileMode(420), modTime: time.Unix(1496733731, 0)}
info := bindataFileInfo{name: "web/ui/static/js/graph.js", size: 27415, mode: os.FileMode(420), modTime: time.Unix(1497004733, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -441,7 +441,7 @@ func webUiStaticJsGraph_templateHandlebar() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/js/graph_template.handlebar", size: 6337, mode: os.FileMode(420), modTime: time.Unix(1495630073, 0)}
info := bindataFileInfo{name: "web/ui/static/js/graph_template.handlebar", size: 6337, mode: os.FileMode(420), modTime: time.Unix(1491024130, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -461,7 +461,7 @@ func webUiStaticJsProm_consoleJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/js/prom_console.js", size: 22279, mode: os.FileMode(420), modTime: time.Unix(1496733731, 0)}
info := bindataFileInfo{name: "web/ui/static/js/prom_console.js", size: 22279, mode: os.FileMode(420), modTime: time.Unix(1497004733, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -481,7 +481,7 @@ func webUiStaticVendorBootstrap331CssBootstrapThemeMinCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap-theme.min.css", size: 19835, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap-theme.min.css", size: 19835, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -501,7 +501,7 @@ func webUiStaticVendorBootstrap331CssBootstrapMinCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css", size: 113498, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css", size: 113498, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -521,7 +521,7 @@ func webUiStaticVendorBootstrap331FontsGlyphiconsHalflingsRegularEot() (*asset,
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.eot", size: 20335, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.eot", size: 20335, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -541,7 +541,7 @@ func webUiStaticVendorBootstrap331FontsGlyphiconsHalflingsRegularSvg() (*asset,
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.svg", size: 62926, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.svg", size: 62926, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -561,7 +561,7 @@ func webUiStaticVendorBootstrap331FontsGlyphiconsHalflingsRegularTtf() (*asset,
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.ttf", size: 41280, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.ttf", size: 41280, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -581,7 +581,7 @@ func webUiStaticVendorBootstrap331FontsGlyphiconsHalflingsRegularWoff() (*asset,
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.woff", size: 23320, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.woff", size: 23320, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -601,7 +601,7 @@ func webUiStaticVendorBootstrap331JsBootstrapMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/js/bootstrap.min.js", size: 35601, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/js/bootstrap.min.js", size: 35601, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -621,7 +621,7 @@ func webUiStaticVendorBootstrap331JsNpmJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/js/npm.js", size: 484, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap-3.3.1/js/npm.js", size: 484, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -641,7 +641,7 @@ func webUiStaticVendorBootstrap3TypeaheadBootstrap3TypeaheadMinJs() (*asset, err
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap3-typeahead/bootstrap3-typeahead.min.js", size: 7856, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/bootstrap3-typeahead/bootstrap3-typeahead.min.js", size: 7856, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -661,7 +661,7 @@ func webUiStaticVendorEonasdanBootstrapDatetimepickerBootstrapDatetimepickerMinC
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.css", size: 7771, mode: os.FileMode(420), modTime: time.Unix(1481727016, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.css", size: 7771, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -681,7 +681,7 @@ func webUiStaticVendorEonasdanBootstrapDatetimepickerBootstrapDatetimepickerMinJ
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.js", size: 48881, mode: os.FileMode(420), modTime: time.Unix(1481727016, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.js", size: 48881, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -701,7 +701,7 @@ func webUiStaticVendorFuzzyFuzzyJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/fuzzy/fuzzy.js", size: 5669, mode: os.FileMode(420), modTime: time.Unix(1495630073, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/fuzzy/fuzzy.js", size: 5669, mode: os.FileMode(420), modTime: time.Unix(1489836724, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -721,7 +721,7 @@ func webUiStaticVendorJsJqueryHotkeysJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/js/jquery.hotkeys.js", size: 3283, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/js/jquery.hotkeys.js", size: 3283, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -741,7 +741,7 @@ func webUiStaticVendorJsJqueryMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/js/jquery.min.js", size: 95935, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/js/jquery.min.js", size: 95935, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -761,7 +761,7 @@ func webUiStaticVendorJsJquerySelectionJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/js/jquery.selection.js", size: 13320, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/js/jquery.selection.js", size: 13320, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -781,7 +781,7 @@ func webUiStaticVendorMomentMomentMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/moment/moment.min.js", size: 61281, mode: os.FileMode(420), modTime: time.Unix(1481727016, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/moment/moment.min.js", size: 61281, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -801,7 +801,7 @@ func webUiStaticVendorMustacheMustacheMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/mustache/mustache.min.js", size: 9528, mode: os.FileMode(420), modTime: time.Unix(1481727016, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/mustache/mustache.min.js", size: 9528, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -821,7 +821,7 @@ func webUiStaticVendorRickshawRickshawMinCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/rickshaw.min.css", size: 6102, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/rickshaw.min.css", size: 6102, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -841,7 +841,7 @@ func webUiStaticVendorRickshawRickshawMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/rickshaw.min.js", size: 76322, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/rickshaw.min.js", size: 76322, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -861,7 +861,7 @@ func webUiStaticVendorRickshawVendorD3LayoutMinJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/vendor/d3.layout.min.js", size: 17514, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/vendor/d3.layout.min.js", size: 17514, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -881,7 +881,7 @@ func webUiStaticVendorRickshawVendorD3V3Js() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/vendor/d3.v3.js", size: 144718, mode: os.FileMode(420), modTime: time.Unix(1461244866, 0)}
info := bindataFileInfo{name: "web/ui/static/vendor/rickshaw/vendor/d3.v3.js", size: 144718, mode: os.FileMode(420), modTime: time.Unix(1488310986, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}

View file

@ -3,6 +3,7 @@
{{define "content"}}
<div class="container-fluid">
<h2 id="rules">Rules</h2>
<pre>{{range .Rules}}{{.HTMLSnippet pathPrefix}}<br/>{{end}}</pre>
<pre>{{range .RuleGroups}}File: {{.File}}; Group name: {{.Name}}
{{range .Rules}}{{.HTMLSnippet pathPrefix}} </br>{{end}}{{end}}</pre>
</div>
{{end}}