mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 06:04:05 -08:00
Implement initial no-op alert parsing and rule parsing tests.
This commit is contained in:
parent
955708e8db
commit
c0601abf46
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,7 +8,6 @@
|
||||||
*.orig
|
*.orig
|
||||||
*.pyc
|
*.pyc
|
||||||
*.rej
|
*.rej
|
||||||
*.rules
|
|
||||||
*.so
|
*.so
|
||||||
*~
|
*~
|
||||||
.*.swp
|
.*.swp
|
||||||
|
|
|
@ -54,30 +54,30 @@ var configTests = []struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigs(t *testing.T) {
|
func TestConfigs(t *testing.T) {
|
||||||
for _, configTest := range configTests {
|
for i, configTest := range configTests {
|
||||||
testConfig, err := LoadFromFile(path.Join(fixturesPath, configTest.inputFile))
|
testConfig, err := LoadFromFile(path.Join(fixturesPath, configTest.inputFile))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !configTest.shouldFail {
|
if !configTest.shouldFail {
|
||||||
t.Errorf("Error parsing config %v: %v", configTest.inputFile, err)
|
t.Fatalf("%d. Error parsing config %v: %v", i, configTest.inputFile, err)
|
||||||
} else {
|
} else {
|
||||||
if !strings.Contains(err.Error(), configTest.errContains) {
|
if !strings.Contains(err.Error(), configTest.errContains) {
|
||||||
t.Errorf("Expected error containing '%v', got: %v", configTest.errContains, err)
|
t.Fatalf("%d. Expected error containing '%v', got: %v", i, configTest.errContains, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printedConfig, err := ioutil.ReadFile(path.Join(fixturesPath, configTest.printedFile))
|
printedConfig, err := ioutil.ReadFile(path.Join(fixturesPath, configTest.printedFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error reading config %v: %v", configTest.inputFile, err)
|
t.Fatalf("%d. Error reading config %v: %v", i, configTest.inputFile, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expected := string(printedConfig)
|
expected := string(printedConfig)
|
||||||
actual := testConfig.ToString(0)
|
actual := testConfig.ToString(0)
|
||||||
|
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Errorf("%v: printed config doesn't match expected output", configTest.inputFile)
|
t.Errorf("%d. %v: printed config doesn't match expected output", i, configTest.inputFile)
|
||||||
t.Errorf("Expected:\n%v\n\nActual:\n%v\n", expected, actual)
|
t.Errorf("Expected:\n%v\n\nActual:\n%v\n", expected, actual)
|
||||||
t.Errorf("Writing expected and actual printed configs to /tmp for diffing (see test source for paths)")
|
t.Fatalf("Writing expected and actual printed configs to /tmp for diffing (see test source for paths)")
|
||||||
ioutil.WriteFile(fmt.Sprintf("/tmp/%s.expected", configTest.printedFile), []byte(expected), 0600)
|
ioutil.WriteFile(fmt.Sprintf("/tmp/%s.expected", configTest.printedFile), []byte(expected), 0600)
|
||||||
ioutil.WriteFile(fmt.Sprintf("/tmp/%s.actual", configTest.printedFile), []byte(actual), 0600)
|
ioutil.WriteFile(fmt.Sprintf("/tmp/%s.actual", configTest.printedFile), []byte(actual), 0600)
|
||||||
}
|
}
|
||||||
|
|
0
rules/fixtures/empty.rules
Normal file
0
rules/fixtures/empty.rules
Normal file
13
rules/fixtures/mixed.rules
Normal file
13
rules/fixtures/mixed.rules
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// 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 WITH {
|
||||||
|
description = "Global HTTP request rate low!",
|
||||||
|
summary = "Request rate low"
|
||||||
|
/* ... more fields here ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
foo = bar{label1="value1"}
|
||||||
|
|
||||||
|
ALERT BazAlert IF(foo > 10) WITH {}
|
1
rules/fixtures/non_vector.rules
Normal file
1
rules/fixtures/non_vector.rules
Normal file
|
@ -0,0 +1 @@
|
||||||
|
now = time()
|
13
rules/fixtures/syntax_error.rules
Normal file
13
rules/fixtures/syntax_error.rules
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// A simple test recording rule.
|
||||||
|
dc_http_request_rate5m = sum(rate(http_request_count[5m])) by (dc)
|
||||||
|
|
||||||
|
// A simple test alerting rule with a syntax error (invalid duration string "5").
|
||||||
|
ALERT GlobalRequestRateLow IF(dc_http_request_rate5m < 10000) FOR 5 WITH {
|
||||||
|
description = "Global HTTP request rate low!",
|
||||||
|
summary = "Request rate low"
|
||||||
|
/* ... more fields here ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
foo = bar{label1="value1"}
|
||||||
|
|
||||||
|
ALERT BazAlert IF(foo > 10) WITH {}
|
|
@ -20,11 +20,22 @@ import (
|
||||||
"github.com/prometheus/prometheus/utility"
|
"github.com/prometheus/prometheus/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateRule(name string, labels model.LabelSet, root ast.Node, permanent bool) (*Rule, error) {
|
func CreateRecordingRule(name string, labels model.LabelSet, expr ast.Node, permanent bool) (*RecordingRule, error) {
|
||||||
if root.Type() != ast.VECTOR {
|
if _, ok := expr.(ast.VectorNode); !ok {
|
||||||
return nil, fmt.Errorf("Rule %v does not evaluate to vector type", name)
|
return nil, fmt.Errorf("Recording rule expression %v does not evaluate to vector type", expr)
|
||||||
}
|
}
|
||||||
return NewRule(name, labels, root.(ast.VectorNode), permanent), nil
|
return NewRecordingRule(name, labels, expr.(ast.VectorNode), permanent), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels model.LabelSet) (*AlertingRule, error) {
|
||||||
|
if _, ok := expr.(ast.VectorNode); !ok {
|
||||||
|
return nil, fmt.Errorf("Alert rule expression %v does not evaluate to vector type", expr)
|
||||||
|
}
|
||||||
|
holdDuration, err := utility.StringToDuration(holdDurationStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewAlertingRule(name, expr.(ast.VectorNode), holdDuration, labels), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
|
func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
|
||||||
|
@ -40,7 +51,7 @@ func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy []model.LabelName) (*ast.VectorAggregation, error) {
|
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy []model.LabelName) (*ast.VectorAggregation, error) {
|
||||||
if vector.Type() != ast.VECTOR {
|
if _, ok := vector.(ast.VectorNode); !ok {
|
||||||
return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr)
|
return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr)
|
||||||
}
|
}
|
||||||
var aggrTypes = map[string]ast.AggrType{
|
var aggrTypes = map[string]ast.AggrType{
|
||||||
|
|
|
@ -37,11 +37,16 @@ U [smhdwy]
|
||||||
|
|
||||||
\/\/[^\r\n]*\n { /* gobble up one-line comments */ }
|
\/\/[^\r\n]*\n { /* gobble up one-line comments */ }
|
||||||
|
|
||||||
permanent { return PERMANENT }
|
ALERT|alert { return ALERT }
|
||||||
|
IF|if { return IF }
|
||||||
|
FOR|for { return FOR }
|
||||||
|
WITH|with { return WITH }
|
||||||
|
|
||||||
|
PERMANENT|permanent { return PERMANENT }
|
||||||
BY|by { return GROUP_OP }
|
BY|by { return GROUP_OP }
|
||||||
AVG|SUM|MAX|MIN { yylval.str = yytext; return AGGR_OP }
|
AVG|SUM|MAX|MIN { yylval.str = yytext; return AGGR_OP }
|
||||||
avg|sum|max|min { yylval.str = strings.ToUpper(yytext); return AGGR_OP }
|
avg|sum|max|min { yylval.str = strings.ToUpper(yytext); return AGGR_OP }
|
||||||
\<|>|AND|OR { yylval.str = yytext; return CMP_OP }
|
\<|>|AND|OR|and|or { yylval.str = strings.ToUpper(yytext); return CMP_OP }
|
||||||
==|!=|>=|<= { yylval.str = yytext; return CMP_OP }
|
==|!=|>=|<= { yylval.str = yytext; return CMP_OP }
|
||||||
[+\-] { yylval.str = yytext; return ADDITIVE_OP }
|
[+\-] { yylval.str = yytext; return ADDITIVE_OP }
|
||||||
[*/%] { yylval.str = yytext; return MULT_OP }
|
[*/%] { yylval.str = yytext; return MULT_OP }
|
||||||
|
@ -49,7 +54,7 @@ avg|sum|max|min { yylval.str = strings.ToUpper(yytext); return AGGR_OP
|
||||||
{D}+{U} { yylval.str = yytext; return DURATION }
|
{D}+{U} { yylval.str = yytext; return DURATION }
|
||||||
{L}({L}|{D})* { yylval.str = yytext; return IDENTIFIER }
|
{L}({L}|{D})* { yylval.str = yytext; return IDENTIFIER }
|
||||||
|
|
||||||
\-?{D}+(\.{D}*)? { num, err := strconv.ParseFloat(yytext, 32);
|
\-?{D}+(\.{D}*)? { num, err := strconv.ParseFloat(yytext, 64);
|
||||||
if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) {
|
if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) {
|
||||||
panic("Invalid float")
|
panic("Invalid float")
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,7 +302,59 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
}}, {regexp.MustCompile("permanent"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
}}, {regexp.MustCompile("ALERT|alert"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if r != "yyREJECT" {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
yyar.returnType = yyRT_REJECT
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
{
|
||||||
|
return yyactionreturn{ALERT, yyRT_USER_RETURN}
|
||||||
|
}
|
||||||
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
|
}}, {regexp.MustCompile("IF|if"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if r != "yyREJECT" {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
yyar.returnType = yyRT_REJECT
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
{
|
||||||
|
return yyactionreturn{IF, yyRT_USER_RETURN}
|
||||||
|
}
|
||||||
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
|
}}, {regexp.MustCompile("FOR|for"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if r != "yyREJECT" {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
yyar.returnType = yyRT_REJECT
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
{
|
||||||
|
return yyactionreturn{FOR, yyRT_USER_RETURN}
|
||||||
|
}
|
||||||
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
|
}}, {regexp.MustCompile("WITH|with"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if r != "yyREJECT" {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
yyar.returnType = yyRT_REJECT
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
{
|
||||||
|
return yyactionreturn{WITH, yyRT_USER_RETURN}
|
||||||
|
}
|
||||||
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
|
}}, {regexp.MustCompile("PERMANENT|permanent"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
if r != "yyREJECT" {
|
if r != "yyREJECT" {
|
||||||
|
@ -356,7 +408,7 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
|
||||||
return yyactionreturn{AGGR_OP, yyRT_USER_RETURN}
|
return yyactionreturn{AGGR_OP, yyRT_USER_RETURN}
|
||||||
}
|
}
|
||||||
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
}}, {regexp.MustCompile("\\<|>|AND|OR"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
}}, {regexp.MustCompile("\\<|>|AND|OR|and|or"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
if r != "yyREJECT" {
|
if r != "yyREJECT" {
|
||||||
|
@ -366,7 +418,7 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
yylval.str = yytext
|
yylval.str = strings.ToUpper(yytext)
|
||||||
return yyactionreturn{CMP_OP, yyRT_USER_RETURN}
|
return yyactionreturn{CMP_OP, yyRT_USER_RETURN}
|
||||||
}
|
}
|
||||||
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
return yyactionreturn{0, yyRT_FALLTHROUGH}
|
||||||
|
@ -450,7 +502,7 @@ var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcon
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
num, err := strconv.ParseFloat(yytext, 32)
|
num, err := strconv.ParseFloat(yytext, 64)
|
||||||
if err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax {
|
if err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax {
|
||||||
panic("Invalid float")
|
panic("Invalid float")
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ var (
|
||||||
type RulesLexer struct {
|
type RulesLexer struct {
|
||||||
errors []string // Errors encountered during parsing.
|
errors []string // Errors encountered during parsing.
|
||||||
startToken int // Dummy token to simulate multiple start symbols (see below).
|
startToken int // Dummy token to simulate multiple start symbols (see below).
|
||||||
parsedRules []*Rule // Parsed full rules.
|
parsedRules []Rule // Parsed full rules.
|
||||||
parsedExpr ast.Node // Parsed single expression.
|
parsedExpr ast.Node // Parsed single expression.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,23 +95,23 @@ func LoadFromReader(rulesReader io.Reader, singleExpr bool) (interface{}, error)
|
||||||
panic("")
|
panic("")
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadRulesFromReader(rulesReader io.Reader) ([]*Rule, error) {
|
func LoadRulesFromReader(rulesReader io.Reader) ([]Rule, error) {
|
||||||
expr, err := LoadFromReader(rulesReader, false)
|
expr, err := LoadFromReader(rulesReader, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return expr.([]*Rule), err
|
return expr.([]Rule), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadRulesFromString(rulesString string) ([]*Rule, error) {
|
func LoadRulesFromString(rulesString string) ([]Rule, error) {
|
||||||
rulesReader := strings.NewReader(rulesString)
|
rulesReader := strings.NewReader(rulesString)
|
||||||
return LoadRulesFromReader(rulesReader)
|
return LoadRulesFromReader(rulesReader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadRulesFromFile(fileName string) ([]*Rule, error) {
|
func LoadRulesFromFile(fileName string) ([]Rule, error) {
|
||||||
rulesReader, err := os.Open(fileName)
|
rulesReader, err := os.Open(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*Rule{}, err
|
return []Rule{}, err
|
||||||
}
|
}
|
||||||
defer rulesReader.Close()
|
defer rulesReader.Close()
|
||||||
return LoadRulesFromReader(rulesReader)
|
return LoadRulesFromReader(rulesReader)
|
||||||
|
|
|
@ -31,7 +31,7 @@ type RuleManager interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ruleManager struct {
|
type ruleManager struct {
|
||||||
rules []*Rule
|
rules []Rule
|
||||||
results chan *Result
|
results chan *Result
|
||||||
done chan bool
|
done chan bool
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
|
@ -40,7 +40,8 @@ type ruleManager struct {
|
||||||
func NewRuleManager(results chan *Result, interval time.Duration) RuleManager {
|
func NewRuleManager(results chan *Result, interval time.Duration) RuleManager {
|
||||||
manager := &ruleManager{
|
manager := &ruleManager{
|
||||||
results: results,
|
results: results,
|
||||||
rules: []*Rule{},
|
rules: []Rule{},
|
||||||
|
done: make(chan bool),
|
||||||
interval: interval,
|
interval: interval,
|
||||||
}
|
}
|
||||||
go manager.run(results)
|
go manager.run(results)
|
||||||
|
@ -70,7 +71,7 @@ func (m *ruleManager) runIteration(results chan *Result) {
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
for _, rule := range m.rules {
|
for _, rule := range m.rules {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(rule *Rule) {
|
go func(rule Rule) {
|
||||||
vector, err := rule.Eval(&now)
|
vector, err := rule.Eval(&now)
|
||||||
m.results <- &Result{
|
m.results <- &Result{
|
||||||
Samples: vector,
|
Samples: vector,
|
||||||
|
|
|
@ -39,12 +39,14 @@
|
||||||
%token <num> NUMBER
|
%token <num> NUMBER
|
||||||
%token PERMANENT GROUP_OP
|
%token PERMANENT GROUP_OP
|
||||||
%token <str> AGGR_OP CMP_OP ADDITIVE_OP MULT_OP
|
%token <str> AGGR_OP CMP_OP ADDITIVE_OP MULT_OP
|
||||||
|
%token ALERT IF FOR WITH
|
||||||
|
|
||||||
%type <ruleNodeSlice> func_arg_list
|
%type <ruleNodeSlice> func_arg_list
|
||||||
%type <labelNameSlice> label_list grouping_opts
|
%type <labelNameSlice> label_list grouping_opts
|
||||||
%type <labelSet> label_assign label_assign_list rule_labels
|
%type <labelSet> label_assign label_assign_list rule_labels
|
||||||
%type <ruleNode> rule_expr func_arg
|
%type <ruleNode> rule_expr func_arg
|
||||||
%type <boolean> qualifier
|
%type <boolean> qualifier
|
||||||
|
%type <str> for_duration
|
||||||
|
|
||||||
%right '='
|
%right '='
|
||||||
%left CMP_OP
|
%left CMP_OP
|
||||||
|
@ -67,10 +69,22 @@ saved_rule_expr : rule_expr
|
||||||
|
|
||||||
rules_stat : qualifier IDENTIFIER rule_labels '=' rule_expr
|
rules_stat : qualifier IDENTIFIER rule_labels '=' rule_expr
|
||||||
{
|
{
|
||||||
rule, err := CreateRule($2, $3, $5, $1)
|
rule, err := CreateRecordingRule($2, $3, $5, $1)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
||||||
}
|
}
|
||||||
|
| ALERT IDENTIFIER IF rule_expr for_duration WITH rule_labels
|
||||||
|
{
|
||||||
|
rule, err := CreateAlertingRule($2, $4, $5, $7)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
for_duration : /* empty */
|
||||||
|
{ $$ = "0s" }
|
||||||
|
| FOR DURATION
|
||||||
|
{ $$ = $2 }
|
||||||
;
|
;
|
||||||
|
|
||||||
qualifier : /* empty */
|
qualifier : /* empty */
|
||||||
|
|
|
@ -30,6 +30,10 @@ const AGGR_OP = 57354
|
||||||
const CMP_OP = 57355
|
const CMP_OP = 57355
|
||||||
const ADDITIVE_OP = 57356
|
const ADDITIVE_OP = 57356
|
||||||
const MULT_OP = 57357
|
const MULT_OP = 57357
|
||||||
|
const ALERT = 57358
|
||||||
|
const IF = 57359
|
||||||
|
const FOR = 57360
|
||||||
|
const WITH = 57361
|
||||||
|
|
||||||
var yyToknames = []string{
|
var yyToknames = []string{
|
||||||
"START_RULES",
|
"START_RULES",
|
||||||
|
@ -44,6 +48,10 @@ var yyToknames = []string{
|
||||||
"CMP_OP",
|
"CMP_OP",
|
||||||
"ADDITIVE_OP",
|
"ADDITIVE_OP",
|
||||||
"MULT_OP",
|
"MULT_OP",
|
||||||
|
"ALERT",
|
||||||
|
"IF",
|
||||||
|
"FOR",
|
||||||
|
"WITH",
|
||||||
" =",
|
" =",
|
||||||
}
|
}
|
||||||
var yyStatenames = []string{}
|
var yyStatenames = []string{}
|
||||||
|
@ -52,7 +60,7 @@ const yyEofCode = 1
|
||||||
const yyErrCode = 2
|
const yyErrCode = 2
|
||||||
const yyMaxDepth = 200
|
const yyMaxDepth = 200
|
||||||
|
|
||||||
//line parser.y:175
|
//line parser.y:189
|
||||||
|
|
||||||
|
|
||||||
//line yacctab:1
|
//line yacctab:1
|
||||||
|
@ -61,75 +69,79 @@ var yyExca = []int{
|
||||||
1, -1,
|
1, -1,
|
||||||
-2, 0,
|
-2, 0,
|
||||||
-1, 4,
|
-1, 4,
|
||||||
6, 7,
|
6, 10,
|
||||||
-2, 1,
|
-2, 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
const yyNprod = 33
|
const yyNprod = 36
|
||||||
const yyPrivate = 57344
|
const yyPrivate = 57344
|
||||||
|
|
||||||
var yyTokenNames []string
|
var yyTokenNames []string
|
||||||
var yyStates []string
|
var yyStates []string
|
||||||
|
|
||||||
const yyLast = 84
|
const yyLast = 97
|
||||||
|
|
||||||
var yyAct = []int{
|
var yyAct = []int{
|
||||||
|
|
||||||
32, 36, 31, 16, 6, 17, 15, 16, 18, 40,
|
20, 38, 34, 17, 33, 43, 6, 67, 15, 66,
|
||||||
14, 14, 21, 46, 14, 20, 25, 26, 27, 8,
|
19, 18, 16, 17, 15, 45, 59, 44, 60, 27,
|
||||||
33, 54, 10, 38, 22, 9, 19, 17, 15, 16,
|
28, 29, 16, 17, 15, 41, 40, 18, 16, 17,
|
||||||
17, 15, 16, 7, 30, 28, 14, 8, 33, 14,
|
18, 16, 17, 22, 15, 23, 21, 46, 47, 49,
|
||||||
10, 15, 16, 9, 47, 48, 49, 21, 53, 14,
|
15, 8, 35, 15, 10, 51, 22, 9, 50, 53,
|
||||||
39, 7, 8, 37, 58, 10, 57, 42, 9, 41,
|
52, 48, 61, 57, 18, 16, 17, 39, 8, 7,
|
||||||
43, 44, 52, 13, 45, 35, 7, 24, 50, 59,
|
32, 10, 65, 42, 9, 56, 30, 15, 8, 35,
|
||||||
56, 37, 23, 2, 3, 11, 5, 4, 1, 12,
|
54, 10, 62, 37, 9, 14, 7, 26, 68, 64,
|
||||||
34, 51, 55, 29,
|
39, 13, 25, 24, 2, 3, 7, 11, 5, 4,
|
||||||
|
1, 58, 12, 36, 55, 63, 31,
|
||||||
}
|
}
|
||||||
var yyPact = []int{
|
var yyPact = []int{
|
||||||
|
|
||||||
69, -1000, -1000, 46, 53, -1000, 17, 46, -5, 4,
|
80, -1000, -1000, 52, 65, -1000, 17, 52, 12, 11,
|
||||||
-1000, -1000, 66, -1000, 59, 46, 46, 46, 14, -1000,
|
-1000, -1000, 77, 76, -1000, 69, 52, 52, 52, 41,
|
||||||
13, 47, 46, 30, -14, -12, -11, 27, -1000, 38,
|
-1000, 35, 51, 52, 25, 46, -22, -12, -18, 8,
|
||||||
-1000, -1000, 17, -1000, 42, -1000, -1000, 48, -8, 28,
|
-1000, -8, -1000, -1000, 17, -1000, 15, -1000, -1000, 31,
|
||||||
-1000, -1000, 31, -1000, 65, 61, 51, 46, -1000, -1000,
|
14, 28, 52, -1000, -1000, 62, -1000, 74, 63, 54,
|
||||||
-1000, -1000, 1, 17, 64, 35, -1000, -1000, 63, -1000,
|
52, -2, -1000, -1000, -1000, -1000, -6, 17, 33, 64,
|
||||||
|
73, 25, -1000, -16, -1000, -1000, -1000, 72, -1000,
|
||||||
}
|
}
|
||||||
var yyPgo = []int{
|
var yyPgo = []int{
|
||||||
|
|
||||||
0, 83, 82, 81, 1, 80, 26, 0, 2, 79,
|
0, 96, 95, 94, 1, 93, 0, 2, 4, 92,
|
||||||
78, 77, 76, 75,
|
91, 90, 89, 88, 87,
|
||||||
}
|
}
|
||||||
var yyR1 = []int{
|
var yyR1 = []int{
|
||||||
|
|
||||||
0, 10, 10, 11, 11, 12, 13, 9, 9, 6,
|
0, 11, 11, 12, 12, 13, 14, 14, 10, 10,
|
||||||
6, 6, 5, 5, 4, 7, 7, 7, 7, 7,
|
9, 9, 6, 6, 6, 5, 5, 4, 7, 7,
|
||||||
7, 7, 7, 7, 7, 3, 3, 2, 2, 1,
|
7, 7, 7, 7, 7, 7, 7, 7, 3, 3,
|
||||||
1, 8, 8,
|
2, 2, 1, 1, 8, 8,
|
||||||
}
|
}
|
||||||
var yyR2 = []int{
|
var yyR2 = []int{
|
||||||
|
|
||||||
0, 2, 2, 0, 2, 1, 5, 0, 1, 0,
|
0, 2, 2, 0, 2, 1, 5, 7, 0, 2,
|
||||||
3, 2, 1, 3, 3, 3, 2, 4, 3, 4,
|
0, 1, 0, 3, 2, 1, 3, 3, 3, 2,
|
||||||
5, 3, 3, 3, 1, 0, 4, 1, 3, 1,
|
4, 3, 4, 5, 3, 3, 3, 1, 0, 4,
|
||||||
3, 1, 1,
|
1, 3, 1, 3, 1, 1,
|
||||||
}
|
}
|
||||||
var yyChk = []int{
|
var yyChk = []int{
|
||||||
|
|
||||||
-1000, -10, 4, 5, -11, -12, -7, 20, 6, 12,
|
-1000, -11, 4, 5, -12, -13, -7, 24, 6, 12,
|
||||||
9, -13, -9, 10, 22, 14, 15, 13, -7, -6,
|
9, -14, -9, 16, 10, 26, 14, 15, 13, -7,
|
||||||
20, 17, 20, 6, 8, -7, -7, -7, 21, -1,
|
-6, 24, 21, 24, 6, 6, 8, -7, -7, -7,
|
||||||
21, -8, -7, 7, -5, 18, -4, 6, -7, -6,
|
25, -1, 25, -8, -7, 7, -5, 22, -4, 6,
|
||||||
23, 21, 19, 18, 19, 16, 21, 16, -8, -4,
|
-7, -6, 17, 27, 25, 23, 22, 23, 20, 25,
|
||||||
7, -3, 11, -7, 20, -2, 6, 21, 19, 6,
|
20, -7, -8, -4, 7, -3, 11, -7, -10, 18,
|
||||||
|
24, 19, 8, -2, 6, -6, 25, 23, 6,
|
||||||
}
|
}
|
||||||
var yyDef = []int{
|
var yyDef = []int{
|
||||||
|
|
||||||
0, -2, 3, 0, -2, 2, 5, 0, 9, 0,
|
0, -2, 3, 0, -2, 2, 5, 0, 12, 0,
|
||||||
24, 4, 0, 8, 0, 0, 0, 0, 0, 16,
|
27, 4, 0, 0, 11, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 9, 0, 21, 22, 23, 15, 0,
|
19, 0, 0, 0, 12, 0, 0, 24, 25, 26,
|
||||||
18, 29, 31, 32, 0, 11, 12, 0, 0, 0,
|
18, 0, 21, 32, 34, 35, 0, 14, 15, 0,
|
||||||
19, 17, 0, 10, 0, 0, 25, 0, 30, 13,
|
0, 0, 0, 22, 20, 0, 13, 0, 0, 28,
|
||||||
14, 20, 0, 6, 0, 0, 27, 26, 0, 28,
|
0, 8, 33, 16, 17, 23, 0, 6, 0, 0,
|
||||||
|
0, 12, 9, 0, 30, 7, 29, 0, 31,
|
||||||
}
|
}
|
||||||
var yyTok1 = []int{
|
var yyTok1 = []int{
|
||||||
|
|
||||||
|
@ -137,20 +149,20 @@ var yyTok1 = []int{
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
20, 21, 3, 3, 19, 3, 3, 3, 3, 3,
|
24, 25, 3, 3, 23, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 16, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 20, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 22, 3, 23, 3, 3, 3, 3, 3, 3,
|
3, 26, 3, 27, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 17, 3, 18,
|
3, 3, 3, 21, 3, 22,
|
||||||
}
|
}
|
||||||
var yyTok2 = []int{
|
var yyTok2 = []int{
|
||||||
|
|
||||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||||
12, 13, 14, 15,
|
12, 13, 14, 15, 16, 17, 18, 19,
|
||||||
}
|
}
|
||||||
var yyTok3 = []int{
|
var yyTok3 = []int{
|
||||||
0,
|
0,
|
||||||
|
@ -381,120 +393,133 @@ yydefault:
|
||||||
switch yynt {
|
switch yynt {
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
//line parser.y:65
|
//line parser.y:67
|
||||||
{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode }
|
{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode }
|
||||||
case 6:
|
case 6:
|
||||||
//line parser.y:69
|
//line parser.y:71
|
||||||
{
|
{
|
||||||
rule, err := CreateRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
|
rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
||||||
}
|
}
|
||||||
case 7:
|
case 7:
|
||||||
//line parser.y:77
|
//line parser.y:77
|
||||||
{ yyVAL.boolean = false }
|
{
|
||||||
|
rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
||||||
|
}
|
||||||
case 8:
|
case 8:
|
||||||
//line parser.y:79
|
|
||||||
{ yyVAL.boolean = true }
|
|
||||||
case 9:
|
|
||||||
//line parser.y:83
|
|
||||||
{ yyVAL.labelSet = model.LabelSet{} }
|
|
||||||
case 10:
|
|
||||||
//line parser.y:85
|
//line parser.y:85
|
||||||
{ yyVAL.labelSet = yyS[yypt-1].labelSet }
|
{ yyVAL.str = "0s" }
|
||||||
case 11:
|
case 9:
|
||||||
//line parser.y:87
|
//line parser.y:87
|
||||||
{ yyVAL.labelSet = model.LabelSet{} }
|
{ yyVAL.str = yyS[yypt-0].str }
|
||||||
|
case 10:
|
||||||
|
//line parser.y:91
|
||||||
|
{ yyVAL.boolean = false }
|
||||||
|
case 11:
|
||||||
|
//line parser.y:93
|
||||||
|
{ yyVAL.boolean = true }
|
||||||
case 12:
|
case 12:
|
||||||
//line parser.y:90
|
//line parser.y:97
|
||||||
{ yyVAL.labelSet = yyS[yypt-0].labelSet }
|
{ yyVAL.labelSet = model.LabelSet{} }
|
||||||
case 13:
|
case 13:
|
||||||
//line parser.y:92
|
//line parser.y:99
|
||||||
{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } }
|
{ yyVAL.labelSet = yyS[yypt-1].labelSet }
|
||||||
case 14:
|
case 14:
|
||||||
//line parser.y:96
|
|
||||||
{ yyVAL.labelSet = model.LabelSet{ model.LabelName(yyS[yypt-2].str): model.LabelValue(yyS[yypt-0].str) } }
|
|
||||||
case 15:
|
|
||||||
//line parser.y:101
|
//line parser.y:101
|
||||||
{ yyVAL.ruleNode = yyS[yypt-1].ruleNode }
|
{ yyVAL.labelSet = model.LabelSet{} }
|
||||||
|
case 15:
|
||||||
|
//line parser.y:104
|
||||||
|
{ yyVAL.labelSet = yyS[yypt-0].labelSet }
|
||||||
case 16:
|
case 16:
|
||||||
//line parser.y:103
|
//line parser.y:106
|
||||||
{ yyS[yypt-0].labelSet[model.MetricNameLabel] = model.LabelValue(yyS[yypt-1].str); yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet) }
|
{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } }
|
||||||
case 17:
|
case 17:
|
||||||
//line parser.y:105
|
//line parser.y:110
|
||||||
|
{ yyVAL.labelSet = model.LabelSet{ model.LabelName(yyS[yypt-2].str): model.LabelValue(yyS[yypt-0].str) } }
|
||||||
|
case 18:
|
||||||
|
//line parser.y:115
|
||||||
|
{ yyVAL.ruleNode = yyS[yypt-1].ruleNode }
|
||||||
|
case 19:
|
||||||
|
//line parser.y:117
|
||||||
|
{ yyS[yypt-0].labelSet[model.MetricNameLabel] = model.LabelValue(yyS[yypt-1].str); yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet) }
|
||||||
|
case 20:
|
||||||
|
//line parser.y:119
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
|
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 18:
|
case 21:
|
||||||
//line parser.y:111
|
//line parser.y:125
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
|
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 19:
|
case 22:
|
||||||
//line parser.y:117
|
//line parser.y:131
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
|
yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 20:
|
case 23:
|
||||||
//line parser.y:123
|
//line parser.y:137
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice)
|
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 21:
|
|
||||||
//line parser.y:131
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
|
||||||
}
|
|
||||||
case 22:
|
|
||||||
//line parser.y:137
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
|
||||||
}
|
|
||||||
case 23:
|
|
||||||
//line parser.y:143
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
|
||||||
}
|
|
||||||
case 24:
|
case 24:
|
||||||
//line parser.y:149
|
//line parser.y:145
|
||||||
{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)}
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
case 25:
|
case 25:
|
||||||
//line parser.y:153
|
//line parser.y:151
|
||||||
{ yyVAL.labelNameSlice = []model.LabelName{} }
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
case 26:
|
case 26:
|
||||||
//line parser.y:155
|
//line parser.y:157
|
||||||
{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice }
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
case 27:
|
case 27:
|
||||||
//line parser.y:159
|
//line parser.y:163
|
||||||
{ yyVAL.labelNameSlice = []model.LabelName{model.LabelName(yyS[yypt-0].str)} }
|
{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)}
|
||||||
case 28:
|
case 28:
|
||||||
//line parser.y:161
|
|
||||||
{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, model.LabelName(yyS[yypt-0].str)) }
|
|
||||||
case 29:
|
|
||||||
//line parser.y:165
|
|
||||||
{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} }
|
|
||||||
case 30:
|
|
||||||
//line parser.y:167
|
//line parser.y:167
|
||||||
{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) }
|
{ yyVAL.labelNameSlice = []model.LabelName{} }
|
||||||
case 31:
|
case 29:
|
||||||
//line parser.y:171
|
//line parser.y:169
|
||||||
{ yyVAL.ruleNode = yyS[yypt-0].ruleNode }
|
{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice }
|
||||||
case 32:
|
case 30:
|
||||||
//line parser.y:173
|
//line parser.y:173
|
||||||
|
{ yyVAL.labelNameSlice = []model.LabelName{model.LabelName(yyS[yypt-0].str)} }
|
||||||
|
case 31:
|
||||||
|
//line parser.y:175
|
||||||
|
{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, model.LabelName(yyS[yypt-0].str)) }
|
||||||
|
case 32:
|
||||||
|
//line parser.y:179
|
||||||
|
{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} }
|
||||||
|
case 33:
|
||||||
|
//line parser.y:181
|
||||||
|
{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) }
|
||||||
|
case 34:
|
||||||
|
//line parser.y:185
|
||||||
|
{ yyVAL.ruleNode = yyS[yypt-0].ruleNode }
|
||||||
|
case 35:
|
||||||
|
//line parser.y:187
|
||||||
{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) }
|
{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) }
|
||||||
}
|
}
|
||||||
goto yystack /* stack new state and value */
|
goto yystack /* stack new state and value */
|
||||||
|
|
|
@ -20,21 +20,41 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A recorded rule.
|
// A Rule encapsulates a vector expression which is evaluated at a specified
|
||||||
type Rule struct {
|
// interval and acted upon (currently either recorded or used for alerting).
|
||||||
|
type Rule interface {
|
||||||
|
// Name returns the name of the rule.
|
||||||
|
Name() string
|
||||||
|
// EvalRaw evaluates the rule's vector expression without triggering any
|
||||||
|
// other actions, like recording or alerting.
|
||||||
|
EvalRaw(timestamp *time.Time) (vector ast.Vector, err error)
|
||||||
|
// Eval evaluates the rule, including any associated recording or alerting actions.
|
||||||
|
Eval(timestamp *time.Time) (vector ast.Vector, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A RecordingRule records its vector expression into new timeseries.
|
||||||
|
type RecordingRule struct {
|
||||||
name string
|
name string
|
||||||
vector ast.VectorNode
|
vector ast.VectorNode
|
||||||
labels model.LabelSet
|
labels model.LabelSet
|
||||||
permanent bool
|
permanent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *Rule) Name() string { return rule.name }
|
// An alerting rule generates alerts from its vector expression.
|
||||||
|
type AlertingRule struct {
|
||||||
|
name string
|
||||||
|
vector ast.VectorNode
|
||||||
|
holdDuration time.Duration
|
||||||
|
labels model.LabelSet
|
||||||
|
}
|
||||||
|
|
||||||
func (rule *Rule) EvalRaw(timestamp *time.Time) (vector ast.Vector, err error) {
|
func (rule RecordingRule) Name() string { return rule.name }
|
||||||
|
|
||||||
|
func (rule RecordingRule) EvalRaw(timestamp *time.Time) (vector ast.Vector, err error) {
|
||||||
return ast.EvalVectorInstant(rule.vector, *timestamp)
|
return ast.EvalVectorInstant(rule.vector, *timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *Rule) Eval(timestamp *time.Time) (vector ast.Vector, err error) {
|
func (rule RecordingRule) Eval(timestamp *time.Time) (vector ast.Vector, err error) {
|
||||||
// Get the raw value of the rule expression.
|
// Get the raw value of the rule expression.
|
||||||
vector, err = rule.EvalRaw(timestamp)
|
vector, err = rule.EvalRaw(timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -55,20 +75,46 @@ func (rule *Rule) Eval(timestamp *time.Time) (vector ast.Vector, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *Rule) RuleToDotGraph() string {
|
func (rule RecordingRule) RuleToDotGraph() string {
|
||||||
graph := "digraph \"Rules\" {\n"
|
graph := "digraph \"Rules\" {\n"
|
||||||
graph += fmt.Sprintf("%#p[shape=\"box\",label=\"%v = \"];\n", rule, rule.name)
|
graph += fmt.Sprintf("%#p[shape=\"box\",label=\"%v = \"];\n", rule, rule.name)
|
||||||
graph += fmt.Sprintf("%#p -> %#p;\n", rule, rule.vector)
|
graph += fmt.Sprintf("%#p -> %#p;\n", &rule, rule.vector)
|
||||||
graph += rule.vector.NodeTreeToDotGraph()
|
graph += rule.vector.NodeTreeToDotGraph()
|
||||||
graph += "}\n"
|
graph += "}\n"
|
||||||
return graph
|
return graph
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRule(name string, labels model.LabelSet, vector ast.VectorNode, permanent bool) *Rule {
|
func (rule AlertingRule) Name() string { return rule.name }
|
||||||
return &Rule{
|
|
||||||
|
func (rule AlertingRule) EvalRaw(timestamp *time.Time) (vector ast.Vector, err error) {
|
||||||
|
return ast.EvalVectorInstant(rule.vector, *timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rule AlertingRule) Eval(timestamp *time.Time) (vector ast.Vector, err error) {
|
||||||
|
// Get the raw value of the rule expression.
|
||||||
|
vector, err = rule.EvalRaw(timestamp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(julius): handle alerting.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecordingRule(name string, labels model.LabelSet, vector ast.VectorNode, permanent bool) *RecordingRule {
|
||||||
|
return &RecordingRule{
|
||||||
name: name,
|
name: name,
|
||||||
labels: labels,
|
labels: labels,
|
||||||
vector: vector,
|
vector: vector,
|
||||||
permanent: permanent,
|
permanent: permanent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels model.LabelSet) *AlertingRule {
|
||||||
|
return &AlertingRule{
|
||||||
|
name: name,
|
||||||
|
vector: vector,
|
||||||
|
holdDuration: holdDuration,
|
||||||
|
labels: labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,12 +18,16 @@ import (
|
||||||
"github.com/prometheus/prometheus/rules/ast"
|
"github.com/prometheus/prometheus/rules/ast"
|
||||||
"github.com/prometheus/prometheus/storage/metric"
|
"github.com/prometheus/prometheus/storage/metric"
|
||||||
"github.com/prometheus/prometheus/utility/test"
|
"github.com/prometheus/prometheus/utility/test"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testEvalTime = testStartTime.Add(testDuration5m * 10)
|
var (
|
||||||
|
testEvalTime = testStartTime.Add(testDuration5m * 10)
|
||||||
|
fixturesPath = "fixtures"
|
||||||
|
)
|
||||||
|
|
||||||
// Labels in expected output need to be alphabetically sorted.
|
// Labels in expected output need to be alphabetically sorted.
|
||||||
var expressionTests = []struct {
|
var expressionTests = []struct {
|
||||||
|
@ -349,3 +353,70 @@ func TestExpressions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ruleTests = []struct {
|
||||||
|
inputFile string
|
||||||
|
shouldFail bool
|
||||||
|
errContains string
|
||||||
|
numRecordingRules int
|
||||||
|
numAlertingRules int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
inputFile: "empty.rules",
|
||||||
|
numRecordingRules: 0,
|
||||||
|
numAlertingRules: 0,
|
||||||
|
}, {
|
||||||
|
inputFile: "mixed.rules",
|
||||||
|
numRecordingRules: 2,
|
||||||
|
numAlertingRules: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputFile: "syntax_error.rules",
|
||||||
|
shouldFail: true,
|
||||||
|
errContains: "Error parsing rules at line 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputFile: "non_vector.rules",
|
||||||
|
shouldFail: true,
|
||||||
|
errContains: "does not evaluate to vector type",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRules(t *testing.T) {
|
||||||
|
for i, ruleTest := range ruleTests {
|
||||||
|
testRules, err := LoadRulesFromFile(path.Join(fixturesPath, ruleTest.inputFile))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !ruleTest.shouldFail {
|
||||||
|
t.Fatalf("%d. Error parsing rules file %v: %v", i, ruleTest.inputFile, err)
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(err.Error(), ruleTest.errContains) {
|
||||||
|
t.Fatalf("%d. Expected error containing '%v', got: %v", i, ruleTest.errContains, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
numRecordingRules := 0
|
||||||
|
numAlertingRules := 0
|
||||||
|
|
||||||
|
for j, rule := range testRules {
|
||||||
|
switch rule.(type) {
|
||||||
|
case *RecordingRule:
|
||||||
|
numRecordingRules++
|
||||||
|
case *AlertingRule:
|
||||||
|
numAlertingRules++
|
||||||
|
default:
|
||||||
|
t.Fatalf("%d.%d. Unknown rule type!", i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if numRecordingRules != ruleTest.numRecordingRules {
|
||||||
|
t.Fatalf("%d. Expected %d recording rules, got %d", i, ruleTest.numRecordingRules, numRecordingRules)
|
||||||
|
}
|
||||||
|
if numAlertingRules != ruleTest.numAlertingRules {
|
||||||
|
t.Fatalf("%d. Expected %d alerting rules, got %d", i, ruleTest.numAlertingRules, numAlertingRules)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(julius): add more complex checks on the parsed rules here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue