mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-10 07:34:04 -08:00
commit
ab1bc9bcdd
|
@ -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
177
pkg/rulefmt/rulefmt.go
Normal 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()
|
||||
}
|
77
pkg/rulefmt/rulefmt_test.go
Normal file
77
pkg/rulefmt/rulefmt_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
7
pkg/rulefmt/testdata/bad_annotation.bad.yaml
vendored
Normal file
7
pkg/rulefmt/testdata/bad_annotation.bad.yaml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
rules:
|
||||
- alert: hola
|
||||
expr: 1
|
||||
annotations:
|
||||
ins-tance: localhost
|
5
pkg/rulefmt/testdata/bad_expr.bad.yaml
vendored
Normal file
5
pkg/rulefmt/testdata/bad_expr.bad.yaml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
rules:
|
||||
- record: yolo
|
||||
expr: rate(hi)
|
7
pkg/rulefmt/testdata/bad_lname.bad.yaml
vendored
Normal file
7
pkg/rulefmt/testdata/bad_lname.bad.yaml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
rules:
|
||||
- record: hola
|
||||
expr: 1
|
||||
labels:
|
||||
ins-tance: localhost
|
3
pkg/rulefmt/testdata/duplicate_grp.bad.yaml
vendored
Normal file
3
pkg/rulefmt/testdata/duplicate_grp.bad.yaml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
- name: yolo
|
4
pkg/rulefmt/testdata/no_rec_alert.bad.yaml
vendored
Normal file
4
pkg/rulefmt/testdata/no_rec_alert.bad.yaml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
rules:
|
||||
- expr: 1
|
4
pkg/rulefmt/testdata/noexpr.bad.yaml
vendored
Normal file
4
pkg/rulefmt/testdata/noexpr.bad.yaml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
rules:
|
||||
- record: ylo
|
6
pkg/rulefmt/testdata/record_and_alert.bad.yaml
vendored
Normal file
6
pkg/rulefmt/testdata/record_and_alert.bad.yaml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
groups:
|
||||
- name: yolo
|
||||
rules:
|
||||
- record: Hi
|
||||
alert: Hello
|
||||
expr: 1
|
64
pkg/rulefmt/testdata/test.yaml
vendored
Normal file
64
pkg/rulefmt/testdata/test.yaml
vendored
Normal 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 }}"
|
142
rules/manager.go
142
rules/manager.go
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
|
|
Loading…
Reference in a new issue