Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ issues:
- "Function 'buildListMacros' is too long"
- "Function 'fetchProcs' has too many statements"
- "Function 'ReplaceConditionals' has too many statements"
- "Function 'ParseDateKeyword' has too many statements"
- "Function 'expandUnitByType' has too many statements"
- "cognitive complexity .* of func .*.Check"
- "cognitive complexity .* of func .*conditionAdd"
- "cognitive complexity .* of func .*.matchSingle"
Expand Down
99 changes: 99 additions & 0 deletions pkg/snclient/check_files_time_keywords_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//go:build linux
// +build linux

package snclient

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func checkFilesConfigfile(t *testing.T, scriptsDir, scriptsType string) string {
t.Helper()

config := fmt.Sprintf(`
[/modules]
CheckExternalScripts = enabled
[/paths]
scripts = %s
shared-path = %%(scripts)
[/settings/external scripts/wrappings]
sh = %%SCRIPT%% %%ARGS%%
exe = %%SCRIPT%% %%ARGS%%
[/settings/external scripts]
timeout = 1111111
allow arguments = true
[/settings/external scripts/scripts]
check_files_generate_files = ./check_files_generate_files.EXTENSION "$ARG1$"
`, scriptsDir)

config = strings.ReplaceAll(config, "EXTENSION", scriptsType)

return config
}

