mirror of https://github.com/prometheus/prometheus
Merge pull request #3839 from brancz/remove-old-alert-record
promql: Remove old and unused alerting/reconding syntaxpull/4834/head
commit
bda9781ccd
|
@ -16,7 +16,6 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -26,16 +25,13 @@ import (
|
|||
"time"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/prometheus/client_golang/api"
|
||||
"github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
config_util "github.com/prometheus/common/config"
|
||||
"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/promlint"
|
||||
)
|
||||
|
||||
|
@ -60,10 +56,6 @@ func main() {
|
|||
|
||||
checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage)
|
||||
|
||||
updateCmd := app.Command("update", "Update the resources to newer formats.")
|
||||
updateRulesCmd := updateCmd.Command("rules", "Update rules from the 1.x to 2.x format.")
|
||||
ruleFilesUp := updateRulesCmd.Arg("rule-files", "The rule files to update.").Required().ExistingFiles()
|
||||
|
||||
queryCmd := app.Command("query", "Run query against a Prometheus server.")
|
||||
queryInstantCmd := queryCmd.Command("instant", "Run instant query.")
|
||||
queryServer := queryInstantCmd.Arg("server", "Prometheus server to query.").Required().String()
|
||||
|
@ -111,9 +103,6 @@ func main() {
|
|||
case checkMetricsCmd.FullCommand():
|
||||
os.Exit(CheckMetrics())
|
||||
|
||||
case updateRulesCmd.FullCommand():
|
||||
os.Exit(UpdateRules(*ruleFilesUp...))
|
||||
|
||||
case queryInstantCmd.FullCommand():
|
||||
os.Exit(QueryInstant(*queryServer, *queryExpr))
|
||||
|
||||
|
@ -296,74 +285,6 @@ func checkRules(filename string) (int, []error) {
|
|||
return numRules, nil
|
||||
}
|
||||
|
||||
// UpdateRules updates the rule files.
|
||||
func UpdateRules(files ...string) int {
|
||||
failed := false
|
||||
|
||||
for _, f := range files {
|
||||
if err := updateRules(f); err != nil {
|
||||
fmt.Fprintln(os.Stderr, " FAILED:", err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func updateRules(filename string) error {
|
||||
fmt.Println("Updating", filename)
|
||||
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rules, err := promql.ParseStmts(string(content))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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+".yml", y, 0666)
|
||||
}
|
||||
|
||||
var checkMetricsUsage = strings.TrimSpace(`
|
||||
Pass Prometheus metrics over stdin to lint them for consistency and correctness.
|
||||
|
||||
|
|
|
@ -48,18 +48,6 @@ type Statement interface {
|
|||
stmt()
|
||||
}
|
||||
|
||||
// Statements is a list of statement nodes that implements Node.
|
||||
type Statements []Statement
|
||||
|
||||
// AlertStmt represents an added alert rule.
|
||||
type AlertStmt struct {
|
||||
Name string
|
||||
Expr Expr
|
||||
Duration time.Duration
|
||||
Labels labels.Labels
|
||||
Annotations labels.Labels
|
||||
}
|
||||
|
||||
// EvalStmt holds an expression and information on the range it should
|
||||
// be evaluated on.
|
||||
type EvalStmt struct {
|
||||
|
@ -72,16 +60,7 @@ type EvalStmt struct {
|
|||
Interval time.Duration
|
||||
}
|
||||
|
||||
// RecordStmt represents an added recording rule.
|
||||
type RecordStmt struct {
|
||||
Name string
|
||||
Expr Expr
|
||||
Labels labels.Labels
|
||||
}
|
||||
|
||||
func (*AlertStmt) stmt() {}
|
||||
func (*EvalStmt) stmt() {}
|
||||
func (*RecordStmt) stmt() {}
|
||||
func (*EvalStmt) stmt() {}
|
||||
|
||||
// Expr is a generic interface for all expression types.
|
||||
type Expr interface {
|
||||
|
@ -257,27 +236,11 @@ func Walk(v Visitor, node Node, path []Node) error {
|
|||
path = append(path, node)
|
||||
|
||||
switch n := node.(type) {
|
||||
case Statements:
|
||||
for _, s := range n {
|
||||
if err := Walk(v, s, path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *AlertStmt:
|
||||
if err := Walk(v, n.Expr, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *EvalStmt:
|
||||
if err := Walk(v, n.Expr, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *RecordStmt:
|
||||
if err := Walk(v, n.Expr, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case Expressions:
|
||||
for _, e := range n {
|
||||
if err := Walk(v, e, path); err != nil {
|
||||
|
|
|
@ -183,11 +183,6 @@ const (
|
|||
|
||||
keywordsStart
|
||||
// Keywords.
|
||||
itemAlert
|
||||
itemIf
|
||||
itemFor
|
||||
itemLabels
|
||||
itemAnnotations
|
||||
itemOffset
|
||||
itemBy
|
||||
itemWithout
|
||||
|
@ -219,11 +214,6 @@ var key = map[string]ItemType{
|
|||
"quantile": itemQuantile,
|
||||
|
||||
// Keywords.
|
||||
"alert": itemAlert,
|
||||
"if": itemIf,
|
||||
"for": itemFor,
|
||||
"labels": itemLabels,
|
||||
"annotations": itemAnnotations,
|
||||
"offset": itemOffset,
|
||||
"by": itemBy,
|
||||
"without": itemWithout,
|
||||
|
|
|
@ -249,21 +249,6 @@ var tests = []struct {
|
|||
},
|
||||
// Test keywords.
|
||||
{
|
||||
input: "alert",
|
||||
expected: []item{{itemAlert, 0, "alert"}},
|
||||
}, {
|
||||
input: "if",
|
||||
expected: []item{{itemIf, 0, "if"}},
|
||||
}, {
|
||||
input: "for",
|
||||
expected: []item{{itemFor, 0, "for"}},
|
||||
}, {
|
||||
input: "labels",
|
||||
expected: []item{{itemLabels, 0, "labels"}},
|
||||
}, {
|
||||
input: "annotations",
|
||||
expected: []item{{itemAnnotations, 0, "annotations"}},
|
||||
}, {
|
||||
input: "offset",
|
||||
expected: []item{{itemOffset, 0, "offset"}},
|
||||
}, {
|
||||
|
|
130
promql/parse.go
130
promql/parse.go
|
@ -51,18 +51,6 @@ func (e *ParseErr) Error() string {
|
|||
return fmt.Sprintf("parse error at line %d, char %d: %s", e.Line, e.Pos, e.Err)
|
||||
}
|
||||
|
||||
// ParseStmts parses the input and returns the resulting statements or any occurring error.
|
||||
func ParseStmts(input string) (Statements, error) {
|
||||
p := newParser(input)
|
||||
|
||||
stmts, err := p.parseStmts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = p.typecheck(stmts)
|
||||
return stmts, err
|
||||
}
|
||||
|
||||
// ParseExpr returns the expression parsed from the input.
|
||||
func ParseExpr(input string) (Expr, error) {
|
||||
p := newParser(input)
|
||||
|
@ -112,20 +100,6 @@ func newParser(input string) *parser {
|
|||
return p
|
||||
}
|
||||
|
||||
// parseStmts parses a sequence of statements from the input.
|
||||
func (p *parser) parseStmts() (stmts Statements, err error) {
|
||||
defer p.recover(&err)
|
||||
stmts = Statements{}
|
||||
|
||||
for p.peek().typ != itemEOF {
|
||||
if p.peek().typ == itemComment {
|
||||
continue
|
||||
}
|
||||
stmts = append(stmts, p.stmt())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseExpr parses a single expression from the input.
|
||||
func (p *parser) parseExpr() (expr Expr, err error) {
|
||||
defer p.recover(&err)
|
||||
|
@ -365,93 +339,6 @@ func (p *parser) recover(errp *error) {
|
|||
}
|
||||
}
|
||||
|
||||
// stmt parses any statement.
|
||||
//
|
||||
// alertStatement | recordStatement
|
||||
//
|
||||
func (p *parser) stmt() Statement {
|
||||
switch tok := p.peek(); tok.typ {
|
||||
case itemAlert:
|
||||
return p.alertStmt()
|
||||
case itemIdentifier, itemMetricIdentifier:
|
||||
return p.recordStmt()
|
||||
}
|
||||
p.errorf("no valid statement detected")
|
||||
return nil
|
||||
}
|
||||
|
||||
// alertStmt parses an alert rule.
|
||||
//
|
||||
// ALERT name IF expr [FOR duration]
|
||||
// [LABELS label_set]
|
||||
// [ANNOTATIONS label_set]
|
||||
//
|
||||
func (p *parser) alertStmt() *AlertStmt {
|
||||
const ctx = "alert statement"
|
||||
|
||||
p.expect(itemAlert, ctx)
|
||||
name := p.expect(itemIdentifier, ctx)
|
||||
// Alerts require a Vector typed expression.
|
||||
p.expect(itemIf, ctx)
|
||||
expr := p.expr()
|
||||
|
||||
// Optional for clause.
|
||||
var (
|
||||
duration time.Duration
|
||||
err error
|
||||
)
|
||||
if p.peek().typ == itemFor {
|
||||
p.next()
|
||||
dur := p.expect(itemDuration, ctx)
|
||||
duration, err = parseDuration(dur.val)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
lset labels.Labels
|
||||
annotations labels.Labels
|
||||
)
|
||||
if p.peek().typ == itemLabels {
|
||||
p.expect(itemLabels, ctx)
|
||||
lset = p.labelSet()
|
||||
}
|
||||
if p.peek().typ == itemAnnotations {
|
||||
p.expect(itemAnnotations, ctx)
|
||||
annotations = p.labelSet()
|
||||
}
|
||||
|
||||
return &AlertStmt{
|
||||
Name: name.val,
|
||||
Expr: expr,
|
||||
Duration: duration,
|
||||
Labels: lset,
|
||||
Annotations: annotations,
|
||||
}
|
||||
}
|
||||
|
||||
// recordStmt parses a recording rule.
|
||||
func (p *parser) recordStmt() *RecordStmt {
|
||||
const ctx = "record statement"
|
||||
|
||||
name := p.expectOneOf(itemIdentifier, itemMetricIdentifier, ctx).val
|
||||
|
||||
var lset labels.Labels
|
||||
if p.peek().typ == itemLeftBrace {
|
||||
lset = p.labelSet()
|
||||
}
|
||||
|
||||
p.expect(itemAssign, ctx)
|
||||
expr := p.expr()
|
||||
|
||||
return &RecordStmt{
|
||||
Name: name,
|
||||
Labels: lset,
|
||||
Expr: expr,
|
||||
}
|
||||
}
|
||||
|
||||
// expr parses any expression.
|
||||
func (p *parser) expr() Expr {
|
||||
// Parse the starting expression.
|
||||
|
@ -1009,9 +896,9 @@ func (p *parser) expectType(node Node, want ValueType, context string) {
|
|||
// them, but the costs are small and might reveal errors when making changes.
|
||||
func (p *parser) checkType(node Node) (typ ValueType) {
|
||||
// For expressions the type is determined by their Type function.
|
||||
// Statements and lists do not have a type but are not invalid either.
|
||||
// Lists do not have a type but are not invalid either.
|
||||
switch n := node.(type) {
|
||||
case Statements, Expressions, Statement:
|
||||
case Expressions:
|
||||
typ = ValueTypeNone
|
||||
case Expr:
|
||||
typ = n.Type()
|
||||
|
@ -1022,25 +909,12 @@ func (p *parser) checkType(node Node) (typ ValueType) {
|
|||
// Recursively check correct typing for child nodes and raise
|
||||
// errors in case of bad typing.
|
||||
switch n := node.(type) {
|
||||
case Statements:
|
||||
for _, s := range n {
|
||||
p.expectType(s, ValueTypeNone, "statement list")
|
||||
}
|
||||
case *AlertStmt:
|
||||
p.expectType(n.Expr, ValueTypeVector, "alert statement")
|
||||
|
||||
case *EvalStmt:
|
||||
ty := p.checkType(n.Expr)
|
||||
if ty == ValueTypeNone {
|
||||
p.errorf("evaluation statement must have a valid expression type but got %s", documentedType(ty))
|
||||
}
|
||||
|
||||
case *RecordStmt:
|
||||
ty := p.checkType(n.Expr)
|
||||
if ty != ValueTypeVector && ty != ValueTypeScalar {
|
||||
p.errorf("record statement must have a valid expression of type instant vector or scalar but got %s", documentedType(ty))
|
||||
}
|
||||
|
||||
case Expressions:
|
||||
for _, e := range n {
|
||||
ty := p.checkType(e)
|
||||
|
|
|
@ -1341,10 +1341,6 @@ var testExpr = []struct {
|
|||
input: "e-+=/(0)",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
}, {
|
||||
input: "-If",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
},
|
||||
// String quoting and escape sequence interpretation tests.
|
||||
{
|
||||
|
@ -1445,241 +1441,6 @@ func TestNaNExpression(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var testStatement = []struct {
|
||||
input string
|
||||
expected Statements
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
// Test a file-like input.
|
||||
input: `
|
||||
# A simple test recording rule.
|
||||
dc:http_request:rate5m = sum(rate(http_request_count[5m])) by (dc)
|
||||
|
||||
# A simple test alerting rule.
|
||||
ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5m
|
||||
LABELS {
|
||||
service = "testservice"
|
||||
# ... more fields here ...
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "Global request rate low",
|
||||
description = "The global request rate is low"
|
||||
}
|
||||
|
||||
foo = bar{label1="value1"}
|
||||
|
||||
ALERT BazAlert IF foo > 10
|
||||
ANNOTATIONS {
|
||||
description = "BazAlert",
|
||||
runbook = "http://my.url",
|
||||
summary = "Baz",
|
||||
}
|
||||
`,
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "dc:http_request:rate5m",
|
||||
Expr: &AggregateExpr{
|
||||
Op: itemSum,
|
||||
Grouping: []string{"dc"},
|
||||
Expr: &Call{
|
||||
Func: mustGetFunction("rate"),
|
||||
Args: Expressions{
|
||||
&MatrixSelector{
|
||||
Name: "http_request_count",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "http_request_count"),
|
||||
},
|
||||
Range: 5 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Labels: nil,
|
||||
},
|
||||
&AlertStmt{
|
||||
Name: "GlobalRequestRateLow",
|
||||
Expr: &ParenExpr{&BinaryExpr{
|
||||
Op: itemLSS,
|
||||
LHS: &VectorSelector{
|
||||
Name: "dc:http_request:rate5m",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "dc:http_request:rate5m"),
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{10000},
|
||||
}},
|
||||
Labels: labels.FromStrings("service", "testservice"),
|
||||
Duration: 5 * time.Minute,
|
||||
Annotations: labels.FromStrings(
|
||||
"summary", "Global request rate low",
|
||||
"description", "The global request rate is low",
|
||||
),
|
||||
},
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &VectorSelector{
|
||||
Name: "bar",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, "label1", "value1"),
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
|
||||
},
|
||||
},
|
||||
},
|
||||
&AlertStmt{
|
||||
Name: "BazAlert",
|
||||
Expr: &BinaryExpr{
|
||||
Op: itemGTR,
|
||||
LHS: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "foo"),
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{10},
|
||||
},
|
||||
Annotations: labels.FromStrings(
|
||||
"summary", "Baz",
|
||||
"description", "BazAlert",
|
||||
"runbook", "http://my.url",
|
||||
),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
input: `foo{x="", a="z"} = bar{a="b", x=~"y"}`,
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &VectorSelector{
|
||||
Name: "bar",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, "a", "b"),
|
||||
mustLabelMatcher(labels.MatchRegexp, "x", "y"),
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "bar"),
|
||||
},
|
||||
},
|
||||
Labels: labels.FromStrings("x", "", "a", "z"),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
input: `ALERT SomeName IF some_metric > 1
|
||||
LABELS {}
|
||||
ANNOTATIONS {
|
||||
summary = "Global request rate low",
|
||||
description = "The global request rate is low",
|
||||
}
|
||||
`,
|
||||
expected: Statements{
|
||||
&AlertStmt{
|
||||
Name: "SomeName",
|
||||
Expr: &BinaryExpr{
|
||||
Op: itemGTR,
|
||||
LHS: &VectorSelector{
|
||||
Name: "some_metric",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
mustLabelMatcher(labels.MatchEqual, string(model.MetricNameLabel), "some_metric"),
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{1},
|
||||
},
|
||||
Labels: labels.Labels{},
|
||||
Annotations: labels.FromStrings(
|
||||
"summary", "Global request rate low",
|
||||
"description", "The global request rate is low",
|
||||
),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
input: `
|
||||
# A simple test alerting rule.
|
||||
ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5
|
||||
LABELS {
|
||||
service = "testservice"
|
||||
# ... more fields here ...
|
||||
}
|
||||
ANNOTATIONS {
|
||||
summary = "Global request rate low"
|
||||
description = "The global request rate is low"
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: "",
|
||||
expected: Statements{},
|
||||
}, {
|
||||
input: "foo = time()",
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &Call{Func: mustGetFunction("time")},
|
||||
Labels: nil,
|
||||
}},
|
||||
}, {
|
||||
input: "foo = 1",
|
||||
expected: Statements{
|
||||
&RecordStmt{
|
||||
Name: "foo",
|
||||
Expr: &NumberLiteral{1},
|
||||
Labels: nil,
|
||||
}},
|
||||
}, {
|
||||
input: "foo = bar[5m]",
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo = "test"`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo = `,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo{a!="b"} = bar`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo{a=~"b"} = bar`,
|
||||
fail: true,
|
||||
}, {
|
||||
input: `foo{a!~"b"} = bar`,
|
||||
fail: true,
|
||||
},
|
||||
// Fuzzing regression tests.
|
||||
{
|
||||
input: `I=-/`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
input: `I=3E8/-=`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
input: `M=-=-0-0`,
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseStatements(t *testing.T) {
|
||||
for _, test := range testStatement {
|
||||
stmts, err := ParseStmts(test.input)
|
||||
|
||||
// Unexpected errors are always caused by a bug.
|
||||
if err == errUnexpected {
|
||||
t.Fatalf("unexpected error occurred")
|
||||
}
|
||||
|
||||
if !test.fail && err != nil {
|
||||
t.Errorf("error in input: \n\n%s\n", test.input)
|
||||
t.Fatalf("could not parse: %s", err)
|
||||
}
|
||||
if test.fail && err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(stmts, test.expected) {
|
||||
t.Errorf("error in input: \n\n%s\n", test.input)
|
||||
t.Fatalf("no match\n\nexpected:\n%s\ngot: \n%s\n", Tree(test.expected), Tree(stmts))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mustLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher {
|
||||
m, err := labels.NewMatcher(mt, name, val)
|
||||
if err != nil {
|
||||
|
|
|
@ -34,30 +34,14 @@ func tree(node Node, level string) string {
|
|||
}
|
||||
typs := strings.Split(fmt.Sprintf("%T", node), ".")[1]
|
||||
|
||||
var t string
|
||||
// Only print the number of statements for readability.
|
||||
if stmts, ok := node.(Statements); ok {
|
||||
t = fmt.Sprintf("%s |---- %s :: %d\n", level, typs, len(stmts))
|
||||
} else {
|
||||
t = fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)
|
||||
}
|
||||
t := fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)
|
||||
|
||||
level += " · · ·"
|
||||
|
||||
switch n := node.(type) {
|
||||
case Statements:
|
||||
for _, s := range n {
|
||||
t += tree(s, level)
|
||||
}
|
||||
case *AlertStmt:
|
||||
t += tree(n.Expr, level)
|
||||
|
||||
case *EvalStmt:
|
||||
t += tree(n.Expr, level)
|
||||
|
||||
case *RecordStmt:
|
||||
t += tree(n.Expr, level)
|
||||
|
||||
case Expressions:
|
||||
for _, e := range n {
|
||||
t += tree(e, level)
|
||||
|
@ -87,41 +71,10 @@ func tree(node Node, level string) string {
|
|||
return t
|
||||
}
|
||||
|
||||
func (stmts Statements) String() (s string) {
|
||||
if len(stmts) == 0 {
|
||||
return ""
|
||||
}
|
||||
for _, stmt := range stmts {
|
||||
s += stmt.String()
|
||||
s += "\n\n"
|
||||
}
|
||||
return s[:len(s)-2]
|
||||
}
|
||||
|
||||
func (node *AlertStmt) String() string {
|
||||
s := fmt.Sprintf("ALERT %s", node.Name)
|
||||
s += fmt.Sprintf("\n\tIF %s", node.Expr)
|
||||
if node.Duration > 0 {
|
||||
s += fmt.Sprintf("\n\tFOR %s", model.Duration(node.Duration))
|
||||
}
|
||||
if len(node.Labels) > 0 {
|
||||
s += fmt.Sprintf("\n\tLABELS %s", node.Labels)
|
||||
}
|
||||
if len(node.Annotations) > 0 {
|
||||
s += fmt.Sprintf("\n\tANNOTATIONS %s", node.Annotations)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (node *EvalStmt) String() string {
|
||||
return "EVAL " + node.Expr.String()
|
||||
}
|
||||
|
||||
func (node *RecordStmt) String() string {
|
||||
s := fmt.Sprintf("%s%s = %s", node.Name, node.Labels, node.Expr)
|
||||
return s
|
||||
}
|
||||
|
||||
func (es Expressions) String() (s string) {
|
||||
if len(es) == 0 {
|
||||
return ""
|
||||
|
|
|
@ -15,40 +15,8 @@ package promql
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
)
|
||||
|
||||
func TestStatementString(t *testing.T) {
|
||||
in := &AlertStmt{
|
||||
Name: "FooAlert",
|
||||
Expr: &BinaryExpr{
|
||||
Op: itemGTR,
|
||||
LHS: &VectorSelector{
|
||||
Name: "foo",
|
||||
LabelMatchers: []*labels.Matcher{
|
||||
{Type: labels.MatchEqual, Name: labels.MetricName, Value: "bar"},
|
||||
},
|
||||
},
|
||||
RHS: &NumberLiteral{10},
|
||||
},
|
||||
Duration: 5 * time.Minute,
|
||||
Labels: labels.FromStrings("foo", "bar"),
|
||||
Annotations: labels.FromStrings("notify", "team-a"),
|
||||
}
|
||||
|
||||
expected := `ALERT FooAlert
|
||||
IF foo > 10
|
||||
FOR 5m
|
||||
LABELS {foo="bar"}
|
||||
ANNOTATIONS {notify="team-a"}`
|
||||
|
||||
if in.String() != expected {
|
||||
t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, in.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestExprString(t *testing.T) {
|
||||
// A list of valid expressions that are expected to be
|
||||
// returned as out when calling String(). If out is empty the output
|
||||
|
@ -129,35 +97,3 @@ func TestExprString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStmtsString(t *testing.T) {
|
||||
// A list of valid statements that are expected to be returned as out when
|
||||
// calling String(). If out is empty the output is expected to equal the
|
||||
// input.
|
||||
inputs := []struct {
|
||||
in, out string
|
||||
}{
|
||||
{
|
||||
in: `ALERT foo IF up == 0 FOR 1m`,
|
||||
out: "ALERT foo\n\tIF up == 0\n\tFOR 1m",
|
||||
},
|
||||
{
|
||||
in: `ALERT foo IF up == 0 FOR 1m ANNOTATIONS {summary="foo"}`,
|
||||
out: "ALERT foo\n\tIF up == 0\n\tFOR 1m\n\tANNOTATIONS {summary=\"foo\"}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range inputs {
|
||||
expr, err := ParseStmts(test.in)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing error for %q: %s", test.in, err)
|
||||
}
|
||||
exp := test.in
|
||||
if test.out != "" {
|
||||
exp = test.out
|
||||
}
|
||||
if expr.String() != exp {
|
||||
t.Fatalf("expected %q to be returned as:\n%s\ngot:\n%s\n", test.in, exp, expr.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue