diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go index 4c610764b4..16198eb1cd 100644 --- a/cmd/promtool/main.go +++ b/cmd/promtool/main.go @@ -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. diff --git a/promql/ast.go b/promql/ast.go index 0c8a3ad2ab..e55989bbb9 100644 --- a/promql/ast.go +++ b/promql/ast.go @@ -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 { diff --git a/promql/lex.go b/promql/lex.go index 4a8130491f..01372ae2e1 100644 --- a/promql/lex.go +++ b/promql/lex.go @@ -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, diff --git a/promql/lex_test.go b/promql/lex_test.go index 333e3b7a0f..178222a0cc 100644 --- a/promql/lex_test.go +++ b/promql/lex_test.go @@ -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"}}, }, { diff --git a/promql/parse.go b/promql/parse.go index 5fe2042b2d..8abf8dbf93 100644 --- a/promql/parse.go +++ b/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) diff --git a/promql/parse_test.go b/promql/parse_test.go index 18b54b8246..f277015489 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -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 { diff --git a/promql/printer.go b/promql/printer.go index 563adbe757..2a362dfdaa 100644 --- a/promql/printer.go +++ b/promql/printer.go @@ -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 "" diff --git a/promql/printer_test.go b/promql/printer_test.go index da0bd9fe7b..9abb595577 100644 --- a/promql/printer_test.go +++ b/promql/printer_test.go @@ -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()) - } - } -}