83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
package automation
|
||
|
||
import (
|
||
"fmt"
|
||
"regexp"
|
||
"strconv"
|
||
)
|
||
|
||
// buildRateFilterGroups implements the "rate-filters" ssm feature: for each
|
||
// rule, extract a rate from each candidate proxy's original name via
|
||
// rule.Pattern's first capture group, keep those satisfying
|
||
// "rate rule.Operator rule.Value", and append a new proxy-group containing
|
||
// them. Proxies whose name doesn't match Pattern at all are excluded.
|
||
// Returns one error per rule with an invalid pattern/operator; other rules
|
||
// still run.
|
||
func buildRateFilterGroups(finalConfig map[string]interface{}, rules []RateFilterRule, proxies []ProxyRef) []error {
|
||
var errs []error
|
||
|
||
for _, rule := range rules {
|
||
re, err := regexp.Compile(rule.Pattern)
|
||
if err != nil {
|
||
errs = append(errs, fmt.Errorf("rate-filter '%s': invalid pattern %q: %w", rule.Name, rule.Pattern, err))
|
||
continue
|
||
}
|
||
|
||
cmp, err := comparator(rule.Operator)
|
||
if err != nil {
|
||
errs = append(errs, fmt.Errorf("rate-filter '%s': %w", rule.Name, err))
|
||
continue
|
||
}
|
||
|
||
candidates := matchProxies(proxies, rule.Match)
|
||
names := make([]interface{}, 0, len(candidates))
|
||
for _, p := range candidates {
|
||
m := re.FindStringSubmatch(p.OriginalName)
|
||
if len(m) < 2 {
|
||
continue
|
||
}
|
||
rate, err := strconv.ParseFloat(m[1], 64)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
if cmp(rate, rule.Value) {
|
||
names = append(names, p.DisplayName)
|
||
}
|
||
}
|
||
if len(names) == 0 {
|
||
fmt.Printf("ℹ️ ssm: rate-filter '%s' matched 0 proxies\n", rule.Name)
|
||
}
|
||
|
||
group := map[string]interface{}{
|
||
"name": rule.Name,
|
||
"type": defaultString(rule.Type, "select"),
|
||
"proxies": names,
|
||
}
|
||
for k, v := range rule.Extra {
|
||
group[k] = v
|
||
}
|
||
appendProxyGroup(finalConfig, group)
|
||
}
|
||
|
||
return errs
|
||
}
|
||
|
||
func comparator(op string) (func(a, b float64) bool, error) {
|
||
switch op {
|
||
case "<":
|
||
return func(a, b float64) bool { return a < b }, nil
|
||
case "<=":
|
||
return func(a, b float64) bool { return a <= b }, nil
|
||
case ">":
|
||
return func(a, b float64) bool { return a > b }, nil
|
||
case ">=":
|
||
return func(a, b float64) bool { return a >= b }, nil
|
||
case "==", "=":
|
||
return func(a, b float64) bool { return a == b }, nil
|
||
case "!=":
|
||
return func(a, b float64) bool { return a != b }, nil
|
||
default:
|
||
return nil, fmt.Errorf("unknown operator %q (expected <, <=, >, >=, ==, !=)", op)
|
||
}
|
||
}
|