2020-09-22 03:20:24 +00:00
|
|
|
package hp
|
2020-09-21 20:04:20 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-09-22 03:20:24 +00:00
|
|
|
"io"
|
2020-09-21 21:11:18 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2020-09-22 03:20:24 +00:00
|
|
|
"path/filepath"
|
2020-09-21 21:44:29 +00:00
|
|
|
"runtime"
|
|
|
|
"syscall"
|
2020-09-21 21:11:18 +00:00
|
|
|
"time"
|
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
"git.eeqj.de/sneak/goutil"
|
|
|
|
"git.eeqj.de/sneak/historyposter/store"
|
2020-09-21 20:04:20 +00:00
|
|
|
"github.com/mattn/go-isatty"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
)
|
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
// HistoryPoster is the main app framework object
|
|
|
|
type HistoryPoster struct {
|
|
|
|
version string
|
|
|
|
buildarch string
|
|
|
|
startup time.Time
|
|
|
|
appCtx context.Context
|
|
|
|
shutdownFunc context.CancelFunc
|
|
|
|
exitCode int
|
|
|
|
store *store.Store
|
|
|
|
logfh *os.File
|
|
|
|
}
|
|
|
|
|
2020-09-21 20:04:20 +00:00
|
|
|
// CLIEntry is the main entrypoint
|
2020-09-21 21:11:18 +00:00
|
|
|
func CLIEntry(version, buildarch string) int {
|
2020-09-21 20:04:20 +00:00
|
|
|
hp := new(HistoryPoster)
|
|
|
|
hp.version = version
|
|
|
|
hp.buildarch = buildarch
|
|
|
|
hp.startup = time.Now()
|
2020-09-22 03:20:24 +00:00
|
|
|
hp.exitCode = 0
|
2020-09-21 20:04:20 +00:00
|
|
|
|
2020-09-21 21:44:29 +00:00
|
|
|
c := make(chan os.Signal)
|
|
|
|
signal.Ignore(syscall.SIGPIPE)
|
|
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
2020-09-21 20:04:20 +00:00
|
|
|
|
|
|
|
hp.configure()
|
|
|
|
hp.setupLogging()
|
|
|
|
|
2020-09-22 03:30:12 +00:00
|
|
|
s, err := store.NewStore()
|
2020-09-22 03:20:24 +00:00
|
|
|
if err != nil {
|
|
|
|
hp.shutdown("cannot create state file: "+err.Error(), -1)
|
|
|
|
return hp.exitCode
|
|
|
|
}
|
2020-09-22 03:30:12 +00:00
|
|
|
hp.store = s
|
2020-09-22 03:20:24 +00:00
|
|
|
|
2020-09-21 21:44:29 +00:00
|
|
|
hp.appCtx, hp.shutdownFunc = context.WithCancel(context.Background())
|
2020-09-21 20:04:20 +00:00
|
|
|
go func() {
|
2020-09-21 21:11:18 +00:00
|
|
|
// this sits and waits for an interrupt to be received
|
2020-09-21 20:04:20 +00:00
|
|
|
sig := <-c
|
|
|
|
log.Info().Msgf("signal received: %+v", sig)
|
2020-09-21 21:44:29 +00:00
|
|
|
hp.shutdownFunc()
|
2020-09-21 20:04:20 +00:00
|
|
|
}()
|
2020-09-21 21:44:29 +00:00
|
|
|
return hp.runForever(hp.appCtx)
|
2020-09-21 20:04:20 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 21:11:18 +00:00
|
|
|
func (hp *HistoryPoster) configure() {
|
|
|
|
viper.SetConfigName("historyposter")
|
|
|
|
viper.SetConfigType("yaml")
|
|
|
|
viper.AddConfigPath("/etc/historyposter") // path to look for the config file in
|
|
|
|
viper.AddConfigPath("$HOME/.config/historyposter") // call multiple times to add many search paths
|
|
|
|
viper.SetEnvPrefix("HISTORYPOSTER")
|
|
|
|
viper.AutomaticEnv()
|
|
|
|
|
|
|
|
viper.SetDefault("Debug", false)
|
2020-09-22 03:20:24 +00:00
|
|
|
viper.SetDefault("Logfile", os.Getenv("HOME")+"/Library/Logs/historyposter/historyposter.log")
|
2020-09-21 21:11:18 +00:00
|
|
|
|
|
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
|
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
|
|
// Config file not found; ignore error if desired
|
|
|
|
} else {
|
|
|
|
// Config file was found but another error was produced
|
|
|
|
log.Panic().
|
|
|
|
Err(err).
|
2020-09-22 03:20:24 +00:00
|
|
|
Msg("config file malformed")
|
2020-09-21 21:11:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-21 20:04:20 +00:00
|
|
|
|
2020-09-22 03:30:12 +00:00
|
|
|
// if viper.GetBool("debug") {
|
2020-09-22 03:20:24 +00:00
|
|
|
// pp.Print(viper.AllSettings())
|
2020-09-22 03:30:12 +00:00
|
|
|
// }
|
2020-09-21 20:04:20 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 21:11:18 +00:00
|
|
|
func (hp *HistoryPoster) runForever(ctx context.Context) int {
|
2020-09-22 03:20:24 +00:00
|
|
|
log.Info().Msg("entering main loop")
|
|
|
|
|
|
|
|
interval := 60 * time.Second
|
|
|
|
timeout := 10 * time.Second
|
2020-09-21 21:44:29 +00:00
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
ticker := time.NewTicker(interval)
|
|
|
|
go func() {
|
|
|
|
// do it once right now, without an insta-tick
|
2020-09-22 03:30:12 +00:00
|
|
|
go func() { hp.postURLs(context.WithTimeout(ctx, timeout)) }()
|
2020-09-22 03:20:24 +00:00
|
|
|
|
|
|
|
// then go do it repeatedly:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
2020-09-22 03:30:12 +00:00
|
|
|
go func() { hp.postURLs(context.WithTimeout(ctx, timeout)) }()
|
2020-09-22 03:20:24 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
ticker.Stop()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2020-09-21 21:44:29 +00:00
|
|
|
|
2020-09-21 21:11:18 +00:00
|
|
|
<-ctx.Done()
|
2020-09-22 03:20:24 +00:00
|
|
|
hp.cleanup()
|
|
|
|
log.Info().Msgf("exiting")
|
|
|
|
hp.logfh.Close()
|
|
|
|
return hp.exitCode
|
2020-09-21 21:11:18 +00:00
|
|
|
}
|
2020-09-21 20:04:20 +00:00
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
func (hp *HistoryPoster) cleanup() {
|
|
|
|
log.Info().Msgf("begin cleanup")
|
|
|
|
hp.store.Close()
|
|
|
|
}
|
2020-09-21 21:11:18 +00:00
|
|
|
func (hp *HistoryPoster) setupLogging() {
|
2020-09-22 03:20:24 +00:00
|
|
|
// always log in UTC
|
|
|
|
zerolog.TimestampFunc = func() time.Time {
|
|
|
|
return time.Now().UTC()
|
|
|
|
}
|
2020-09-21 20:04:20 +00:00
|
|
|
log.Logger = log.With().Caller().Logger()
|
2020-09-22 03:20:24 +00:00
|
|
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
2020-09-21 20:04:20 +00:00
|
|
|
|
|
|
|
tty := isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
|
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
consoleWriter := zerolog.NewConsoleWriter(
|
|
|
|
func(w *zerolog.ConsoleWriter) {
|
|
|
|
// Customize time format
|
|
|
|
w.TimeFormat = time.RFC3339
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
var writers []io.Writer
|
2020-09-21 20:04:20 +00:00
|
|
|
if tty {
|
2020-09-22 03:20:24 +00:00
|
|
|
writers = append(writers, consoleWriter)
|
2020-09-21 20:04:20 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
logfile := viper.GetString("Logfile")
|
|
|
|
logfileDir := filepath.Dir(logfile)
|
|
|
|
err := goutil.Mkdirp(logfileDir)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("unable to create log dir")
|
2020-09-21 20:04:20 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 03:20:24 +00:00
|
|
|
hp.logfh, err = os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
|
|
|
if err != nil {
|
|
|
|
panic("unable to open logfile: " + err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
writers = append(writers, hp.logfh)
|
|
|
|
|
|
|
|
multi := zerolog.MultiLevelWriter(writers...)
|
|
|
|
logger := zerolog.New(multi).With().Timestamp().Logger().With().Caller().Logger()
|
|
|
|
|
|
|
|
log.Logger = logger
|
|
|
|
// FIXME get caller back in there zerolog.New(multi).Caller().Logger()
|
|
|
|
|
2020-09-21 20:04:20 +00:00
|
|
|
if viper.GetBool("debug") {
|
|
|
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
|
|
|
}
|
|
|
|
|
|
|
|
hp.identify()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hp *HistoryPoster) identify() {
|
|
|
|
log.Info().
|
|
|
|
Str("version", hp.version).
|
|
|
|
Str("buildarch", hp.buildarch).
|
2020-09-21 21:44:29 +00:00
|
|
|
Str("os", runtime.GOOS).
|
2020-09-21 20:04:20 +00:00
|
|
|
Msg("starting")
|
|
|
|
}
|
2020-09-22 03:20:24 +00:00
|
|
|
|
|
|
|
func (hp *HistoryPoster) shutdown(reason string, exitcode int) {
|
|
|
|
log.Info().Msgf("shutting down: %s", reason)
|
|
|
|
hp.exitCode = exitcode
|
|
|
|
hp.shutdownFunc()
|
|
|
|
}
|