feat: Include and exclude databases
- Add filters to include and exclude strings - Use filters to include and exclude sessions (user and databases supported) - Add tests to filters and terminator Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
parent
29dbbc5bef
commit
8709ee542b
9 changed files with 553 additions and 53 deletions
|
@ -17,31 +17,41 @@ var AppName string
|
|||
|
||||
// Config receives configuration options
|
||||
type Config struct {
|
||||
mutex sync.Mutex
|
||||
File string
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Interval float64 `yaml:"interval"`
|
||||
ConnectTimeout int `yaml:"connect-timeout"`
|
||||
IdleTimeout float64 `yaml:"idle-timeout"`
|
||||
ActiveTimeout float64 `yaml:"active-timeout"`
|
||||
LogDestination string `yaml:"log-destination"`
|
||||
LogFile string `yaml:"log-file"`
|
||||
LogFormat string `yaml:"log-format"`
|
||||
PidFile string `yaml:"pid-file"`
|
||||
SyslogIdent string `yaml:"syslog-ident"`
|
||||
SyslogFacility string `yaml:"syslog-facility"`
|
||||
IncludeUsers StringFlags `yaml:"include-users"`
|
||||
IncludeUsersRegex string `yaml:"include-users-regex"`
|
||||
IncludeUsersRegexCompiled *regexp.Regexp
|
||||
ExcludeUsers StringFlags `yaml:"exclude-users"`
|
||||
ExcludeUsersRegex string `yaml:"exclude-users-regex"`
|
||||
ExcludeUsersRegexCompiled *regexp.Regexp
|
||||
ExcludeListeners bool `yaml:"exclude-listeners"`
|
||||
Cancel bool `yaml:"cancel"`
|
||||
mutex sync.Mutex
|
||||
File string
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Interval float64 `yaml:"interval"`
|
||||
ConnectTimeout int `yaml:"connect-timeout"`
|
||||
IdleTimeout float64 `yaml:"idle-timeout"`
|
||||
ActiveTimeout float64 `yaml:"active-timeout"`
|
||||
LogDestination string `yaml:"log-destination"`
|
||||
LogFile string `yaml:"log-file"`
|
||||
LogFormat string `yaml:"log-format"`
|
||||
PidFile string `yaml:"pid-file"`
|
||||
SyslogIdent string `yaml:"syslog-ident"`
|
||||
SyslogFacility string `yaml:"syslog-facility"`
|
||||
IncludeUsers StringFlags `yaml:"include-users"`
|
||||
IncludeUsersRegex string `yaml:"include-users-regex"`
|
||||
IncludeUsersRegexCompiled *regexp.Regexp
|
||||
IncludeUsersFilters []Filter
|
||||
ExcludeUsers StringFlags `yaml:"exclude-users"`
|
||||
ExcludeUsersRegex string `yaml:"exclude-users-regex"`
|
||||
ExcludeUsersRegexCompiled *regexp.Regexp
|
||||
ExcludeUsersFilters []Filter
|
||||
IncludeDatabases StringFlags `yaml:"include-databases"`
|
||||
IncludeDatabasesRegex string `yaml:"include-databases-regex"`
|
||||
IncludeDatabasesRegexCompiled *regexp.Regexp
|
||||
IncludeDatabasesFilters []Filter
|
||||
ExcludeDatabases StringFlags `yaml:"exclude-databases"`
|
||||
ExcludeDatabasesRegex string `yaml:"exclude-databases-regex"`
|
||||
ExcludeDatabasesRegexCompiled *regexp.Regexp
|
||||
ExcludeDatabasesFilters []Filter
|
||||
ExcludeListeners bool `yaml:"exclude-listeners"`
|
||||
Cancel bool `yaml:"cancel"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -83,6 +93,7 @@ func (c *Config) Reload() {
|
|||
}
|
||||
err := c.CompileRegexes()
|
||||
Panic(err)
|
||||
c.CompileFilters()
|
||||
}
|
||||
|
||||
// Dsn formats a connection string based on Config
|
||||
|
@ -129,6 +140,43 @@ func (c *Config) CompileRegexes() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CompileFilters creates Filter objects based on patterns and compiled regexp
|
||||
func (c *Config) CompileFilters() {
|
||||
|
||||
c.IncludeUsersFilters = nil
|
||||
if c.IncludeUsers != nil {
|
||||
c.IncludeUsersFilters = append(c.IncludeUsersFilters, NewIncludeFilter(c.IncludeUsers))
|
||||
}
|
||||
if c.IncludeUsersRegexCompiled != nil {
|
||||
c.IncludeUsersFilters = append(c.IncludeUsersFilters, NewIncludeFilterRegex(c.IncludeUsersRegexCompiled))
|
||||
}
|
||||
|
||||
c.ExcludeUsersFilters = nil
|
||||
if c.ExcludeUsers != nil {
|
||||
c.ExcludeUsersFilters = append(c.ExcludeUsersFilters, NewExcludeFilter(c.ExcludeUsers))
|
||||
}
|
||||
if c.ExcludeUsersRegexCompiled != nil {
|
||||
c.ExcludeUsersFilters = append(c.ExcludeUsersFilters, NewExcludeFilterRegex(c.ExcludeUsersRegexCompiled))
|
||||
}
|
||||
|
||||
c.IncludeDatabasesFilters = nil
|
||||
if c.IncludeDatabases != nil {
|
||||
c.IncludeDatabasesFilters = append(c.IncludeDatabasesFilters, NewIncludeFilter(c.IncludeDatabases))
|
||||
}
|
||||
if c.IncludeDatabasesRegexCompiled != nil {
|
||||
c.IncludeDatabasesFilters = append(c.IncludeDatabasesFilters, NewIncludeFilterRegex(c.IncludeDatabasesRegexCompiled))
|
||||
}
|
||||
|
||||
c.ExcludeDatabasesFilters = nil
|
||||
if c.ExcludeDatabases != nil {
|
||||
c.ExcludeDatabasesFilters = append(c.ExcludeDatabasesFilters, NewExcludeFilter(c.ExcludeDatabases))
|
||||
}
|
||||
if c.ExcludeDatabasesRegexCompiled != nil {
|
||||
c.ExcludeDatabasesFilters = append(c.ExcludeDatabasesFilters, NewExcludeFilterRegex(c.ExcludeDatabasesRegexCompiled))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// StringFlags append multiple string flags into a string slice
|
||||
type StringFlags []string
|
||||
|
||||
|
|
122
base/filter.go
Normal file
122
base/filter.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Filter interface to tell if a string should be included or not
|
||||
type Filter interface {
|
||||
Include(string) bool
|
||||
String() string
|
||||
}
|
||||
|
||||
// IncludeFilter to include a string when it's included in a list of strings
|
||||
type IncludeFilter struct {
|
||||
patterns []string
|
||||
}
|
||||
|
||||
// NewIncludeFilter to create an IncludeFilter
|
||||
func NewIncludeFilter(patterns []string) IncludeFilter {
|
||||
return IncludeFilter{
|
||||
patterns: patterns,
|
||||
}
|
||||
}
|
||||
|
||||
// Include returns true when a string is included in a list of patterns
|
||||
// Implements the Filter interface
|
||||
func (f IncludeFilter) Include(s string) bool {
|
||||
// No or empty patterns must include
|
||||
if f.patterns == nil || reflect.DeepEqual(f.patterns, []string{""}) {
|
||||
return true
|
||||
}
|
||||
return InSlice(s, f.patterns)
|
||||
}
|
||||
|
||||
// String to pretty print an IncludeFilter
|
||||
// Implements the Filter interface
|
||||
func (f IncludeFilter) String() string {
|
||||
return fmt.Sprintf("<IncludeFilter(%s)>", f.patterns)
|
||||
}
|
||||
|
||||
// IncludeFilterRegex to include a string when it matches a regex
|
||||
type IncludeFilterRegex struct {
|
||||
regex *regexp.Regexp
|
||||
}
|
||||
|
||||
// NewIncludeFilterRegex to create an IncludeFilterRegex
|
||||
func NewIncludeFilterRegex(regex *regexp.Regexp) IncludeFilterRegex {
|
||||
return IncludeFilterRegex{
|
||||
regex: regex,
|
||||
}
|
||||
}
|
||||
|
||||
// Include returns true when the string matches the regex
|
||||
// Implements the Filter interface
|
||||
func (f IncludeFilterRegex) Include(s string) bool {
|
||||
if f.regex == nil || f.regex.MatchString(s) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String to pretty print an IncludeFilterRegex
|
||||
// Implements the Filter interface
|
||||
func (f IncludeFilterRegex) String() string {
|
||||
return fmt.Sprintf("<IncludeFilterRegex(%s)>", f.regex.String())
|
||||
}
|
||||
|
||||
// ExcludeFilter to include a string when it's not included in a list of strings
|
||||
type ExcludeFilter struct {
|
||||
patterns []string
|
||||
}
|
||||
|
||||
// NewExcludeFilter to create an ExcludeFilter
|
||||
func NewExcludeFilter(patterns []string) ExcludeFilter {
|
||||
return ExcludeFilter{
|
||||
patterns: patterns,
|
||||
}
|
||||
}
|
||||
|
||||
// Include returns true when the string is not included in the patterns
|
||||
// Implements the Filter interface
|
||||
func (f ExcludeFilter) Include(s string) bool {
|
||||
return !InSlice(s, f.patterns)
|
||||
}
|
||||
|
||||
// String to pretty print an ExcludeFilter
|
||||
// Implements the Filter interface
|
||||
func (f ExcludeFilter) String() string {
|
||||
return fmt.Sprintf("<ExcludeFilter(%s)>", f.patterns)
|
||||
}
|
||||
|
||||
// ExcludeFilterRegex to include a string when it doesnn't match a regex
|
||||
type ExcludeFilterRegex struct {
|
||||
regex *regexp.Regexp
|
||||
}
|
||||
|
||||
// NewExcludeFilterRegex to create an ExcludeFilterRegex
|
||||
func NewExcludeFilterRegex(regex *regexp.Regexp) ExcludeFilterRegex {
|
||||
return ExcludeFilterRegex{
|
||||
regex: regex,
|
||||
}
|
||||
}
|
||||
|
||||
// Include returns true when the string doesn't match the regex
|
||||
// Implements the Filter interface
|
||||
func (f ExcludeFilterRegex) Include(s string) bool {
|
||||
if f.regex == nil || f.regex.MatchString("") {
|
||||
return true
|
||||
}
|
||||
if f.regex.MatchString(s) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String to pretty print an ExcludeFilterRegex
|
||||
// Implements the Filter interface
|
||||
func (f ExcludeFilterRegex) String() string {
|
||||
return fmt.Sprintf("<ExcludeFilterRegex(%s)>", f.regex.String())
|
||||
}
|
124
base/filter_test.go
Normal file
124
base/filter_test.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIncludeFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
patterns []string
|
||||
wanted bool
|
||||
}{
|
||||
{"No filter", "test", nil, true},
|
||||
{"Empty filter", "test", []string{""}, true},
|
||||
{"Single pattern matching", "test", []string{"test"}, true},
|
||||
{"Multiple patterns matching", "test", []string{"test", "postgres"}, true},
|
||||
{"Single pattern with no match", "nomatch", []string{"test"}, false},
|
||||
{"Multiple patterns with no match", "nomatch", []string{"test", "postgres"}, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf(tc.name), func(t *testing.T) {
|
||||
f := NewIncludeFilter(tc.patterns)
|
||||
|
||||
if got := f.Include(tc.value); got != tc.wanted {
|
||||
t.Errorf("Included must be %t for patterns '%s'", tc.wanted, tc.patterns)
|
||||
} else {
|
||||
t.Logf("Included is %t for patterns '%s'", tc.wanted, tc.patterns)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncludeFilterRegex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
regex string
|
||||
wanted bool
|
||||
}{
|
||||
{"No filter", "test", "", true},
|
||||
{"String pattern matching", "test", "test", true},
|
||||
{"Regex patterns matching", "test", "^t(.*)$", true},
|
||||
{"String pattern with no match", "nomatch", "test", false},
|
||||
{"Regex patterns with no match", "nomatch", "^t(.*)$", false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf(tc.name), func(t *testing.T) {
|
||||
compiledRegex, err := regexp.Compile(tc.regex)
|
||||
if err != nil {
|
||||
t.Fatalf("Regex '%s' doesn't compile: %v", tc.regex, err)
|
||||
}
|
||||
|
||||
f := NewIncludeFilterRegex(compiledRegex)
|
||||
if got := f.Include(tc.value); got != tc.wanted {
|
||||
t.Errorf("Included must be %t for regex '%s'", tc.wanted, tc.regex)
|
||||
} else {
|
||||
t.Logf("Included is %t for regex '%s'", tc.wanted, tc.regex)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExcludeFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
patterns []string
|
||||
wanted bool
|
||||
}{
|
||||
{"No filter", "test", nil, true},
|
||||
{"Empty filter", "test", []string{""}, true},
|
||||
{"Single pattern matching", "test", []string{"test"}, false},
|
||||
{"Multiple patterns matching", "test", []string{"test", "postgres"}, false},
|
||||
{"Single pattern with no match", "nomatch", []string{"test"}, true},
|
||||
{"Multiple patterns with no match", "nomatch", []string{"test", "postgres"}, true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf(tc.name), func(t *testing.T) {
|
||||
f := NewExcludeFilter(tc.patterns)
|
||||
if got := f.Include(tc.value); got != tc.wanted {
|
||||
t.Errorf("Included must be %t for patterns '%s'", tc.wanted, tc.patterns)
|
||||
} else {
|
||||
t.Logf("Included is %t for patterns '%s'", tc.wanted, tc.patterns)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExcludeFilterRegex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
regex string
|
||||
wanted bool
|
||||
}{
|
||||
{"No filter", "test", "", true},
|
||||
{"String pattern matching", "test", "test", false},
|
||||
{"Regex patterns matching", "test", "^t(.*)$", false},
|
||||
{"String pattern with no match", "nomatch", "test", true},
|
||||
{"Regex patterns with no match", "nomatch", "^t(.*)$", true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(fmt.Sprintf(tc.name), func(t *testing.T) {
|
||||
compiledRegex, err := regexp.Compile(tc.regex)
|
||||
if err != nil {
|
||||
t.Fatalf("Regex '%s' doesn't compile: %v", tc.regex, err)
|
||||
}
|
||||
|
||||
f := NewExcludeFilterRegex(compiledRegex)
|
||||
if got := f.Include(tc.value); got != tc.wanted {
|
||||
t.Errorf("Included must be %t for regex '%s'", tc.wanted, tc.regex)
|
||||
} else {
|
||||
t.Logf("Included is %t for regex '%s'", tc.wanted, tc.regex)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue