Incorporate PR feedback

* Move fingerprint to Hash()
* Move away from tsdb.MultiError
* 0777 -> 0666 for files
* checkOverflow of extra fields

Signed-off-by: Goutham Veeramachaneni <cs14btech11014@iith.ac.in>
This commit is contained in:
Goutham Veeramachaneni 2017-06-16 16:44:33 +05:30
parent dc69645e92
commit 6b70a4d850
No known key found for this signature in database
GPG Key ID: F1C217E8E9023CAD
3 changed files with 65 additions and 24 deletions

View File

@ -29,7 +29,6 @@ import (
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/util/cli" "github.com/prometheus/prometheus/util/cli"
"github.com/prometheus/prometheus/util/promlint" "github.com/prometheus/prometheus/util/promlint"
"github.com/prometheus/tsdb"
) )
// CheckConfigCmd validates configuration files. // CheckConfigCmd validates configuration files.
@ -153,8 +152,11 @@ func CheckRulesCmd(t cli.Term, args ...string) int {
failed := false failed := false
for _, arg := range args { for _, arg := range args {
if n, err := checkRules(t, arg); err != nil { if n, errs := checkRules(t, arg); errs != nil {
t.Errorf(" FAILED: %s", err) t.Errorf(" FAILED:")
for _, e := range errs {
t.Errorf(e.Error())
}
failed = true failed = true
} else { } else {
t.Infof(" SUCCESS: %d rules found", n) t.Infof(" SUCCESS: %d rules found", n)
@ -167,18 +169,18 @@ func CheckRulesCmd(t cli.Term, args ...string) int {
return 0 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) t.Infof("Checking %s", filename)
if stat, err := os.Stat(filename); err != nil { 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() { } else if stat.IsDir() {
return 0, fmt.Errorf("is a directory") return 0, []error{fmt.Errorf("is a directory")}
} }
rgs, errs := rulefmt.ParseFile(filename) rgs, errs := rulefmt.ParseFile(filename)
if errs != nil { if errs != nil {
return 0, tsdb.MultiError(errs) return 0, errs
} }
numRules := 0 numRules := 0
@ -265,7 +267,7 @@ func updateRules(t cli.Term, filename string) error {
return err return err
} }
return ioutil.WriteFile(filename+".yaml", y, 0777) return ioutil.WriteFile(filename+".yaml", y, 0666)
} }
var checkMetricsUsage = strings.TrimSpace(` var checkMetricsUsage = strings.TrimSpace(`
@ -329,7 +331,7 @@ func main() {
}) })
app.Register("update-rules", &cli.Command{ app.Register("update-rules", &cli.Command{
Desc: "update the rules to the new YAML format", Desc: "update rules to the new YAML format",
Run: UpdateRulesCmd, Run: UpdateRulesCmd,
}) })

View File

