PromTool: Support Scrape Interval Lint Checking

Signed-off-by: zhaowang <zhaowang@apac.freewheel.com>
This commit is contained in:
zhaowang 2024-08-23 12:16:05 +08:00
parent 5fd2717aec
commit fcc35ba551
3 changed files with 43 additions and 17 deletions

View file

@ -70,9 +70,13 @@ const (
lintOptionAll = "all"
lintOptionDuplicateRules = "duplicate-rules"
lintOptionLongScrapeInterval = "long-scrape-inerval"
lintOptionNone = "none"
checkHealth = "/-/healthy"
checkReadiness = "/-/ready"
// Remove when the lookback delta can be set as a command line flag on PromTool.
defaultLookbackDelta = 5 * time.Minute
)
var lintOptions = []string{lintOptionAll, lintOptionDuplicateRules, lintOptionNone}
@ -445,6 +449,7 @@ var errLint = fmt.Errorf("lint error")
type lintConfig struct {
all bool
duplicateRules bool
longScrapeInterval bool
fatal bool
}
@ -459,6 +464,8 @@ func newLintConfig(stringVal string, fatal bool) lintConfig {
ls.all = true
case lintOptionDuplicateRules:
ls.duplicateRules = true
case lintOptionLongScrapeInterval:
ls.longScrapeInterval = true
case lintOptionNone:
default:
fmt.Printf("WARNING: unknown lint option %s\n", setting)
@ -471,6 +478,10 @@ func (ls lintConfig) lintDuplicateRules() bool {
return ls.all || ls.duplicateRules
}
func (ls lintConfig) lintLongScrapeInterval() bool {
return ls.all || ls.longScrapeInterval
}
// CheckServerStatus - healthy & ready.
func CheckServerStatus(serverURL *url.URL, checkEndpoint string, roundTripper http.RoundTripper) error {
if serverURL.Scheme == "" {
@ -514,10 +525,10 @@ func CheckConfig(agentMode, checkSyntaxOnly bool, lintSettings lintConfig, files
hasErrors := false
for _, f := range files {
ruleFiles, err := checkConfig(agentMode, f, checkSyntaxOnly)
ruleFiles, err := checkConfig(agentMode, f, checkSyntaxOnly, lintSettings)
if err != nil {
fmt.Fprintln(os.Stderr, " FAILED:", err)
hasErrors = true
hasErrors = !errors.Is(err, errLint)
failed = true
} else {
if len(ruleFiles) > 0 {
@ -571,7 +582,7 @@ func checkFileExists(fn string) error {
return err
}
func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]string, error) {
func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool, lintSettings lintConfig) ([]string, error) {
fmt.Println("Checking", filename)
cfg, err := config.LoadFile(filename, agentMode, false, log.NewNopLogger())
@ -611,6 +622,13 @@ func checkConfig(agentMode bool, filename string, checkSyntaxOnly bool) ([]strin
}
for _, scfg := range scfgs {
if lintSettings.lintLongScrapeInterval() {
if scfg.ScrapeInterval >= model.Duration(defaultLookbackDelta) {
errMessage := fmt.Sprintf("Long Scrape Interval found. Data point will be marked as stale. Job: %s. Interval: %s", scfg.JobName, scfg.ScrapeInterval.String())
return nil, fmt.Errorf("%w %s", errLint, errMessage)
}
}
if !checkSyntaxOnly && scfg.HTTPClientConfig.Authorization != nil {
if err := checkFileExists(scfg.HTTPClientConfig.Authorization.CredentialsFile); err != nil {
return nil, fmt.Errorf("error checking authorization credentials or bearer token file %q: %w", scfg.HTTPClientConfig.Authorization.CredentialsFile, err)

View file

@ -219,7 +219,7 @@ func TestCheckTargetConfig(t *testing.T) {
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
_, err := checkConfig(false, "testdata/"+test.file, false)
_, err := checkConfig(false, "testdata/"+test.file, false, newLintConfig(lintOptionNone, false))
if test.err != "" {
require.Equalf(t, test.err, err.Error(), "Expected error %q, got %q", test.err, err.Error())
return
@ -302,7 +302,7 @@ func TestCheckConfigSyntax(t *testing.T) {
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
_, err := checkConfig(false, "testdata/"+test.file, test.syntaxOnly)
_, err := checkConfig(false, "testdata/"+test.file, test.syntaxOnly, newLintConfig(lintOptionNone, false))
expectedErrMsg := test.err
if strings.Contains(runtime.GOOS, "windows") {
expectedErrMsg = test.errWindows
@ -336,7 +336,7 @@ func TestAuthorizationConfig(t *testing.T) {
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
_, err := checkConfig(false, "testdata/"+test.file, false)
_, err := checkConfig(false, "testdata/"+test.file, false, newLintConfig(lintOptionNone, false))
if test.err != "" {
require.Contains(t, err.Error(), test.err, "Expected error to contain %q, got %q", test.err, err.Error())
return
@ -414,7 +414,7 @@ func TestExitCodes(t *testing.T) {
t.Run(strconv.FormatBool(lintFatal), func(t *testing.T) {
args := []string{"-test.main", "check", "config", "testdata/" + c.file}
if lintFatal {
args = append(args, "--lint-fatal")
args = append(args, "--lint-fatal", "--lint=all")
}
tool := exec.Command(promtoolPath, args...)
err := tool.Run()
@ -531,6 +531,13 @@ func TestCheckRules(t *testing.T) {
exitCode := CheckRules(newLintConfig(lintOptionDuplicateRules, true))
require.Equal(t, lintErrExitCode, exitCode, "")
})
t.Run("config-lint-fatal", func(t *testing.T) {
_, err := checkConfig(false, "./testdata/prometheus-config.lint.yml", false, newLintConfig(lintOptionLongScrapeInterval, false))
expectedErrMsg := "lint error Long Scrape Interval found. Data point will be marked as stale. Job: long_scrape_interval_test. Interval: 1h"
require.Equalf(t, expectedErrMsg, err.Error(), "Expected error %q, got %q", expectedErrMsg, err.Error())
return
})
}
func TestCheckRulesWithRuleFiles(t *testing.T) {

View file

@ -1,2 +1,3 @@
rule_files:
- prometheus-rules.lint.yml
scrape_configs:
- job_name: long_scrape_interval_test
scrape_interval: 1h