Skip to content

Commit

Permalink
Merge pull request #899 from cloudflare/felipesere/main
Browse files Browse the repository at this point in the history
Parse multiple document
  • Loading branch information
prymitive authored Mar 8, 2024
2 parents a27ad9b + fe5ce9b commit f7f32f5
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 61 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Fixed false positive reports from [rule/duplicate](checks/rule/duplicate.md) when
using symlinks.
- Fixed support for multi-document YAML files when using relaxed parsed mode - #746.

## v0.54.0

Expand Down
102 changes: 47 additions & 55 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package parser

import (
"bytes"
"errors"
"fmt"
"io"
"strings"

"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -41,76 +43,66 @@ func (p Parser) Parse(content []byte) (rules []Rule, err error) {
}
}()

var node yaml.Node
err = yaml.Unmarshal(content, &node)
if err != nil {
return nil, err
var documents []yaml.Node
dec := yaml.NewDecoder(bytes.NewReader(content))
for {
var doc yaml.Node
decodeErr := dec.Decode(&doc)
if errors.Is(decodeErr, io.EOF) {
break
}
if decodeErr != nil {
return nil, decodeErr
}
documents = append(documents, doc)
}

rules, err = parseNode(content, &node, 0)
for _, doc := range documents {
rules = append(rules, parseNode(content, &doc, 0)...)
}
return rules, err
}

func parseNode(content []byte, node *yaml.Node, offset int) (rules []Rule, err error) {
ret, isEmpty, err := parseRule(content, node, offset)
if err != nil {
return nil, err
}
func parseNode(content []byte, node *yaml.Node, offset int) (rules []Rule) {
ret, isEmpty := parseRule(content, node, offset)
if !isEmpty {
rules = append(rules, ret)
return rules, nil
return rules
}

var rl []Rule
var rule Rule
for _, root := range node.Content {
// nolint: exhaustive
switch root.Kind {
case yaml.SequenceNode:
for _, n := range root.Content {
rl, err = parseNode(content, n, offset)
if err != nil {
return nil, err
}
rules = append(rules, rl...)
rules = append(rules, parseNode(content, n, offset)...)
}
case yaml.MappingNode:
rule, isEmpty, err = parseRule(content, root, offset)
if err != nil {
return nil, err
}
rule, isEmpty = parseRule(content, root, offset)
if !isEmpty {
rules = append(rules, rule)
} else {
for _, n := range root.Content {
rl, err = parseNode(content, n, offset)
if err != nil {
return nil, err
}
rules = append(rules, rl...)
rules = append(rules, parseNode(content, n, offset)...)
}
}
case yaml.ScalarNode:
if root.Value != string(content) {
c := []byte(root.Value)
var n yaml.Node
err = yaml.Unmarshal(c, &n)
if err == nil {
ret, err := parseNode(c, &n, offset+root.Line)
if err != nil {
return nil, err
}
rules = append(rules, ret...)
if err := yaml.Unmarshal(c, &n); err == nil {
rules = append(rules, parseNode(c, &n, offset+root.Line)...)
}
}
}
}
return rules, nil
return rules
}

func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool, err error) {
func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool) {
if node.Kind != yaml.MappingNode {
return rule, true, err
return rule, true
}

var recordPart *YamlNode
Expand Down Expand Up @@ -253,7 +245,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Err: fmt.Errorf("got both %s and %s keys in a single rule", recordKey, alertKey),
},
}
return rule, false, err
return rule, false
}
if exprPart != nil && alertPart == nil && recordPart == nil {
rule = Rule{
Expand All @@ -263,7 +255,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Err: fmt.Errorf("incomplete rule, no %s or %s key", alertKey, recordKey),
},
}
return rule, false, err
return rule, false
}
if recordPart != nil && forPart != nil {
rule = Rule{
Expand All @@ -273,7 +265,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Err: fmt.Errorf("invalid field '%s' in recording rule", forKey),
},
}
return rule, false, err
return rule, false
}
if recordPart != nil && keepFiringForPart != nil {
rule = Rule{
Expand All @@ -283,7 +275,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Err: fmt.Errorf("invalid field '%s' in recording rule", keepFiringForKey),
},
}
return rule, false, err
return rule, false
}
if recordPart != nil && annotationsPart != nil {
rule = Rule{
Expand All @@ -293,7 +285,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Err: fmt.Errorf("invalid field '%s' in recording rule", annotationsKey),
},
}
return rule, false, err
return rule, false
}
for key, part := range map[string]*yaml.Node{
recordKey: recordNode,
Expand Down Expand Up @@ -328,10 +320,10 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
}