func TestTimeKeywordFilters(t *testing.T) {
// prepare a tempdir
tempDir := t.TempDir()

testDir, _ := os.Getwd()
scriptsDir := filepath.Join(testDir, "t", "scripts")

config := checkFilesConfigfile(t, scriptsDir, "sh")
snc := StartTestAgent(t, config)

// There is a bash script on this path: pkg/snclient/t/scripts/check_files_generate_files.sh
// It generates files on a temporary path, and changes their modification date
// This script is added to the snclient config first and registered as a check command, then ran by the snclient executable itself
res := snc.RunCheck("check_files_generate_files", []string{tempDir})

// The script generates 11 files:
// one_year_from_now_on
// one_month_from_now_on
// one_week_from_now_on
// two_days_from_now_on
// tomorrow
// today
// yesterday
// two_days_ago
// one_week_ago
// one_month_ago
// one_year_ago
assert.Equalf(t, CheckExitOK, res.State, "Generating test files successful")
assert.Equalf(t, "ok - Generated 11 files for testing", string(res.BuildPluginOutput()), "output matches")

// This will be printed if the test fails.
t.Logf("Contents of test directory %s:", tempDir)
files, _ := os.ReadDir(tempDir)
for _, file := range files {
info, _ := file.Info()
t.Logf("- File: %s, ModTime: %s", file.Name(), info.ModTime().Format(time.RFC3339))
}

// Note on 2025-11-06 : Multiple filter="<condition>"s are combined with a logical OR.
// res = snc.RunCheck("check_files", []string{fmt.Sprintf("path=%s", tempDir), "filter=\"written>=today\"", "filter=\"written<tomorrow\""})
// Such a test got every file

// combine the two conditions, filters only to the single 'today' file that is written after today midnight and earlier then tomorrow midnight
res = snc.RunCheck("check_files", []string{fmt.Sprintf("path=%s", tempDir), "filter=\"written>=today && written<tomorrow\""})
assert.Equalf(t, CheckExitOK, res.State, "state OK")
assert.Containsf(t, string(res.BuildPluginOutput()), "OK - All 1 files are ok", "output matches")

// Should get these five files, it cant get today because for that written==today
// tomorrow
// two_days_from_now_on
// one_week_from_now_on
// one_month_from_now_on
// one_year_from_now_on
res = snc.RunCheck("check_files", []string{fmt.Sprintf("path=%s", tempDir), "filter=\"written>today\""})
assert.Equalf(t, CheckExitOK, res.State, "state OK")
assert.Containsf(t, string(res.BuildPluginOutput()), "OK - All 5 files are ok", "output matches")

StopTestAgent(t, snc)
}
16 changes: 8 additions & 8 deletions pkg/snclient/checkdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ func (cd *CheckData) parseArgs(args []string) (argList []Argument, err error) {

return nil, nil
case "ok":
cond, err2 := NewCondition(argValue, &cd.attributes)
cond, err2 := NewCondition(argValue, &cd.attributes, cd.timezone)
if err2 != nil {
return nil, err2
}
Expand All @@ -638,7 +638,7 @@ func (cd *CheckData) parseArgs(args []string) (argList []Argument, err error) {
}
cd.warnThreshold = warn
case "warn", "warning":
cond, err2 := NewCondition(argValue, &cd.attributes)
cond, err2 := NewCondition(argValue, &cd.attributes, cd.timezone)
if err2 != nil {
return nil, err2
}
Expand All @@ -650,7 +650,7 @@ func (cd *CheckData) parseArgs(args []string) (argList []Argument, err error) {
}
cd.critThreshold = crit
case "crit", "critical":
cond, err2 := NewCondition(argValue, &cd.attributes)
cond, err2 := NewCondition(argValue, &cd.attributes, cd.timezone)
if err2 != nil {
return nil, err2
}
Expand All @@ -664,7 +664,7 @@ func (cd *CheckData) parseArgs(args []string) (argList []Argument, err error) {
cd.filter = filter
case "filter":
applyDefaultFilter = false
cond, err2 := NewCondition(argValue, &cd.attributes)
cond, err2 := NewCondition(argValue, &cd.attributes, cd.timezone)
if err2 != nil {
return nil, err2
}
Expand Down Expand Up @@ -909,7 +909,7 @@ func (cd *CheckData) removeQuotes(str string) string {
// setFallbacks sets default filter/warn/crit thresholds unless already set.
func (cd *CheckData) setFallbacks(applyDefaultFilter bool, defaultWarning, defaultCritical string) error {
if applyDefaultFilter && cd.defaultFilter != "" {
cond, err := NewCondition(cd.defaultFilter, &cd.attributes)
cond, err := NewCondition(cd.defaultFilter, &cd.attributes, cd.timezone)
if err != nil {
return err
}
Expand Down Expand Up @@ -1580,7 +1580,7 @@ func (cd *CheckData) applyDefaultThreshold(defaultThreshold string, list Conditi
return list
}

condDef, err := NewCondition(defaultThreshold, &cd.attributes)
condDef, err := NewCondition(defaultThreshold, &cd.attributes, cd.timezone)
if err != nil {
log.Errorf("default threshold: %s", defaultThreshold)
log.Panicf("default threshold failed: %s", err.Error())
Expand All @@ -1596,7 +1596,7 @@ func (cd *CheckData) appendDefaultThreshold(keyword, condStr, defaultThreshold s
return nil, fmt.Errorf("keyword %s= cannot be used multiple times", keyword)
}

cond, err := NewCondition(condStr, &cd.attributes)
cond, err := NewCondition(condStr, &cd.attributes, cd.timezone)
if err != nil {
return nil, err
}
Expand All @@ -1607,7 +1607,7 @@ func (cd *CheckData) appendDefaultThreshold(keyword, condStr, defaultThreshold s
return list, nil
}

condDef, err := NewCondition(defaultThreshold, &cd.attributes)
condDef, err := NewCondition(defaultThreshold, &cd.attributes, cd.timezone)
if err != nil {
log.Errorf("default threshold: %s", defaultThreshold)
log.Panicf("default threshold failed: %s", err.Error())
Expand Down
3 changes: 2 additions & 1 deletion pkg/snclient/checkresult.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ func (cr *CheckResult) Finalize(timezone *time.Location, macros ...map[string]st
log.Debugf("replacing template failed: %s: %s", cr.Output, err.Error())
}
cr.Output = output
details, err := ReplaceConditionals(cr.Details, macroSet...)
// cannot get timezone of the conditionals here
details, err := ReplaceConditionals(cr.Details, nil, macroSet...)
if err != nil {
log.Debugf("replacing details template failed: %s: %s", cr.Details, err.Error())
}
Expand Down
Loading