Initial pgterminate code

This commit is contained in:
Julien Riou 2018-06-10 08:44:53 +02:00
parent 0487d635fc
commit 565c45a8fc
No known key found for this signature in database
GPG key ID: BA3E15176E45E85D
15 changed files with 697 additions and 0 deletions

140
cmd/pgterminate/main.go Normal file
View file

@ -0,0 +1,140 @@
package main
import (
"flag"
"fmt"
"github.com/jouir/pgterminate/base"
"github.com/jouir/pgterminate/notifier"
"github.com/jouir/pgterminate/terminator"
"golang.org/x/crypto/ssh/terminal"
"log"
"os"
"os/signal"
"strconv"
"sync"
"syscall"
)
// AppVersion stores application version at compilation time
var AppVersion string
func main() {
var err error
config := base.NewConfig()
version := flag.Bool("version", false, "Print version")
flag.StringVar(&config.File, "config", "", "Configuration file")
flag.StringVar(&config.Host, "host", "", "Instance host address")
flag.IntVar(&config.Port, "port", 0, "Instance port")
flag.StringVar(&config.User, "user", "", "Instance username")
flag.StringVar(&config.Password, "password", "", "Instance password")
flag.StringVar(&config.Database, "database", "", "Instance database")
prompt := flag.Bool("prompt-password", false, "Prompt for password")
flag.Float64Var(&config.Interval, "interval", 1, "Time to sleep between iterations in seconds")
flag.IntVar(&config.ConnectTimeout, "connect-timeout", 3, "Connection timeout in seconds")
flag.Float64Var(&config.IdleTimeout, "idle-timeout", 0, "Time for idle connections to be terminated in seconds")
flag.Float64Var(&config.ActiveTimeout, "active-timeout", 0, "Time for active connections to be terminated in seconds")
flag.StringVar(&config.LogFile, "log-file", "", "Write logs to a file")
flag.StringVar(&config.PidFile, "pid-file", "", "Write process id into a file")
flag.Parse()
if *version {
if AppVersion == "" {
AppVersion = "unknown"
}
fmt.Println(AppVersion)
return
}
if *prompt {
fmt.Print("Password:")
bytes, err := terminal.ReadPassword(syscall.Stdin)
base.Panic(err)
config.Password = string(bytes)
fmt.Print("\n")
}
if config.File != "" {
err = config.Read(config.File)
base.Panic(err)
}
if config.ActiveTimeout == 0 && config.IdleTimeout == 0 {
log.Fatalln("Parameter -active-timeout or -idle-timeout required")
}
if config.PidFile != "" {
writePid(config.PidFile)
defer removePid(config.PidFile)
}
done := make(chan bool)
sessions := make(chan base.Session)
ctx := base.NewContext(config, sessions, done)
terminator := terminator.NewTerminator(ctx)
notifier := notifier.NewNotifier(ctx)
handleSignals(ctx, notifier)
// Run managers asynchronously and wait for all of them to end
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
terminator.Run()
}()
go func() {
defer wg.Done()
notifier.Run()
}()
wg.Wait()
}
// handleSignals handles operating system signals
func handleSignals(ctx *base.Context, n notifier.Notifier) {
// When interrupt or terminated, terminate managers, close channel and terminate program
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
signal.Notify(c, syscall.SIGTERM)
go func() {
for sig := range c {
log.Printf("Received %v signal\n", sig)
close(ctx.Sessions)
ctx.Done <- true
}
}()
// When hangup, reload notifier
h := make(chan os.Signal, 1)
signal.Notify(h, syscall.SIGHUP)
go func() {
for sig := range h {
log.Printf("Received %v signal\n", sig)
ctx.Config.Reload()
n.Reload()
}
}()
}
// writePid writes current pid into a pid file
func writePid(file string) {
log.Println("Creating pid file", file)
pid := strconv.Itoa(os.Getpid())
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0644)
base.Panic(err)
defer f.Close()
_, err = f.WriteString(pid)
base.Panic(err)
}
// removePid removes pid file
func removePid(file string) {
if _, err := os.Stat(file); err == nil {
log.Println("Removing pid file", file)
err := os.Remove(file)
base.Panic(err)
}
}