if r, ok := ensureRequiredKeys(lines, recordKey, recordPart, exprPart); !ok {
return r, false, err
return r, false
}
if r, ok := ensureRequiredKeys(lines, alertKey, alertPart, exprPart); !ok {
return r, false, err
return r, false
}
if (recordPart != nil || alertPart != nil) && len(unknownKeys) > 0 {
var keys []string
Expand All @@ -345,7 +337,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Err: fmt.Errorf("invalid key(s) found: %s", strings.Join(keys, ", ")),
},
}
return rule, false, err
return rule, false
}

if recordPart != nil && !model.IsValidMetricName(model.LabelValue(recordPart.Value)) {
Expand All @@ -355,7 +347,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Line: recordPart.Lines.First,
Err: fmt.Errorf("invalid recording rule name: %s", recordPart.Value),
},
}, false, err
}, false
}

if (recordPart != nil || alertPart != nil) && labelsPart != nil {
Expand All @@ -367,7 +359,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Line: lab.Key.Lines.First,
Err: fmt.Errorf("invalid label name: %s", lab.Key.Value),
},
}, false, err
}, false
}
if !model.LabelValue(lab.Value.Value).IsValid() {
return Rule{
Expand All @@ -376,7 +368,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Line: lab.Key.Lines.First,
Err: fmt.Errorf("invalid label value: %s", lab.Value.Value),
},
}, false, err
}, false
}
}
}
Expand All @@ -390,7 +382,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
Line: ann.Key.Lines.First,
Err: fmt.Errorf("invalid annotation name: %s", ann.Key.Value),
},
}, false, err
}, false
}
}
}
Expand All @@ -405,7 +397,7 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
},
Comments: ruleComments,
}
return rule, false, err
return rule, false
}

if alertPart != nil && exprPart != nil {
Expand All @@ -421,10 +413,10 @@ func parseRule(content []byte, node *yaml.Node, offset int) (rule Rule, _ bool,
},
Comments: ruleComments,
}
return rule, false, err
return rule, false
}

return rule, true, err
return rule, true
}

func unpackNodes(node *yaml.Node) []*yaml.Node {
Expand Down Expand Up @@ -532,26 +524,26 @@ func resolveMapAlias(part, parent *yaml.Node) *yaml.Node {
return &node
}

func duplicatedKeyError(lines LineRange, line int, key string) (Rule, bool, error) {
func duplicatedKeyError(lines LineRange, line int, key string) (Rule, bool) {
rule := Rule{
Lines: lines,
Error: ParseError{
Line: line,
Err: fmt.Errorf("duplicated %s key", key),
},
}
return rule, false, nil
return rule, false
}

func invalidValueError(lines LineRange, line int, key, expectedKind, gotKind string) (Rule, bool, error) {
func invalidValueError(lines LineRange, line int, key, expectedKind, gotKind string) (Rule, bool) {
rule := Rule{
Lines: lines,
Error: ParseError{
Line: line,
Err: fmt.Errorf("%s value must be a YAML %s, got %s instead", key, expectedKind, gotKind),
},
}
return rule, false, nil
return rule, false
}

func isTag(tag, expected string) bool {
Expand Down
Loading

0 comments on commit f7f32f5

Please sign in to comment.