@ -14,7 +14,9 @@
package rulefmt package rulefmt
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
@ -37,6 +39,9 @@ func (err *Error) Error() string {
type RuleGroups struct { type RuleGroups struct {
Version int `yaml:"version"` Version int `yaml:"version"`
Groups []RuleGroup `yaml:"groups"` 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. // Validate validates all rules in the rule groups.
@ -58,6 +63,10 @@ func (g *RuleGroups) Validate() (errs []error) {
) )
} }
if err := checkOverflow(g.XXX, "rule_group"); err != nil {
errs = append(errs, errors.Wrapf(err, "Group: %s", g.Name))
}
set[g.Name] = struct{}{} set[g.Name] = struct{}{}
for i, r := range g.Rules { for i, r := range g.Rules {
@ -70,6 +79,11 @@ func (g *RuleGroups) Validate() (errs []error) {
} }
} }
} }
if err := checkOverflow(g.XXX, "config_file"); err != nil {
errs = append(errs, err)
}
return errs return errs
} }
@ -78,6 +92,9 @@ type RuleGroup struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Interval model.Duration `yaml:"interval,omitempty"` Interval model.Duration `yaml:"interval,omitempty"`
Rules []Rule `yaml:"rules"` 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. // Rule describes an alerting or recording rule.
@ -88,6 +105,9 @@ type Rule struct {
For model.Duration `yaml:"for,omitempty"` For model.Duration `yaml:"for,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"` Labels map[string]string `yaml:"labels,omitempty"`
Annotations map[string]string `yaml:"annotations,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. // Validate the rule and return a list of encountered errors.
@ -129,9 +149,24 @@ func (r *Rule) Validate() (errs []error) {
} }
} }
if err := checkOverflow(r.XXX, "rule"); err != nil {
errs = append(errs, err)
}
return errs 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. // ParseFile parses the rule file and validates it.
func ParseFile(file string) (*RuleGroups, []error) { func ParseFile(file string) (*RuleGroups, []error) {
b, err := ioutil.ReadFile(file) b, err := ioutil.ReadFile(file)

View File

@ -14,6 +14,7 @@
package rules package rules
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"net/url" "net/url"
@ -26,8 +27,6 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log" "github.com/prometheus/common/log"
"github.com/prometheus/common/model"
"github.com/prometheus/tsdb"
"golang.org/x/net/context" "golang.org/x/net/context"
"github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/config"
@ -212,12 +211,13 @@ func (g *Group) stop() {
<-g.terminated <-g.terminated
} }
func (g *Group) fingerprint() model.Fingerprint { func (g *Group) hash() uint64 {
l := model.LabelSet{ l := labels.New(
"name": model.LabelValue(g.name), labels.Label{"name", g.name},
"filename": model.LabelValue(g.file), labels.Label{"file", g.file},
} )
return l.Fingerprint()
return l.Hash()
} }
// offset returns until the next consistently slotted evaluation interval. // offset returns until the next consistently slotted evaluation interval.
@ -226,7 +226,7 @@ func (g *Group) offset() time.Duration {
var ( var (
base = now - (now % int64(g.interval)) base = now - (now % int64(g.interval))
offset = uint64(g.fingerprint()) % uint64(g.interval) offset = g.hash() % uint64(g.interval)
next = base + int64(offset) next = base + int64(offset)
) )
@ -466,9 +466,13 @@ func (m *Manager) ApplyConfig(conf *config.Config) error {
} }
// To be replaced with a configurable per-group interval. // To be replaced with a configurable per-group interval.
groups, err := m.loadGroups(time.Duration(conf.GlobalConfig.EvaluationInterval), files...) groups, errs := m.loadGroups(time.Duration(conf.GlobalConfig.EvaluationInterval), files...)
if err != nil { if errs != nil {
return fmt.Errorf("error loading rules, previous rule set restored: %s", err) for _, e := range errs {
// TODO(gouthamve): Move to non-global logger.
log.Errorln(e)
}
return errors.New("error loading rules, previous rule set restored")
} }
var wg sync.WaitGroup var wg sync.WaitGroup
@ -511,13 +515,13 @@ func (m *Manager) ApplyConfig(conf *config.Config) error {
// loadGroups reads groups from a list of files. // loadGroups reads groups from a list of files.
// As there's currently no group syntax a single group named "default" containing // As there's currently no group syntax a single group named "default" containing
// all rules will be returned. // all rules will be returned.
func (m *Manager) loadGroups(interval time.Duration, filenames ...string) (map[string]*Group, error) { func (m *Manager) loadGroups(interval time.Duration, filenames ...string) (map[string]*Group, []error) {
groups := make(map[string]*Group) groups := make(map[string]*Group)
for _, fn := range filenames { for _, fn := range filenames {
rgs, errs := rulefmt.ParseFile(fn) rgs, errs := rulefmt.ParseFile(fn)
if errs != nil { if errs != nil {
return nil, tsdb.MultiError(errs) return nil, errs
} }
for _, rg := range rgs.Groups { for _, rg := range rgs.Groups {
@ -530,7 +534,7 @@ func (m *Manager) loadGroups(interval time.Duration, filenames ...string) (map[s
for _, r := range rg.Rules { for _, r := range rg.Rules {
expr, err := promql.ParseExpr(r.Expr) expr, err := promql.ParseExpr(r.Expr)
if err != nil { if err != nil {
return nil, err return nil, []error{err}
} }
if r.Alert != "" { if r.Alert != "" {