Skip to content

Commit

Permalink
Add fuzz tests to CI
Browse files Browse the repository at this point in the history
  • Loading branch information
prymitive committed Mar 18, 2022
1 parent 54b27fc commit 7d1ea93
Show file tree
Hide file tree
Showing 4 changed files with 342 additions and 0 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Fuzz Go code

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18

- name: Get Date
id: get-date
run: |
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
shell: bash

- uses: actions/cache@v2
with:
path: |
~/.cache/go-build/fuzz
key: ${{ runner.os }}-fuzz-${{ steps.get-date.outputs.date }}
restore-keys: |
${{ runner.os }}-fuzz-
- name: Fuzz
run: go test -fuzz=Fuzz -fuzztime 3m ./internal/parser
284 changes: 284 additions & 0 deletions internal/parser/fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
package parser_test

import (
"testing"

"github.com/cloudflare/pint/internal/parser"
)

func FuzzParse(f *testing.F) {
testcases := []string{
`# head comment
- record: foo # record comment
expr: foo offset 10m # expr comment
# pre-labels comment
labels:
# pre-foo comment
foo: bar
# post-foo comment
bob: alice
# foot comment
`,
`
- alert: foo
annotations: {}
expr: bar
annotations: {}
`,
`- record: name
expr: sum(foo)
labels:
foo: bar
bob: alice
`,
`
groups:
- name: custom_rules
rules:
- record: name
expr: sum(foo)
labels:
foo: bar
bob: alice
`,
`- alert: Down
expr: |
up == 0
for: |+
11m
labels:
severity: critical
annotations:
uri: https://docs.example.com/down.html
- record: >
foo
expr: |-
bar
/
baz > 1
labels: {}
`,
`- alert: Foo
expr:
(
xxx
-
yyy
) * bar > 0
and on(instance, device) baz
for: 30m
`,
`
# pint ignore/begin
{%- set foo = 1 %}
{% set bar = 2 -%}
{# comment #}
{#
comment
#}
# pint ignore/end
- record: colo_job:up:count
expr: sum(foo) without(job)
- record: invalid
expr: sum(foo) by ())
# pint ignore/begin
- record: colo_job:down:count
expr: up == {{ foo }}
# pint ignore/end
- record: colo:multiline
expr: |
sum(
multiline
) without(job, instance)
- record: colo:multiline:sum
expr: |
sum(sum) without(job)
+
sum(sum) without(job)
- record: colo:multiline2
expr: >-
sum(
multiline2
) without(job, instance)
- record: colo_job:up:byinstance
expr: sum(byinstance) by(instance)
- record: instance_mode:node_cpu:rate4m
expr: sum(rate(node_cpu_seconds_total[4m])) without (cpu)
- record: instance_mode:node_cpu:rate4m
expr: sum(rate(node_cpu_seconds_total[5m])) without (cpu)
- record: instance_mode:node_cpu:rate5min
expr: sum(irate(node_cpu_seconds_total[5m])) without (cpu)
- alert: Instance Is Down
expr: up == 0
`,
`
- record: colo_job:down:count
expr: up{job=~"foo"} == 0
- record: colo_job:down:count
expr: up{job!~"foo"} == 0
`,
`
- record: colo_job:fl_cf_html_bytes_in:rate10m
expr: sum(rate(fl_cf_html_bytes_in[10m])) WITHOUT (colo_id, instance, node_type, region, node_status, job, colo_name)
- record: colo_job:foo:rate1m
expr: sum(rate(foo[1m])) WITHOUT (instance)
- record: colo_job:foo:irate3m
expr: sum(irate(foo[3m])) WITHOUT (colo_id)
`,
`
xxx:
xxx:
xxx:
- xx
- yyy
`,
`
- record: "colo:test1"
expr: topk(6, sum(rate(edgeworker_subrequest_errorCount{cordon="free"}[5m])) BY (zoneId,job))
- record: "colo:test2"
expr: topk(6, sum(rate(edgeworker_subrequest_errorCount{cordon="free"}[10m])) without (instance))
`,
`- alert: Always
expr: up
- alert: AlwaysIgnored
expr: up # pint disable alerts/comparison
labels:
severity: warning
annotations:
url: "https://wiki.example.com/page/ServiceIsDown.html"
- alert: ServiceIsDown
expr: up == 0
- alert: ServiceIsDown
expr: up == 0
labels:
severity: bad
annotations:
url: bad
- alert: ServiceIsDown
expr: up == 0
labels:
severity: warning
annotations:
url: "https://wiki.example.com/page/ServiceIsDown.html"
`,
`
- alert: Foo Is Down
expr: up{job="foo"} == 0
annotations:
url: "https://wiki.example.com/page/ServiceIsDown.html"
summary: 'Instance {{ $label.instance }} down'
func: '{{ $valuexx | xxx }}'
labels:
severity: warning
summary: 'Instance {{ $label.instance }} down'
func: '{{ $value | xxx }}'
bar: 'Some {{$value}} value'
val: '{{ .Value|humanizeDuration }}'
ignore: '$value is not a variable'
`,
`groups:
- name: example
rules:
# Alert for any instance that is unreachable for >5 minutes.
- alert: InstanceDown
expr: up == 0
for: 5m
labels:
severity: page
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
# Alert for any instance that has a median request latency >1s.
- alert: APIHighRequestLatency
expr: sum by (instance) (http_inprogress_requests) > 0
for: 10m
annotations:
summary: "High request latency on {{ $labels.instance }}"
description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"
`,
`- alert: Good
expr: up == 0
for: 2m
labels:
component: foo
alert: Bad
expr: up == 0
for: 2m
labels:
component: foo
`,
`
- record: disabled
expr: sum(errors_total) by ) # pint disable promql/syntax
- record: active
expr: sum(errors_total) by )
- record: disabled
# pint disable promql/aggregate(job:true)
expr: sum(errors_total) without(job)
- record: disabled
# pint disable promql/aggregate
expr: sum(errors_total) without(job)
- record: active
expr: sum(errors_total) without(job)
- alert: disabled
expr: sum(errors_total) by ) # pint disable promql/syntax
- alert: active
expr: sum(errors_total) by )
- alert: disabled
# pint disable promql/aggregate(job:true)
expr: sum(errors_total) without(job) > 0
- alert: disabled
# pint disable promql/aggregate
expr: sum(errors_total) without(job) > 0
- alert: active
expr: sum(errors_total) without(job)
`,
`groups:
- name: "haproxy.api_server.rules"
rules:
- alert: HaproxyServerHealthcheckFailure
expr: increase(haproxy_server_check_failures_total[15m]) > 100
for: 5m
labels:
severity: 24x7
annotations:
summary: "HAProxy server healthcheck failure (instance {{ $labels.instance }})"
description: "Some server healthcheck are failing on {{ $labels.server }}\n VALUE = {{ $value }}\n LABELS: {{ $labels }}"
`,
}
for _, tc := range testcases {
f.Add(tc)
}
p := parser.NewParser()
f.Fuzz(func(t *testing.T, s string) {
t.Logf("Parsing: [%s]\n", s)
_, _ = p.Parse([]byte(s))
})
}
9 changes: 9 additions & 0 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ func (p Parser) Parse(content []byte) (rules []Rule, err error) {
return
}

defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("unable to parse YAML file: %s", r)
}
}()

var node yaml.Node
err = yaml.Unmarshal(content, &node)
if err != nil {
Expand Down Expand Up @@ -145,6 +151,9 @@ func parseRule(content []byte, node *yaml.Node) (rule Rule, isEmpty bool, err er
for {
start := exprPart.Value.Position.FirstLine() - 1
end := exprPart.Value.Position.LastLine()
if end > len(strings.Split(string(content), "\n")) {
end--
}
input := strings.Join(strings.Split(string(content), "\n")[start:end], "")
input = strings.ReplaceAll(input, " ", "")
output := strings.ReplaceAll(exprPart.Value.Value, "\n", "")
Expand Down
11 changes: 11 additions & 0 deletions internal/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ func TestParse(t *testing.T) {
output: nil,
shouldError: false,
},
{
content: []byte(string("! !00 \xf6")),
output: nil,
shouldError: true,
},
{
content: []byte(string("- 0: 0\n 00000000: 000000\n 000000:00000000000: 00000000\n 00000000000:000000: 0000000000000000000000000000000000\n 000000: 0000000\n expr: |")),
output: []parser.Rule{
{Error: parser.ParseError{Err: fmt.Errorf("incomplete rule, no alert or record key"), Line: 6}},
},
},
{
content: []byte("- record: |\n multiline\n"),
output: []parser.Rule{
Expand Down

0 comments on commit 7d1ea93

Please sign in to comment.