| 
									
										
										
										
											2017-06-14 16:04:13 +08:00
										 |  |  | // Copyright 2017 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | package rulefmt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-08-09 21:42:25 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-08-09 21:42:25 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2019-04-16 00:52:58 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2017-06-14 17:37:54 +08:00
										 |  |  | 	"github.com/prometheus/common/model" | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	yaml "gopkg.in/yaml.v3" | 
					
						
							| 
									
										
										
										
											2019-03-26 07:01:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-08 22:23:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/model/timestamp" | 
					
						
							| 
									
										
										
										
											2020-02-04 02:06:39 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/promql/parser" | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/template" | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-02 22:54:09 +08:00
										 |  |  | // Error represents semantic errors on parsing rule groups.
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | type Error struct { | 
					
						
							| 
									
										
										
										
											2017-12-06 23:39:06 +08:00
										 |  |  | 	Group    string | 
					
						
							|  |  |  | 	Rule     int | 
					
						
							|  |  |  | 	RuleName string | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	Err      WrappedError | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-13 06:38:59 +08:00
										 |  |  | // Error prints the error message in a formatted string.
 | 
					
						
							|  |  |  | func (err *Error) Error() string { | 
					
						
							|  |  |  | 	if err.Err.nodeAlt != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err.Err.err, "%d:%d: %d:%d: group %q, rule %d, %q", err.Err.node.Line, err.Err.node.Column, err.Err.nodeAlt.Line, err.Err.nodeAlt.Column, err.Group, err.Rule, err.RuleName).Error() | 
					
						
							|  |  |  | 	} else if err.Err.node != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(err.Err.err, "%d:%d: group %q, rule %d, %q", err.Err.node.Line, err.Err.node.Column, err.Group, err.Rule, err.RuleName).Error() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return errors.Wrapf(err.Err.err, "group %q, rule %d, %q", err.Group, err.Rule, err.RuleName).Error() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | // WrappedError wraps error with the yaml node which can be used to represent
 | 
					
						
							|  |  |  | // the line and column numbers of the error.
 | 
					
						
							|  |  |  | type WrappedError struct { | 
					
						
							|  |  |  | 	err     error | 
					
						
							|  |  |  | 	node    *yaml.Node | 
					
						
							|  |  |  | 	nodeAlt *yaml.Node | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-13 06:38:59 +08:00
										 |  |  | // Error prints the error message in a formatted string.
 | 
					
						
							|  |  |  | func (we *WrappedError) Error() string { | 
					
						
							|  |  |  | 	if we.nodeAlt != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(we.err, "%d:%d: %d:%d", we.node.Line, we.node.Column, we.nodeAlt.Line, we.nodeAlt.Column).Error() | 
					
						
							|  |  |  | 	} else if we.node != nil { | 
					
						
							|  |  |  | 		return errors.Wrapf(we.err, "%d:%d", we.node.Line, we.node.Column).Error() | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-11-13 06:38:59 +08:00
										 |  |  | 	return we.err.Error() | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RuleGroups is a set of rule groups that are typically exposed in a file.
 | 
					
						
							|  |  |  | type RuleGroups struct { | 
					
						
							| 
									
										
										
										
											2017-06-19 19:08:46 +08:00
										 |  |  | 	Groups []RuleGroup `yaml:"groups"` | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | type ruleGroups struct { | 
					
						
							|  |  |  | 	Groups []yaml.Node `yaml:"groups"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | // Validate validates all rules in the rule groups.
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | func (g *RuleGroups) Validate(node ruleGroups) (errs []error) { | 
					
						
							| 
									
										
										
										
											2017-06-14 14:49:21 +08:00
										 |  |  | 	set := map[string]struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	for j, g := range g.Groups { | 
					
						
							| 
									
										
										
										
											2017-06-14 15:51:32 +08:00
										 |  |  | 		if g.Name == "" { | 
					
						
							| 
									
										
										
										
											2020-07-09 06:00:06 +08:00
										 |  |  | 			errs = append(errs, errors.Errorf("%d:%d: Groupname must not be empty", node.Groups[j].Line, node.Groups[j].Column)) | 
					
						
							| 
									
										
										
										
											2017-06-14 15:51:32 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-14 14:49:21 +08:00
										 |  |  | 		if _, ok := set[g.Name]; ok { | 
					
						
							|  |  |  | 			errs = append( | 
					
						
							|  |  |  | 				errs, | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 				errors.Errorf("%d:%d: groupname: \"%s\" is repeated in the same file", node.Groups[j].Line, node.Groups[j].Column, g.Name), | 
					
						
							| 
									
										
										
										
											2017-06-14 14:49:21 +08:00
										 |  |  | 			) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		set[g.Name] = struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 		for i, r := range g.Rules { | 
					
						
							| 
									
										
										
										
											2021-10-16 02:24:55 +08:00
										 |  |  | 			for _, node := range g.Rules[i].Validate() { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 				var ruleName yaml.Node | 
					
						
							|  |  |  | 				if r.Alert.Value != "" { | 
					
						
							| 
									
										
										
										
											2017-12-06 23:39:06 +08:00
										 |  |  | 					ruleName = r.Alert | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					ruleName = r.Record | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 				errs = append(errs, &Error{ | 
					
						
							| 
									
										
										
										
											2017-12-06 23:39:06 +08:00
										 |  |  | 					Group:    g.Name, | 
					
						
							| 
									
										
										
										
											2020-07-02 18:09:01 +08:00
										 |  |  | 					Rule:     i + 1, | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 					RuleName: ruleName.Value, | 
					
						
							|  |  |  | 					Err:      node, | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-16 19:14:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	return errs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RuleGroup is a list of sequentially evaluated recording and alerting rules.
 | 
					
						
							|  |  |  | type RuleGroup struct { | 
					
						
							| 
									
										
										
										
											2017-06-16 13:16:21 +08:00
										 |  |  | 	Name     string         `yaml:"name"` | 
					
						
							|  |  |  | 	Interval model.Duration `yaml:"interval,omitempty"` | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 	Limit    int            `yaml:"limit,omitempty"` | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	Rules    []RuleNode     `yaml:"rules"` | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Rule describes an alerting or recording rule.
 | 
					
						
							|  |  |  | type Rule struct { | 
					
						
							| 
									
										
										
										
											2017-06-16 13:16:21 +08:00
										 |  |  | 	Record      string            `yaml:"record,omitempty"` | 
					
						
							|  |  |  | 	Alert       string            `yaml:"alert,omitempty"` | 
					
						
							|  |  |  | 	Expr        string            `yaml:"expr"` | 
					
						
							|  |  |  | 	For         model.Duration    `yaml:"for,omitempty"` | 
					
						
							|  |  |  | 	Labels      map[string]string `yaml:"labels,omitempty"` | 
					
						
							|  |  |  | 	Annotations map[string]string `yaml:"annotations,omitempty"` | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | // RuleNode adds yaml.v3 layer to support line and column outputs for invalid rules.
 | 
					
						
							|  |  |  | type RuleNode struct { | 
					
						
							|  |  |  | 	Record      yaml.Node         `yaml:"record,omitempty"` | 
					
						
							|  |  |  | 	Alert       yaml.Node         `yaml:"alert,omitempty"` | 
					
						
							|  |  |  | 	Expr        yaml.Node         `yaml:"expr"` | 
					
						
							|  |  |  | 	For         model.Duration    `yaml:"for,omitempty"` | 
					
						
							|  |  |  | 	Labels      map[string]string `yaml:"labels,omitempty"` | 
					
						
							|  |  |  | 	Annotations map[string]string `yaml:"annotations,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | // Validate the rule and return a list of encountered errors.
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | func (r *RuleNode) Validate() (nodes []WrappedError) { | 
					
						
							|  |  |  | 	if r.Record.Value != "" && r.Alert.Value != "" { | 
					
						
							|  |  |  | 		nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 			err:     errors.Errorf("only one of 'record' and 'alert' must be set"), | 
					
						
							|  |  |  | 			node:    &r.Record, | 
					
						
							|  |  |  | 			nodeAlt: &r.Alert, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	if r.Record.Value == "" && r.Alert.Value == "" { | 
					
						
							|  |  |  | 		if r.Record.Value == "0" { | 
					
						
							|  |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err:  errors.Errorf("one of 'record' or 'alert' must be set"), | 
					
						
							|  |  |  | 				node: &r.Alert, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err:  errors.Errorf("one of 'record' or 'alert' must be set"), | 
					
						
							|  |  |  | 				node: &r.Record, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-14 15:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	if r.Expr.Value == "" { | 
					
						
							|  |  |  | 		nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 			err:  errors.Errorf("field 'expr' must be set in rule"), | 
					
						
							|  |  |  | 			node: &r.Expr, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2020-02-04 02:06:39 +08:00
										 |  |  | 	} else if _, err := parser.ParseExpr(r.Expr.Value); err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 		nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 			err:  errors.Wrapf(err, "could not parse expression"), | 
					
						
							|  |  |  | 			node: &r.Expr, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	if r.Record.Value != "" { | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 		if len(r.Annotations) > 0 { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err:  errors.Errorf("invalid field 'annotations' in recording rule"), | 
					
						
							|  |  |  | 				node: &r.Record, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-16 13:16:21 +08:00
										 |  |  | 		if r.For != 0 { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err:  errors.Errorf("invalid field 'for' in recording rule"), | 
					
						
							|  |  |  | 				node: &r.Record, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 		if !model.IsValidMetricName(model.LabelValue(r.Record.Value)) { | 
					
						
							|  |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err:  errors.Errorf("invalid recording rule name: %s", r.Record.Value), | 
					
						
							|  |  |  | 				node: &r.Record, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2017-10-17 17:22:59 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-14 17:37:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for k, v := range r.Labels { | 
					
						
							| 
									
										
										
										
											2020-08-13 05:37:31 +08:00
										 |  |  | 		if !model.LabelName(k).IsValid() || k == model.MetricNameLabel { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err: errors.Errorf("invalid label name: %s", k), | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2017-06-14 17:37:54 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !model.LabelValue(v).IsValid() { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err: errors.Errorf("invalid label value: %s", v), | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2017-06-14 17:37:54 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k := range r.Annotations { | 
					
						
							|  |  |  | 		if !model.LabelName(k).IsValid() { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 			nodes = append(nodes, WrappedError{ | 
					
						
							|  |  |  | 				err: errors.Errorf("invalid annotation name: %s", k), | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2017-06-14 17:37:54 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	for _, err := range testTemplateParsing(r) { | 
					
						
							|  |  |  | 		nodes = append(nodes, WrappedError{err: err}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testTemplateParsing checks if the templates used in labels and annotations
 | 
					
						
							|  |  |  | // of the alerting rules are parsed correctly.
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | func testTemplateParsing(rl *RuleNode) (errs []error) { | 
					
						
							|  |  |  | 	if rl.Alert.Value == "" { | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 		// Not an alerting rule.
 | 
					
						
							|  |  |  | 		return errs | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Trying to parse templates.
 | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:26 +08:00
										 |  |  | 	tmplData := template.AlertTemplateData(map[string]string{}, map[string]string{}, "", 0) | 
					
						
							| 
									
										
										
										
											2019-04-16 00:52:58 +08:00
										 |  |  | 	defs := []string{ | 
					
						
							|  |  |  | 		"{{$labels := .Labels}}", | 
					
						
							|  |  |  | 		"{{$externalLabels := .ExternalLabels}}", | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:26 +08:00
										 |  |  | 		"{{$externalURL := .ExternalURL}}", | 
					
						
							| 
									
										
										
										
											2019-04-16 00:52:58 +08:00
										 |  |  | 		"{{$value := .Value}}", | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 	parseTest := func(text string) error { | 
					
						
							|  |  |  | 		tmpl := template.NewTemplateExpander( | 
					
						
							|  |  |  | 			context.TODO(), | 
					
						
							| 
									
										
										
										
											2019-04-16 00:52:58 +08:00
										 |  |  | 			strings.Join(append(defs, text), ""), | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 			"__alert_"+rl.Alert.Value, | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 			tmplData, | 
					
						
							|  |  |  | 			model.Time(timestamp.FromTime(time.Now())), | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			nil, | 
					
						
							| 
									
										
										
										
											2021-09-13 19:49:08 +08:00
										 |  |  | 			nil, | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 		) | 
					
						
							|  |  |  | 		return tmpl.ParseTest() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Parsing Labels.
 | 
					
						
							| 
									
										
										
										
											2019-08-28 23:36:48 +08:00
										 |  |  | 	for k, val := range rl.Labels { | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 		err := parseTest(val) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-08-28 23:36:48 +08:00
										 |  |  | 			errs = append(errs, errors.Wrapf(err, "label %q", k)) | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Parsing Annotations.
 | 
					
						
							| 
									
										
										
										
											2019-08-28 23:36:48 +08:00
										 |  |  | 	for k, val := range rl.Annotations { | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 		err := parseTest(val) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-08-28 23:36:48 +08:00
										 |  |  | 			errs = append(errs, errors.Wrapf(err, "annotation %q", k)) | 
					
						
							| 
									
										
										
										
											2018-09-13 20:55:58 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	return errs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 18:43:52 +08:00
										 |  |  | // Parse parses and validates a set of rules.
 | 
					
						
							|  |  |  | func Parse(content []byte) (*RuleGroups, []error) { | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		groups RuleGroups | 
					
						
							|  |  |  | 		node   ruleGroups | 
					
						
							| 
									
										
										
										
											2020-03-21 20:05:19 +08:00
										 |  |  | 		errs   []error | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2020-03-21 20:05:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-09 21:42:25 +08:00
										 |  |  | 	decoder := yaml.NewDecoder(bytes.NewReader(content)) | 
					
						
							|  |  |  | 	decoder.KnownFields(true) | 
					
						
							|  |  |  | 	err := decoder.Decode(&groups) | 
					
						
							|  |  |  | 	// Ignore io.EOF which happens with empty input.
 | 
					
						
							|  |  |  | 	if err != nil && err != io.EOF { | 
					
						
							| 
									
										
										
										
											2020-03-21 20:05:19 +08:00
										 |  |  | 		errs = append(errs, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = yaml.Unmarshal(content, &node) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		errs = append(errs, err) | 
					
						
							| 
									
										
										
										
											2018-01-22 18:43:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-21 20:05:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if len(errs) > 0 { | 
					
						
							|  |  |  | 		return nil, errs | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 02:07:54 +08:00
										 |  |  | 	return &groups, groups.Validate(node) | 
					
						
							| 
									
										
										
										
											2018-01-22 18:43:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ParseFile reads and parses rules from a file.
 | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | func ParseFile(file string) (*RuleGroups, []error) { | 
					
						
							|  |  |  | 	b, err := ioutil.ReadFile(file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-08-28 23:36:48 +08:00
										 |  |  | 		return nil, []error{errors.Wrap(err, file)} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rgs, errs := Parse(b) | 
					
						
							|  |  |  | 	for i := range errs { | 
					
						
							|  |  |  | 		errs[i] = errors.Wrap(errs[i], file) | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-28 23:36:48 +08:00
										 |  |  | 	return rgs, errs | 
					
						
							| 
									
										
										
										
											2017-06-07 22:58:15 +08:00
										 |  |  | } |