bd6288dae0
This means the minimal valid configuration contains one stream, with one filter, with one regex and one action.
151 lines
3.6 KiB
Go
151 lines
3.6 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Conf struct {
|
|
Patterns map[string]string `yaml:"patterns"`
|
|
Streams map[string]*Stream `yaml:"streams"`
|
|
}
|
|
|
|
type Stream struct {
|
|
name string `yaml:"-"`
|
|
|
|
Cmd []string `yaml:"cmd"`
|
|
Filters map[string]*Filter `yaml:"filters"`
|
|
}
|
|
|
|
type Filter struct {
|
|
stream *Stream `yaml:"-"`
|
|
name string `yaml:"-"`
|
|
|
|
Regex []string `yaml:"regex"`
|
|
compiledRegex []regexp.Regexp `yaml:"-"`
|
|
patternName string `yaml:"-"`
|
|
patternWithBraces string `yaml:"-"`
|
|
|
|
Retry int `yaml:"retry"`
|
|
RetryPeriod string `yaml:"retry-period"`
|
|
retryDuration time.Duration `yaml:"-"`
|
|
|
|
Actions map[string]*Action `yaml:"actions"`
|
|
|
|
matches map[string][]time.Time `yaml:"-"`
|
|
}
|
|
|
|
type Action struct {
|
|
filter *Filter `yaml:"-"`
|
|
name string `yaml:"-"`
|
|
|
|
Cmd []string `yaml:"cmd"`
|
|
|
|
After string `yaml:"after"`
|
|
afterDuration time.Duration `yaml:"-"`
|
|
}
|
|
|
|
func (c *Conf) setup() {
|
|
for patternName, pattern := range c.Patterns {
|
|
c.Patterns[patternName] = fmt.Sprintf("(?P<%s>%s)", patternName, pattern)
|
|
}
|
|
if len(c.Streams) == 0 {
|
|
log.Fatalln("Bad configuration: no streams configured!")
|
|
}
|
|
for streamName := range c.Streams {
|
|
|
|
stream := c.Streams[streamName]
|
|
stream.name = streamName
|
|
|
|
if len(stream.Filters) == 0 {
|
|
log.Fatalln("Bad configuration: no filters configured in '%s'!", stream.name)
|
|
}
|
|
for filterName := range stream.Filters {
|
|
|
|
filter := stream.Filters[filterName]
|
|
filter.stream = stream
|
|
filter.name = filterName
|
|
filter.matches = make(map[string][]time.Time)
|
|
|
|
// Parse Duration
|
|
retryDuration, err := time.ParseDuration(filter.RetryPeriod)
|
|
if err != nil {
|
|
log.Fatalln("Failed to parse time in configuration file:", err)
|
|
}
|
|
filter.retryDuration = retryDuration
|
|
|
|
if len(filter.Regex) == 0 {
|
|
log.Fatalln("Bad configuration: no regexes configured in '%s.%s'!", stream.name, filter.name)
|
|
}
|
|
// Compute Regexes
|
|
// Look for Patterns inside Regexes
|
|
for _, regex := range filter.Regex {
|
|
for patternName, pattern := range c.Patterns {
|
|
if strings.Contains(regex, patternName) {
|
|
|
|
switch filter.patternName {
|
|
case "":
|
|
filter.patternName = patternName
|
|
filter.patternWithBraces = fmt.Sprintf("<%s>", patternName)
|
|
case patternName:
|
|
// no op
|
|
default:
|
|
log.Fatalf(
|
|
"ERROR Can't mix different patterns (%s, %s) in same filter (%s.%s)\n",
|
|
filter.patternName, patternName, streamName, filterName,
|
|
)
|
|
}
|
|
|
|
regex = strings.Replace(regex, fmt.Sprintf("<%s>", patternName), pattern, 1)
|
|
}
|
|
}
|
|
filter.compiledRegex = append(filter.compiledRegex, *regexp.MustCompile(regex))
|
|
}
|
|
|
|
if len(filter.Actions) == 0 {
|
|
log.Fatalln("Bad configuration: no actions configured in '%s.%s'!", stream.name, filter.name)
|
|
}
|
|
for actionName := range filter.Actions {
|
|
|
|
action := filter.Actions[actionName]
|
|
action.filter = filter
|
|
action.name = actionName
|
|
|
|
// Parse Duration
|
|
if action.After != "" {
|
|
afterDuration, err := time.ParseDuration(action.After)
|
|
if err != nil {
|
|
log.Fatalln("Failed to parse time in configuration file:", err)
|
|
}
|
|
action.afterDuration = afterDuration
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseConf(filename string) *Conf {
|
|
|
|
data, err := os.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
log.Fatalln("Failed to read configuration file:", err)
|
|
}
|
|
|
|
var conf Conf
|
|
err = yaml.Unmarshal(data, &conf)
|
|
if err != nil {
|
|
log.Fatalln("Failed to parse configuration file:", err)
|
|
}
|
|
|
|
conf.setup()
|
|
|
|
return &conf
|
|
}
|