diff --git a/app/vmalert/config/config.go b/app/vmalert/config/config.go index 11f8f7eb85..1ea260e4e6 100644 --- a/app/vmalert/config/config.go +++ b/app/vmalert/config/config.go @@ -11,6 +11,7 @@ import ( "time" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/notifier" + "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/utils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/envtemplate" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "github.com/VictoriaMetrics/metricsql" @@ -193,25 +194,32 @@ func Parse(pathPatterns []string, validateAnnotations, validateExpressions bool) } fp = append(fp, matches...) } + errGroup := new(utils.ErrGroup) var groups []Group for _, file := range fp { uniqueGroups := map[string]struct{}{} gr, err := parseFile(file) if err != nil { - return nil, fmt.Errorf("failed to parse file %q: %w", file, err) + errGroup.Add(fmt.Errorf("failed to parse file %q: %w", file, err)) + continue } for _, g := range gr { if err := g.Validate(validateAnnotations, validateExpressions); err != nil { - return nil, fmt.Errorf("invalid group %q in file %q: %w", g.Name, file, err) + errGroup.Add(fmt.Errorf("invalid group %q in file %q: %w", g.Name, file, err)) + continue } if _, ok := uniqueGroups[g.Name]; ok { - return nil, fmt.Errorf("group name %q duplicate in file %q", g.Name, file) + errGroup.Add(fmt.Errorf("group name %q duplicate in file %q", g.Name, file)) + continue } uniqueGroups[g.Name] = struct{}{} g.File = file groups = append(groups, g) } } + if err := errGroup.Err(); err != nil { + return nil, err + } if len(groups) < 1 { logger.Warnf("no groups found in %s", strings.Join(pathPatterns, ";")) } diff --git a/app/vmalert/main.go b/app/vmalert/main.go index a2252b5532..40d2478272 100644 --- a/app/vmalert/main.go +++ b/app/vmalert/main.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/notifier" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/remoteread" @@ -47,6 +48,8 @@ eg. 'explore?orgId=1&left=[\"now-1h\",\"now\",\"VictoriaMetrics\",{\"expr\": \"{ remoteReadLookBack = flag.Duration("remoteRead.lookback", time.Hour, "Lookback defines how far to look into past for alerts timeseries."+ " For example, if lookback=1h then range from now() to now()-1h will be scanned.") + + dryRun = flag.Bool("dryRun", false, "Whether to check only config files without running vmalert. The rules file are validated. The `-rule` flag must be specified.") ) func main() { @@ -58,6 +61,18 @@ func main() { logger.Init() cgroup.UpdateGOMAXPROCSToCPUQuota() + if *dryRun { + u, _ := url.Parse("https://victoriametrics.com/") + notifier.InitTemplateFunc(u) + groups, err := config.Parse(*rulePath, true, true) + if err != nil { + logger.Fatalf(err.Error()) + } + if len(groups) == 0 { + logger.Fatalf("No rules for validation. Please specify path to file(s) with alerting and/or recording rules using `-rule` flag") + } + return + } ctx, cancel := context.WithCancel(context.Background()) manager, err := newManager(ctx) if err != nil {