Add users filtering
This commit is contained in:
parent
a8fdfb61c0
commit
ef67682463
6 changed files with 150 additions and 19 deletions
42
README.md
42
README.md
|
@ -47,5 +47,47 @@ Print usage:
|
||||||
pgterminate -help
|
pgterminate -help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Filtering users
|
||||||
|
|
||||||
|
`pgterminate` is able to include or exclude users from being terminated.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
### List
|
||||||
|
Arguments `-include-user` or `-exclude-user` can be used multiple times for multiple users:
|
||||||
|
|
||||||
|
```
|
||||||
|
pgterminate -include-user user1 -include-user user2
|
||||||
|
```
|
||||||
|
Or in configuration file:
|
||||||
|
|
||||||
|
```
|
||||||
|
include-users:
|
||||||
|
user1
|
||||||
|
user2
|
||||||
|
```
|
||||||
|
Same applies for `-exclude-user` (argument) and `exclude-users` (file).
|
||||||
|
|
||||||
|
### Regexes
|
||||||
|
Regexes can be configured:
|
||||||
|
|
||||||
|
```
|
||||||
|
pgterminate -include-users-regex "(user1|user2)"
|
||||||
|
```
|
||||||
|
Or in configuration file:
|
||||||
|
|
||||||
|
```
|
||||||
|
include-users-regex: "(user1|user2)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Same applies for `-exclude-users-regex` (argument) and `exclude-users-regex` (file).
|
||||||
|
|
||||||
|
## Include users
|
||||||
|
|
||||||
|
When include users list or regex is set, `pgterminate` will focus on included users only. It could terminate excluded users if any. If you want to exclude users, use exclude options only.
|
||||||
|
|
||||||
|
## Exclude users
|
||||||
|
|
||||||
|
When exclude users list or regex is set and no include option is set, `pgterminate` will terminate all sessions except excluded users.
|
||||||
|
|
||||||
# License
|
# License
|
||||||
`pgterminate` is released under [The Unlicense](https://github.com/jouir/pgterminate/blob/master/LICENSE) license. Code is under public domain.
|
`pgterminate` is released under [The Unlicense](https://github.com/jouir/pgterminate/blob/master/LICENSE) license. Code is under public domain.
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -15,22 +16,28 @@ var AppName string
|
||||||
|
|
||||||
// Config receives configuration options
|
// Config receives configuration options
|
||||||
type Config struct {
|
type Config struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
File string
|
File string
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
Port int `yaml:"port"`
|
Port int `yaml:"port"`
|
||||||
User string `yaml:"user"`
|
User string `yaml:"user"`
|
||||||
Password string `yaml:"password"`
|
Password string `yaml:"password"`
|
||||||
Database string `yaml:"database"`
|
Database string `yaml:"database"`
|
||||||
Interval float64 `yaml:"interval"`
|
Interval float64 `yaml:"interval"`
|
||||||
ConnectTimeout int `yaml:"connect-timeout"`
|
ConnectTimeout int `yaml:"connect-timeout"`
|
||||||
IdleTimeout float64 `yaml:"idle-timeout"`
|
IdleTimeout float64 `yaml:"idle-timeout"`
|
||||||
ActiveTimeout float64 `yaml:"active-timeout"`
|
ActiveTimeout float64 `yaml:"active-timeout"`
|
||||||
LogDestination string `yaml:"log-destination"`
|
LogDestination string `yaml:"log-destination"`
|
||||||
LogFile string `yaml:"log-file"`
|
LogFile string `yaml:"log-file"`
|
||||||
PidFile string `yaml:"pid-file"`
|
PidFile string `yaml:"pid-file"`
|
||||||
SyslogIdent string `yaml:"syslog-ident"`
|
SyslogIdent string `yaml:"syslog-ident"`
|
||||||
SyslogFacility string `yaml:"syslog-facility"`
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -62,7 +69,7 @@ func (c *Config) Read(file string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload reads from file and update configuration
|
// Reload reads from file to update configuration and re-compile regexes
|
||||||
func (c *Config) Reload() {
|
func (c *Config) Reload() {
|
||||||
log.Debug("Reloading configuration")
|
log.Debug("Reloading configuration")
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
|
@ -70,6 +77,8 @@ func (c *Config) Reload() {
|
||||||
if c.File != "" {
|
if c.File != "" {
|
||||||
c.Read(c.File)
|
c.Read(c.File)
|
||||||
}
|
}
|
||||||
|
err := c.CompileRegexes()
|
||||||
|
Panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dsn formats a connection string based on Config
|
// Dsn formats a connection string based on Config
|
||||||
|
@ -98,3 +107,34 @@ func (c *Config) Dsn() string {
|
||||||
}
|
}
|
||||||
return strings.Join(parameters, " ")
|
return strings.Join(parameters, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompileRegexes transforms regexes from string to regexp instance
|
||||||
|
func (c *Config) CompileRegexes() (err error) {
|
||||||
|
if c.IncludeUsersRegex != "" {
|
||||||
|
c.IncludeUsersRegexCompiled, err = regexp.Compile(c.IncludeUsersRegex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.ExcludeUsersRegex != "" {
|
||||||
|
c.ExcludeUsersRegexCompiled, err = regexp.Compile(c.ExcludeUsersRegex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringFlags append multiple string flags into a string slice
|
||||||
|
type StringFlags []string
|
||||||
|
|
||||||
|
// String for implementing flag interface
|
||||||
|
func (s *StringFlags) String() string {
|
||||||
|
return "multiple strings flag"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adds alues into the slice
|
||||||
|
func (s *StringFlags) Set(value string) error {
|
||||||
|
*s = append(*s, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -10,3 +10,13 @@ func Panic(err error) {
|
||||||
log.Fatalf("%s\n", err)
|
log.Fatalf("%s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InSlice detects value presence in a string slice
|
||||||
|
func InSlice(value string, slice []string) bool {
|
||||||
|
for _, val := range slice {
|
||||||
|
if value == val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,10 @@ func main() {
|
||||||
flag.StringVar(&config.PidFile, "pid-file", "", "Write process id into a file")
|
flag.StringVar(&config.PidFile, "pid-file", "", "Write process id into a file")
|
||||||
flag.StringVar(&config.SyslogIdent, "syslog-ident", "pgterminate", "Define syslog tag")
|
flag.StringVar(&config.SyslogIdent, "syslog-ident", "pgterminate", "Define syslog tag")
|
||||||
flag.StringVar(&config.SyslogFacility, "syslog-facility", "", "Define syslog facility from LOCAL0 to LOCAL7")
|
flag.StringVar(&config.SyslogFacility, "syslog-facility", "", "Define syslog facility from LOCAL0 to LOCAL7")
|
||||||
|
flag.Var(&config.IncludeUsers, "include-user", "Terminate only this user (can be called multiple times)")
|
||||||
|
flag.StringVar(&config.IncludeUsersRegex, "include-users-regex", "", "Terminate users matching this regexp")
|
||||||
|
flag.Var(&config.ExcludeUsers, "exclude-user", "Ignore this user (can be called multiple times)")
|
||||||
|
flag.StringVar(&config.ExcludeUsersRegex, "exclude-users-regex", "", "Ignore users matching this regexp")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetLevel(log.WarnLevel)
|
log.SetLevel(log.WarnLevel)
|
||||||
|
@ -93,6 +97,9 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = config.CompileRegexes()
|
||||||
|
base.Panic(err)
|
||||||
|
|
||||||
if config.PidFile != "" {
|
if config.PidFile != "" {
|
||||||
writePid(config.PidFile)
|
writePid(config.PidFile)
|
||||||
defer removePid(config.PidFile)
|
defer removePid(config.PidFile)
|
||||||
|
|
|
@ -12,3 +12,11 @@ pid-file: /var/run/pgterminate/pgterminate.pid
|
||||||
#log-destination: console|file|syslog
|
#log-destination: console|file|syslog
|
||||||
#syslog-ident: pgterminate
|
#syslog-ident: pgterminate
|
||||||
#syslog-facility: LOCAL0
|
#syslog-facility: LOCAL0
|
||||||
|
#include-users:
|
||||||
|
# user1
|
||||||
|
# user2
|
||||||
|
#include-users-regex: "(user1|user2)"
|
||||||
|
#exclude-users:
|
||||||
|
# user1
|
||||||
|
# user2
|
||||||
|
#exclude-users-regex: "(user1|user2)"
|
||||||
|
|
|
@ -40,12 +40,12 @@ func (t *Terminator) Run() {
|
||||||
sessions := t.db.Sessions()
|
sessions := t.db.Sessions()
|
||||||
if t.config.ActiveTimeout != 0 {
|
if t.config.ActiveTimeout != 0 {
|
||||||
actives := activeSessions(sessions, t.config.ActiveTimeout)
|
actives := activeSessions(sessions, t.config.ActiveTimeout)
|
||||||
t.terminateAndNotify(actives)
|
t.terminateAndNotify(t.filter(actives))
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.config.IdleTimeout != 0 {
|
if t.config.IdleTimeout != 0 {
|
||||||
idles := idleSessions(sessions, t.config.IdleTimeout)
|
idles := idleSessions(sessions, t.config.IdleTimeout)
|
||||||
t.terminateAndNotify(idles)
|
t.terminateAndNotify(t.filter(idles))
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(t.config.Interval*1000) * time.Millisecond)
|
time.Sleep(time.Duration(t.config.Interval*1000) * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,30 @@ func (t *Terminator) terminateAndNotify(sessions []base.Session) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filter removes sessions according to include and exclude users settings
|
||||||
|
// when include users slice and regex are not set, append all sessions except excluded users
|
||||||
|
// otherwise, append included users
|
||||||
|
func (t *Terminator) filter(sessions []base.Session) (filtered []base.Session) {
|
||||||
|
includeUsers, includeRegex := t.config.IncludeUsers, t.config.IncludeUsersRegexCompiled
|
||||||
|
excludeUsers, excludeRegex := t.config.ExcludeUsers, t.config.ExcludeUsersRegexCompiled
|
||||||
|
|
||||||
|
for _, session := range sessions {
|
||||||
|
if t.config.IncludeUsers == nil && includeRegex == nil {
|
||||||
|
// append all sessions except excluded users
|
||||||
|
if !base.InSlice(session.User, excludeUsers) || (excludeRegex != nil && !excludeRegex.MatchString(session.User)) {
|
||||||
|
filtered = append(filtered, session)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// append included users only
|
||||||
|
if base.InSlice(session.User, includeUsers) || (includeRegex != nil && includeRegex.MatchString(session.User)) {
|
||||||
|
filtered = append(filtered, session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
// terminate terminates gracefully
|
// terminate terminates gracefully
|
||||||
func (t *Terminator) terminate() {
|
func (t *Terminator) terminate() {
|
||||||
log.Info("Disconnecting from instance")
|
log.Info("Disconnecting from instance")
|
||||||
|
|
Loading…
Reference in a new issue