Add parse alerting for rules files (#16601)

Builds over https://github.com/prometheus/prometheus/pull/16462
Addresses comments, adds invalid rules file

Signed-off-by: subhramit <subhramit.bb@live.in>
Co-authored-by: marcodebba <marcodebonis74@gmail.com>
pull/16637/head
Subhramit Basu 1 month ago committed by GitHub
parent 2834a665ed
commit 44e27a876e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 11
      cmd/prometheus/main.go
  2. 6
      rules/fixtures/invalid_rules.yaml
  3. 30
      rules/manager.go
  4. 12
      rules/manager_test.go

@ -648,6 +648,17 @@ func main() {
logger.Error(fmt.Sprintf("Error loading dynamic scrape config files from config (--config.file=%q)", cfg.configFile), "file", absPath, "err", err)
os.Exit(2)
}
// Parse rule files to verify they exist and contain valid rules.
if err := rules.ParseFiles(cfgFile.RuleFiles); err != nil {
absPath, pathErr := filepath.Abs(cfg.configFile)
if pathErr != nil {
absPath = cfg.configFile
}
logger.Error(fmt.Sprintf("Error loading rule file patterns from config (--config.file=%q)", cfg.configFile), "file", absPath, "err", err)
os.Exit(2)
}
if cfg.tsdb.EnableExemplarStorage {
if cfgFile.StorageConfig.ExemplarsConfig == nil {
cfgFile.StorageConfig.ExemplarsConfig = &config.DefaultExemplarsConfig

@ -0,0 +1,6 @@
groups:
- name: invalid
rules:
- record: job:http_requests:rate5m
expr: sum by (job)(rate(http_requests_total[5m]))
unexpected_field: this_field_should_not_be_here

@ -19,6 +19,7 @@ import (
"fmt"
"log/slog"
"net/url"
"path/filepath"
"slices"
"strings"
"sync"
@ -579,3 +580,32 @@ func FromMaps(maps ...map[string]string) labels.Labels {
return labels.FromMap(mLables)
}
// ParseFiles parses the rule files corresponding to glob patterns.
func ParseFiles(patterns []string) error {
files := map[string]string{}
for _, pat := range patterns {
fns, err := filepath.Glob(pat)
if err != nil {
return fmt.Errorf("failed retrieving rule files for %q: %w", pat, err)
}
for _, fn := range fns {
absPath, err := filepath.Abs(fn)
if err != nil {
absPath = fn
}
cleanPath, err := filepath.EvalSymlinks(absPath)
if err != nil {
return fmt.Errorf("failed evaluating rule file path %q (pattern: %q): %w", absPath, pat, err)
}
files[cleanPath] = pat
}
}
for fn, pat := range files {
_, errs := rulefmt.ParseFile(fn, false)
if len(errs) > 0 {
return fmt.Errorf("parse rules from file %q (pattern: %q): %w", fn, pat, errors.Join(errs...))
}
}
return nil
}

@ -20,6 +20,7 @@ import (
"math"
"os"
"path"
"path/filepath"
"slices"
"sort"
"strconv"
@ -2549,6 +2550,17 @@ func TestLabels_FromMaps(t *testing.T) {
require.Equal(t, expected, mLabels, "unexpected labelset")
}
func TestParseFiles(t *testing.T) {
t.Run("good files", func(t *testing.T) {
err := ParseFiles([]string{filepath.Join("fixtures", "rules.y*ml")})
require.NoError(t, err)
})
t.Run("bad files", func(t *testing.T) {
err := ParseFiles([]string{filepath.Join("fixtures", "invalid_rules.y*ml")})
require.ErrorContains(t, err, "field unexpected_field not found in type rulefmt.Rule")
})
}
func TestRuleDependencyController_AnalyseRules(t *testing.T) {
type expectedDependencies struct {
noDependentRules bool

Loading…
Cancel
Save