test: improve coverage across package
This commit is contained in:
125
ruleset/expr.go
125
ruleset/expr.go
@@ -26,11 +26,14 @@ import (
|
||||
|
||||
// ExprRule is the external representation of an expression rule.
|
||||
type ExprRule struct {
|
||||
Name string `yaml:"name"`
|
||||
Action string `yaml:"action"`
|
||||
Log bool `yaml:"log"`
|
||||
Modifier ModifierEntry `yaml:"modifier"`
|
||||
Expr string `yaml:"expr"`
|
||||
Name string `yaml:"name"`
|
||||
Action string `yaml:"action"`
|
||||
Log bool `yaml:"log"`
|
||||
Modifier ModifierEntry `yaml:"modifier"`
|
||||
Expr string `yaml:"expr"`
|
||||
StartTime string `yaml:"start_time"`
|
||||
StopTime string `yaml:"stop_time"`
|
||||
Weekdays []string `yaml:"weekdays"`
|
||||
}
|
||||
|
||||
type ModifierEntry struct {
|
||||
@@ -56,6 +59,10 @@ type compiledExprRule struct {
|
||||
ModInstance modifier.Instance
|
||||
Program *vm.Program
|
||||
GeoSiteConditions []string
|
||||
StartTimeSecs int // seconds since midnight, -1 if unset
|
||||
StopTimeSecs int // seconds since midnight, -1 if unset
|
||||
Weekdays []time.Weekday
|
||||
WeekdaysNegated bool
|
||||
}
|
||||
|
||||
var _ Ruleset = (*exprRuleset)(nil)
|
||||
@@ -73,10 +80,13 @@ func (r *exprRuleset) Analyzers(info StreamInfo) []analyzer.Analyzer {
|
||||
|
||||
func (r *exprRuleset) Match(info StreamInfo) MatchResult {
|
||||
env := streamInfoToExprEnv(info)
|
||||
now := time.Now()
|
||||
for _, rule := range r.Rules {
|
||||
if !matchTime(now, rule.StartTimeSecs, rule.StopTimeSecs, rule.Weekdays, rule.WeekdaysNegated) {
|
||||
continue
|
||||
}
|
||||
v, err := vm.Run(rule.Program, env)
|
||||
if err != nil {
|
||||
// Log the error and continue to the next rule.
|
||||
r.Logger.MatchError(info, rule.Name, err)
|
||||
continue
|
||||
}
|
||||
@@ -163,12 +173,34 @@ func CompileExprRules(rules []ExprRule, ans []analyzer.Analyzer, mods []modifier
|
||||
depAnMap[name] = a
|
||||
}
|
||||
}
|
||||
startSecs := -1
|
||||
if rule.StartTime != "" {
|
||||
startSecs, err = parseTimeOfDay(rule.StartTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rule %q has invalid start_time: %w", rule.Name, err)
|
||||
}
|
||||
}
|
||||
stopSecs := -1
|
||||
if rule.StopTime != "" {
|
||||
stopSecs, err = parseTimeOfDay(rule.StopTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rule %q has invalid stop_time: %w", rule.Name, err)
|
||||
}
|
||||
}
|
||||
weekdays, weekdaysNegated, err := parseWeekdays(rule.Weekdays)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rule %q has invalid weekdays: %w", rule.Name, err)
|
||||
}
|
||||
cr := compiledExprRule{
|
||||
Name: rule.Name,
|
||||
Action: action,
|
||||
Log: rule.Log,
|
||||
Program: program,
|
||||
GeoSiteConditions: extractGeoSiteConditions(rule.Expr),
|
||||
StartTimeSecs: startSecs,
|
||||
StopTimeSecs: stopSecs,
|
||||
Weekdays: weekdays,
|
||||
WeekdaysNegated: weekdaysNegated,
|
||||
}
|
||||
if action != nil && *action == ActionModify {
|
||||
mod, ok := fullModMap[rule.Modifier.Name]
|
||||
@@ -391,6 +423,87 @@ func buildFunctionMap(config *BuiltinConfig) (map[string]*Function, *geo.GeoMatc
|
||||
}, geoMatcher
|
||||
}
|
||||
|
||||
func matchTime(now time.Time, startSecs, stopSecs int, weekdays []time.Weekday, negated bool) bool {
|
||||
if startSecs >= 0 || stopSecs >= 0 {
|
||||
currentSecs := now.Hour()*3600 + now.Minute()*60 + now.Second()
|
||||
if startSecs >= 0 && stopSecs >= 0 {
|
||||
if startSecs <= stopSecs {
|
||||
if currentSecs < startSecs || currentSecs > stopSecs {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if currentSecs < startSecs && currentSecs > stopSecs {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else if startSecs >= 0 {
|
||||
if currentSecs < startSecs {
|
||||
return false
|
||||
}
|
||||
} else if currentSecs > stopSecs {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(weekdays) > 0 {
|
||||
current := now.Weekday()
|
||||
found := false
|
||||
for _, d := range weekdays {
|
||||
if current == d {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if negated == found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func parseTimeOfDay(s string) (int, error) {
|
||||
t, err := time.Parse("15:04:05", s)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("invalid time %q (expected hh:mm:ss)", s)
|
||||
}
|
||||
return t.Hour()*3600 + t.Minute()*60 + t.Second(), nil
|
||||
}
|
||||
|
||||
func parseWeekdays(days []string) ([]time.Weekday, bool, error) {
|
||||
if len(days) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
negated := false
|
||||
parsed := make([]time.Weekday, 0, len(days))
|
||||
for i, d := range days {
|
||||
d = strings.TrimSpace(d)
|
||||
if i == 0 && strings.HasPrefix(d, "!") {
|
||||
negated = true
|
||||
d = strings.TrimSpace(strings.TrimPrefix(d, "!"))
|
||||
}
|
||||
var wd time.Weekday
|
||||
switch strings.ToLower(d) {
|
||||
case "sun", "sunday":
|
||||
wd = time.Sunday
|
||||
case "mon", "monday":
|
||||
wd = time.Monday
|
||||
case "tue", "tues", "tuesday":
|
||||
wd = time.Tuesday
|
||||
case "wed", "wednesday":
|
||||
wd = time.Wednesday
|
||||
case "thu", "thur", "thurs", "thursday":
|
||||
wd = time.Thursday
|
||||
case "fri", "friday":
|
||||
wd = time.Friday
|
||||
case "sat", "saturday":
|
||||
wd = time.Saturday
|
||||
default:
|
||||
return nil, false, fmt.Errorf("invalid weekday %q", d)
|
||||
}
|
||||
parsed = append(parsed, wd)
|
||||
}
|
||||
return parsed, negated, nil
|
||||
}
|
||||
|
||||
const rulesetLogMetaKey = "_ruleset"
|
||||
|
||||
func addGeoSiteLogMetadata(info StreamInfo, gm *geo.GeoMatcher, conditions []string) StreamInfo {
|
||||
|
||||
Reference in New Issue
Block a user