Skip to content

Commit

Permalink
Merge pull request #1210 from cloudflare/thanos
Browse files Browse the repository at this point in the history
Add support for parser schemas
  • Loading branch information
prymitive authored Dec 3, 2024
2 parents b114822 + 60017bf commit 8715ab5
Show file tree
Hide file tree
Showing 33 changed files with 538 additions and 104 deletions.
3 changes: 3 additions & 0 deletions cmd/pint/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/cloudflare/pint/internal/discovery"
"github.com/cloudflare/pint/internal/git"
"github.com/cloudflare/pint/internal/log"
"github.com/cloudflare/pint/internal/parser"
)

func BenchmarkFindEntries(b *testing.B) {
Expand All @@ -24,6 +25,7 @@ func BenchmarkFindEntries(b *testing.B) {
finder := discovery.NewGlobFinder(
[]string{"bench/rules"},
git.NewPathFilter(nil, nil, nil),
parser.PrometheusSchema,
)
for n := 0; n < b.N; n++ {
_, _ = finder.Find()
Expand All @@ -36,6 +38,7 @@ func BenchmarkCheckRules(b *testing.B) {
finder := discovery.NewGlobFinder(
[]string{"bench/rules"},
git.NewPathFilter(nil, nil, nil),
parser.PrometheusSchema,
)
entries, err := finder.Find()
if err != nil {
Expand Down
13 changes: 11 additions & 2 deletions cmd/pint/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/cloudflare/pint/internal/config"
"github.com/cloudflare/pint/internal/discovery"
"github.com/cloudflare/pint/internal/git"
"github.com/cloudflare/pint/internal/parser"
"github.com/cloudflare/pint/internal/reporter"

"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -100,13 +101,14 @@ func actionCI(c *cli.Context) error {
config.MustCompileRegexes(meta.cfg.Parser.Exclude...),
config.MustCompileRegexes(meta.cfg.Parser.Relaxed...),
)
schema := parseSchema(meta.cfg.Parser.Schema)

entries, err = discovery.NewGlobFinder([]string{"*"}, filter).Find()
entries, err = discovery.NewGlobFinder([]string{"*"}, filter, schema).Find()
if err != nil {
return err
}

entries, err = discovery.NewGitBranchFinder(git.RunGit, filter, baseBranch, meta.cfg.CI.MaxCommits).Find(entries)
entries, err = discovery.NewGitBranchFinder(git.RunGit, filter, baseBranch, meta.cfg.CI.MaxCommits, schema).Find(entries)
if err != nil {
return err
}
Expand Down Expand Up @@ -374,3 +376,10 @@ func detectGithubActions(gh *config.GitHub) *config.GitHub {
}
return gh
}

func parseSchema(s string) parser.Schema {
if s == config.SchemaThanos {
return parser.ThanosSchema
}
return parser.PrometheusSchema
}
13 changes: 8 additions & 5 deletions cmd/pint/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,14 @@ func actionLint(c *cli.Context) error {
}

slog.Info("Finding all rules to check", slog.Any("paths", paths))
finder := discovery.NewGlobFinder(paths, git.NewPathFilter(
config.MustCompileRegexes(meta.cfg.Parser.Include...),
config.MustCompileRegexes(meta.cfg.Parser.Exclude...),
config.MustCompileRegexes(meta.cfg.Parser.Relaxed...),
))
finder := discovery.NewGlobFinder(
paths,
git.NewPathFilter(
config.MustCompileRegexes(meta.cfg.Parser.Include...),
config.MustCompileRegexes(meta.cfg.Parser.Exclude...),
config.MustCompileRegexes(meta.cfg.Parser.Relaxed...),
),
parseSchema(meta.cfg.Parser.Schema))
entries, err := finder.Find()
if err != nil {
return err
Expand Down
27 changes: 27 additions & 0 deletions cmd/pint/tests/0203_parser_schema_prom_err.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
! exec pint --no-color lint rules
! stdout .
cmp stderr stderr.txt

-- stderr.txt --
level=INFO msg="Loading configuration file" path=.pint.hcl
level=INFO msg="Finding all rules to check" paths=["rules"]
level=WARN msg="Failed to parse file content" err="error at line 7: partial_response_strategy is only valid when parser is configured to use the Thanos rule schema" path=rules/1.yml lines=1-9
rules/1.yml:7 Fatal: partial_response_strategy is only valid when parser is configured to use the Thanos rule schema (yaml/parse)
7 | partial_response_strategy: warn

level=INFO msg="Problems found" Fatal=1
level=ERROR msg="Fatal error" err="found 1 problem(s) with severity Bug or higher"
-- rules/1.yml --
groups:
- name: foo
rules:
- alert: foo
expr: up == 0
- record: bar
partial_response_strategy: warn
expr: sum(up)

-- .pint.hcl --
parser {
schema = "prometheus"
}
26 changes: 26 additions & 0 deletions cmd/pint/tests/0204_parser_schema_thanos_err.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
! exec pint --no-color lint rules
! stdout .
cmp stderr stderr.txt

-- stderr.txt --
level=INFO msg="Loading configuration file" path=.pint.hcl
level=INFO msg="Finding all rules to check" paths=["rules"]
rules/1.yml:7 Fatal: This rule is not a valid Prometheus rule: `invalid partial_response_strategy value: bob`. (yaml/parse)
7 | partial_response_strategy: bob

level=INFO msg="Problems found" Fatal=1
level=ERROR msg="Fatal error" err="found 1 problem(s) with severity Bug or higher"
-- rules/1.yml --
groups:
- name: foo
rules:
- alert: foo
expr: up == 0
- record: bar
partial_response_strategy: bob
expr: sum(up)

-- .pint.hcl --
parser {
schema = "thanos"
}
22 changes: 22 additions & 0 deletions cmd/pint/tests/0205_parser_schema_thanos_ok.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
exec pint --no-color lint rules
! stdout .
cmp stderr stderr.txt

-- stderr.txt --
level=INFO msg="Loading configuration file" path=.pint.hcl
level=INFO msg="Finding all rules to check" paths=["rules"]
-- rules/1.yml --
groups:
- name: foo
rules:
- alert: foo
partial_response_strategy: warn
expr: up == 0
- record: bar
partial_response_strategy: abort
expr: sum(up)

-- .pint.hcl --
parser {
schema = "thanos"
}
21 changes: 21 additions & 0 deletions cmd/pint/tests/0206_parser_schema_err.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
! exec pint --no-color lint rules
! stdout .
cmp stderr stderr.txt

-- stderr.txt --
level=INFO msg="Loading configuration file" path=.pint.hcl
level=ERROR msg="Fatal error" err="failed to load config file \".pint.hcl\": unsupported parser scheme: bogus"
-- rules/1.yml --
groups:
- name: foo
rules:
- alert: foo
expr: up == 0
- record: bar
partial_response_strategy: bob
expr: sum(up)

-- .pint.hcl --
parser {
schema = "bogus"
}
25 changes: 16 additions & 9 deletions cmd/pint/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/cloudflare/pint/internal/config"
"github.com/cloudflare/pint/internal/discovery"
"github.com/cloudflare/pint/internal/git"
"github.com/cloudflare/pint/internal/parser"
"github.com/cloudflare/pint/internal/promapi"
"github.com/cloudflare/pint/internal/reporter"

Expand Down Expand Up @@ -202,10 +203,12 @@ func actionWatch(c *cli.Context, meta actionMeta, f pathFinderFunc) error {
return err
}

schema := parseSchema(meta.cfg.Parser.Schema)

// start timer to run every $interval
ack := make(chan bool, 1)
mainCtx, mainCancel := context.WithCancel(context.WithValue(context.Background(), config.CommandKey, config.WatchCommand))
stop := startTimer(mainCtx, meta.workers, meta.isOffline, gen, interval, ack, collector)
stop := startTimer(mainCtx, meta.workers, meta.isOffline, gen, schema, interval, ack, collector)

quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
Expand All @@ -229,7 +232,7 @@ func actionWatch(c *cli.Context, meta actionMeta, f pathFinderFunc) error {
return nil
}

func startTimer(ctx context.Context, workers int, isOffline bool, gen *config.PrometheusGenerator, interval time.Duration, ack chan bool, collector *problemCollector) chan bool {
func startTimer(ctx context.Context, workers int, isOffline bool, gen *config.PrometheusGenerator, schema parser.Schema, interval time.Duration, ack chan bool, collector *problemCollector) chan bool {
ticker := time.NewTicker(time.Second)
stop := make(chan bool, 1)
wasBootstrapped := false
Expand All @@ -243,7 +246,7 @@ func startTimer(ctx context.Context, workers int, isOffline bool, gen *config.Pr
ticker.Reset(interval)
wasBootstrapped = true
}
if err := collector.scan(ctx, workers, isOffline, gen); err != nil {
if err := collector.scan(ctx, workers, isOffline, gen, schema); err != nil {
slog.Error("Got an error when running checks", slog.Any("err", err))
}
checkIterationsTotal.Inc()
Expand Down Expand Up @@ -301,18 +304,22 @@ func newProblemCollector(cfg config.Config, f pathFinderFunc, minSeverity checks
}
}

func (c *problemCollector) scan(ctx context.Context, workers int, isOffline bool, gen *config.PrometheusGenerator) error {
func (c *problemCollector) scan(ctx context.Context, workers int, isOffline bool, gen *config.PrometheusGenerator, schema parser.Schema) error {
paths, err := c.finder(ctx)
if err != nil {
return fmt.Errorf("failed to get the list of paths to check: %w", err)
}

slog.Info("Finding all rules to check", slog.Any("paths", paths))
entries, err := discovery.NewGlobFinder(paths, git.NewPathFilter(
config.MustCompileRegexes(c.cfg.Parser.Include...),
config.MustCompileRegexes(c.cfg.Parser.Exclude...),
config.MustCompileRegexes(c.cfg.Parser.Relaxed...),
)).Find()
entries, err := discovery.NewGlobFinder(
paths,
git.NewPathFilter(
config.MustCompileRegexes(c.cfg.Parser.Include...),
config.MustCompileRegexes(c.cfg.Parser.Exclude...),
config.MustCompileRegexes(c.cfg.Parser.Relaxed...),
),
schema,
).Find()
if err != nil {
return err
}
Expand Down
15 changes: 15 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## v0.69.0

### Added

- Added `schema` option to the `parser` configuration block for setting rule validation
schema. Default value is `prometheus` and tells pint to expect rules with the schema
expected by Prometheus itself. If you use pint to validate rules loaded into Thanos Rule
component then set `schema` to `thanos` in your pint config file:

```js
parser {
schema = "thanos"
}
```

## v0.68.0

### Added
Expand Down
9 changes: 9 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,21 @@ Syntax:

```js
parser {
schema = "prometheus|thanos"
include = [ "(.*)", ... ]
exclude = [ "(.*)", ... ]
relaxed = [ "(.*)", ... ]
}
```

- `schema` - rule file schema to use, valid values are `prometheus` and `thanos`.
Setting it to `prometheus` means that pint will assume that all rules have the schema
as defined in [alerting rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)
and [recording rules](https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/)
Prometheus docs.
Setting it to `thanos` will tell pint to use the schema as defined
in [Thanos Rule](https://thanos.io/tip/components/rule.md/) docs.
Default value is `prometheus`.
- `include` - list of file patterns to check when running checks. Only files
matching those regexp rules will be checked, other modified files will be ignored.
- `exclude` - list of file patterns to ignore when running checks.
Expand Down
5 changes: 5 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ If you run pint against a different service, like [Thanos](https://thanos.io/) s
might return problems due to API call errors, since not all Prometheus HTTP APIs are supported by it.
In that case, you might want to disable failing checks in the pint configuration file.

**IMPORTANT** `pint` is a tool we wrote to work with our **Prometheus** deployment. It's not intended to be
used with other services that offer partial compatibility with Prometheus, there are **NO PLANS**
to add support for any other services. The only reason we would add support for other systems is if
we started to use them ourselves.

## Usage

There are three modes it works in:
Expand Down
2 changes: 1 addition & 1 deletion internal/checks/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func runTests(t *testing.T, testCases []checkTest) {
}

func parseContent(content string) (entries []discovery.Entry, err error) {
p := parser.NewParser(false)
p := parser.NewParser(false, parser.PrometheusSchema)
rules, err := p.Parse([]byte(content))
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/checks/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func TestTemplatedRegexpExpand(t *testing.T) {
}

func newMustRule(content string) parser.Rule {
p := parser.NewParser(false)
p := parser.NewParser(false, parser.PrometheusSchema)
rules, err := p.Parse([]byte(content))
if err != nil {
panic(err)
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func TestSetDisabledChecks(t *testing.T) {
}

func newRule(t *testing.T, content string) parser.Rule {
p := parser.NewParser(false)
p := parser.NewParser(false, parser.PrometheusSchema)
rules, err := p.Parse([]byte(content))
if err != nil {
t.Error(err)
Expand Down
21 changes: 21 additions & 0 deletions internal/config/parser.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
package config

import (
"fmt"
"regexp"
)

const (
SchemaPrometheus = "prometheus"
SchemaThanos = "thanos"
)

type Parser struct {
Schema string `hcl:"schema,optional" json:"schema,omitempty"`
Relaxed []string `hcl:"relaxed,optional" json:"relaxed,omitempty"`
Include []string `hcl:"include,optional" json:"include,omitempty"`
Exclude []string `hcl:"exclude,optional" json:"exclude,omitempty"`
}

func (p Parser) getSchema() string {
if p.Schema == "" {
return SchemaPrometheus
}
return p.Schema
}

func (p Parser) validate() error {
switch s := p.getSchema(); s {
case SchemaPrometheus:
case SchemaThanos:
default:
return fmt.Errorf("unsupported parser scheme: %s", s)
}

for _, pattern := range p.Relaxed {
_, err := regexp.Compile(pattern)
if err != nil {
Expand Down
16 changes: 16 additions & 0 deletions internal/config/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ func TestParserSettings(t *testing.T) {
},
err: errors.New("error parsing regexp: invalid nested repetition operator: `++`"),
},
{
conf: Parser{
Schema: SchemaPrometheus,
},
},
{
conf: Parser{
Schema: SchemaThanos,
},
},
{
conf: Parser{
Schema: "xxx",
},
err: errors.New("unsupported parser scheme: xxx"),
},
}

for _, tc := range testCases {
Expand Down
Loading

0 comments on commit 8715ab5

Please sign in to comment.