Skip to content

Commit fc4bf4e

Browse files
committed
Add promql support for the prequel product.
1 parent 9d393e3 commit fc4bf4e

File tree

13 files changed

+370
-117
lines changed

13 files changed

+370
-117
lines changed

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ require (
66
github.com/btcsuite/btcutil v1.0.2
77
github.com/prequel-dev/prequel-logmatch v0.0.13
88
github.com/rs/zerolog v1.34.0
9-
gopkg.in/yaml.v2 v2.4.0
109
gopkg.in/yaml.v3 v3.0.1
1110
)
1211

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
6363
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
6464
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
6565
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
66-
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
67-
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
6866
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6967
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/ast/ast.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,17 @@ func (b *builderT) buildTree(parserNode *parser.NodeT, parentMachineAddress *Ast
161161
)
162162

163163
// Build children (either matcher children or nested machines)
164-
if isMatcherNode(parserNode) {
164+
if parserNode.IsMatcherNode() {
165165
if matchNode, err = b.buildMatcherChildren(parserNode, machineAddress, termIdx); err != nil {
166166
return nil, err
167167
}
168168
children = append(children, matchNode)
169+
} else if parserNode.IsPromNode() {
170+
if matchNode, err = b.buildPromQLNode(parserNode, machineAddress, termIdx); err != nil {
171+
return nil, err
172+
}
173+
children = append(children, matchNode)
174+
169175
} else {
170176
if children, err = b.buildMachineChildren(parserNode, machineAddress); err != nil {
171177
return nil, err
@@ -210,20 +216,6 @@ func newAstNode(parserNode *parser.NodeT, typ schema.NodeTypeT, scope string, pa
210216
}
211217
}
212218

213-
func isMatcherNode(node *parser.NodeT) bool {
214-
var (
215-
hasMatcher = true
216-
)
217-
218-
for _, child := range node.Children {
219-
if _, ok := child.(*parser.MatcherT); !ok {
220-
hasMatcher = false
221-
}
222-
}
223-
224-
return hasMatcher
225-
}
226-
227219
func (b *builderT) buildMatcherChildren(parserNode *parser.NodeT, machineAddress *AstNodeAddressT, termIdx *uint32) (*AstNodeT, error) {
228220

229221
var (
@@ -265,6 +257,8 @@ func (b *builderT) buildMatcherNodes(parserNode *parser.NodeT, machineAddress *A
265257
switch parserNode.Metadata.Type {
266258
case schema.NodeTypeLogSeq:
267259
case schema.NodeTypeLogSet:
260+
case schema.NodeTypePromQL:
261+
return b.buildPromQLNode(parserNode, machineAddress, termIdx)
268262
default:
269263
return nil, parserNode.WrapError(ErrInvalidNodeType)
270264
}
@@ -372,7 +366,7 @@ func (b *builderT) buildStateMachine(parserNode *parser.NodeT, parentMachineAddr
372366
Msg("Window is required for sequences")
373367
return nil, parserNode.WrapError(ErrInvalidWindow)
374368
}
375-
case schema.NodeTypeSet, schema.NodeTypeLogSet:
369+
case schema.NodeTypeSet, schema.NodeTypeLogSet, schema.NodeTypePromQL:
376370
default:
377371
log.Error().
378372
Any("address", machineAddress).

pkg/ast/ast_log.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ func newMatchTerm(field parser.FieldT) (AstFieldT, error) {
202202
}
203203

204204
return t, nil
205+
205206
}
206207

207208
func newNegateTerm(field parser.FieldT, anchors uint32) (AstFieldT, error) {

pkg/ast/ast_machine.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,31 @@ type AstSetMatcherT struct {
2424

2525
func (b *builderT) buildMachineNode(parserNode *parser.NodeT, parentMachineAddress, machineAddress *AstNodeAddressT, children []*AstNodeT) (*AstNodeT, error) {
2626
var (
27-
seqMatcher *AstSeqMatcherT
28-
setMatcher *AstSetMatcherT
29-
matchNode = newAstNode(parserNode, parserNode.Metadata.Type, schema.ScopeCluster, parentMachineAddress, machineAddress)
30-
err error
27+
matchNode = newAstNode(parserNode, parserNode.Metadata.Type, schema.ScopeCluster, parentMachineAddress, machineAddress)
3128
)
3229

3330
switch parserNode.Metadata.Type {
3431
case schema.NodeTypeSeq, schema.NodeTypeLogSeq:
3532
matchNode.Metadata.Type = schema.NodeTypeSeq
36-
if seqMatcher, err = buildSeqMatcher(parserNode, children); err != nil {
33+
if seqMatcher, err := buildSeqMatcher(parserNode, children); err != nil {
3734
return nil, err
35+
} else {
36+
matchNode.Object = seqMatcher
3837
}
39-
matchNode.Object = seqMatcher
4038
case schema.NodeTypeSet, schema.NodeTypeLogSet:
4139
matchNode.Metadata.Type = schema.NodeTypeSet
42-
if setMatcher, err = buildSetMatcher(parserNode, children); err != nil {
40+
if setMatcher, err := buildSetMatcher(parserNode, children); err != nil {
4341
return nil, err
42+
} else {
43+
matchNode.Object = setMatcher
44+
}
45+
case schema.NodeTypePromQL:
46+
matchNode.Metadata.Type = schema.NodeTypePromQL
47+
if promMatcher, err := b.buildPromQLNode(parserNode, machineAddress, nil); err != nil {
48+
return nil, err
49+
} else {
50+
matchNode.Object = promMatcher
4451
}
45-
matchNode.Object = setMatcher
4652
default:
4753
log.Error().
4854
Str("type", parserNode.Metadata.Type.String()).

pkg/ast/ast_metrics.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package ast
2+
3+
import (
4+
"time"
5+
6+
"github.com/prequel-dev/prequel-compiler/pkg/parser"
7+
"github.com/prequel-dev/prequel-compiler/pkg/schema"
8+
"github.com/rs/zerolog/log"
9+
)
10+
11+
type AstPromQL struct {
12+
Expr string
13+
For time.Duration
14+
Interval time.Duration
15+
Event *AstEventT
16+
}
17+
18+
func (b *builderT) buildPromQLNode(parserNode *parser.NodeT, machineAddress *AstNodeAddressT, termIdx *uint32) (*AstNodeT, error) {
19+
20+
// Expects one child of type ParsePromQL
21+
22+
if len(parserNode.Children) != 1 {
23+
log.Error().Int("child_count", len(parserNode.Children)).Msg("PromQL node must have exactly one child")
24+
return nil, parserNode.WrapError(ErrInvalidNodeType)
25+
}
26+
27+
promNode, ok := parserNode.Children[0].(*parser.PromQLT)
28+
29+
if !ok {
30+
log.Error().Any("promql", parserNode.Children[0]).Msg("Failed to build PromQL node")
31+
return nil, parserNode.WrapError(ErrMissingScalar)
32+
}
33+
34+
if promNode.Expr == "" {
35+
log.Error().Msg("PromQL Expr string is empty")
36+
return nil, parserNode.WrapError(ErrMissingScalar)
37+
}
38+
39+
pn := &AstPromQL{
40+
Expr: promNode.Expr,
41+
}
42+
43+
if parserNode.Metadata.Event != nil {
44+
if parserNode.Metadata.Event.Origin {
45+
b.HasOrigin = true
46+
}
47+
pn.Event = &AstEventT{
48+
Source: parserNode.Metadata.Event.Source,
49+
Origin: parserNode.Metadata.Event.Origin,
50+
}
51+
}
52+
53+
if promNode.Interval != nil {
54+
pn.Interval = *promNode.Interval
55+
}
56+
57+
if promNode.For != nil {
58+
pn.For = *promNode.For
59+
}
60+
61+
var (
62+
address = b.newAstNodeAddress(parserNode.Metadata.RuleHash, parserNode.Metadata.Type.String(), termIdx)
63+
node = newAstNode(parserNode, parserNode.Metadata.Type, schema.ScopeCluster, machineAddress, address)
64+
)
65+
66+
node.Object = pn
67+
return node, nil
68+
69+
}

pkg/ast/ast_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ func TestAstSuccess(t *testing.T) {
6969
rule: testdata.TestSuccessSimpleExtraction,
7070
expectedNodeTypes: []string{"machine_seq", "log_seq"},
7171
},
72+
"Success_PromQLMetric": {
73+
rule: testdata.TestSuccessSimplePromQL,
74+
expectedNodeTypes: []string{"machine_set", "promql", "log_set"},
75+
},
7276
}
7377

7478
for name, test := range tests {
@@ -172,7 +176,7 @@ func TestAstFail(t *testing.T) {
172176
"Fail_TermsSemanticError4": {
173177
rule: testdata.TestFailTermsSemanticError4,
174178
err: ErrInvalidEventType,
175-
line: 16,
179+
line: 14,
176180
col: 11,
177181
},
178182
"Fail_TermsSemanticError5": {

pkg/datasrc/parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"os"
55
"time"
66

7-
"gopkg.in/yaml.v2"
7+
"gopkg.in/yaml.v3"
88
)
99

1010
// version: 0.0.1

pkg/parser/parse.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type ParseTermT struct {
101101
Set *ParseSetT `yaml:"set,omitempty"`
102102
Sequence *ParseSequenceT `yaml:"sequence,omitempty"`
103103
NegateOpts *ParseNegateOptsT `yaml:",inline,omitempty"`
104+
PromQL *ParsePromQL `yaml:"promql,omitempty"`
104105
Extract []ParseExtractT `yaml:"extract,omitempty"`
105106
}
106107

@@ -118,22 +119,30 @@ type ParseExtractT struct {
118119
RegexValue string `yaml:"regex,omitempty"`
119120
}
120121

122+
type ParsePromQL struct {
123+
Expr string `yaml:"expr"`
124+
Interval string `yaml:"interval,omitempty"`
125+
For string `yaml:"for,omitempty"`
126+
Event *ParseEventT `yaml:"event,omitempty"`
127+
}
128+
121129
func (o *ParseTermT) UnmarshalYAML(unmarshal func(any) error) error {
122130
var str string
123131
if err := unmarshal(&str); err == nil {
124132
o.StrValue = str
125133
return nil
126134
}
127135
var temp struct {
128-
Field string `yaml:"field,omitempty"`
129-
StrValue string `yaml:"value,omitempty"`
130-
JqValue string `yaml:"jq,omitempty"`
131-
RegexValue string `yaml:"regex,omitempty"`
132-
Count int `yaml:"count,omitempty"`
133-
Set *ParseSetT `yaml:"set,omitempty"`
134-
Sequence *ParseSequenceT `yaml:"sequence,omitempty"`
135-
NegateOpts *ParseNegateOptsT `yaml:",inline,omitempty"`
136-
Extract []ParseExtractT `yaml:"extract,omitempty"`
136+
Field string `yaml:"field,omitempty"`
137+
StrValue string `yaml:"value,omitempty"`
138+
JqValue string `yaml:"jq,omitempty"`
139+
RegexValue string `yaml:"regex,omitempty"`
140+
Count int `yaml:"count,omitempty"`
141+
Set *ParseSetT `yaml:"set,omitempty"`
142+
Sequence *ParseSequenceT `yaml:"sequence,omitempty"`
143+
NegateOpts *ParseNegateOptsT `yaml:",inline,omitempty"`
144+
ParsePromQL *ParsePromQL `yaml:"promql,omitempty"`
145+
Extract []ParseExtractT `yaml:"extract,omitempty"`
137146
}
138147
if err := unmarshal(&temp); err != nil {
139148
return err
@@ -146,6 +155,7 @@ func (o *ParseTermT) UnmarshalYAML(unmarshal func(any) error) error {
146155
o.Set = temp.Set
147156
o.Sequence = temp.Sequence
148157
o.NegateOpts = temp.NegateOpts
158+
o.PromQL = temp.ParsePromQL
149159
o.Extract = temp.Extract
150160
return nil
151161
}

pkg/parser/parse_test.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,20 @@ func TestParseSuccess(t *testing.T) {
6161
expectedNegIndexes: []int{-1, 2, 2, -1, -1, -1, -1},
6262
},
6363
"Success_MissingRuleId": {
64-
rule: testdata.TestFailMissingRuleIdRule,
64+
rule: testdata.TestFailMissingRuleIdRule,
6565
expectedNodeTypes: []string{"log_set"},
6666
expectedNegIndexes: []int{-1},
67-
},
68-
"Success_MissingRuleHash": {
69-
rule: testdata.TestFailMissingRuleHashRule,
67+
},
68+
"Success_MissingRuleHash": {
69+
rule: testdata.TestFailMissingRuleHashRule,
7070
expectedNodeTypes: []string{"log_set"},
7171
expectedNegIndexes: []int{-1},
72-
},
72+
},
73+
"Success_PromQL": {
74+
rule: testdata.TestSuccessSimplePromQL,
75+
expectedNodeTypes: []string{"machine_set", "promql", "log_set"},
76+
expectedNegIndexes: []int{-1, -1, -1},
77+
},
7378
}
7479

7580
for name, test := range tests {

0 commit comments

Comments
 (0)