Files
go-synapse-backupper/cmd/synapse-backupper/run.go
T

115 lines
2.3 KiB
Go

package main
import (
"context"
"errors"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/spf13/cobra"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/config"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/healthz"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/scheduler/cron"
"git.tswf.io/infra/go-synapse-backupper/pkg/domain"
"git.tswf.io/infra/go-synapse-backupper/pkg/domain/backup"
)
var (
newScheduler = func(
expr string,
job func(),
) (
domain.Scheduler,
error,
) {
return cron.NewCronScheduler(expr, job)
}
newHealthz = func(port int) (healthz.Server, error) {
return healthz.New(port)
}
runOnceFunc = backup.RunOnce
osExitFunc = func(code int) { os.Exit(code) }
)
var runCmd = &cobra.Command{
Use: "run",
Short: "Start the backup scheduler",
RunE: run,
}
func init() {
config.RegisterFlags(runCmd)
}
func run(cmd *cobra.Command, args []string) error {
cfg, err := config.Load(cmd)
if err != nil {
return err
}
return runWithConfig(cmd.Context(), cfg)
}
func runWithConfig(
ctx context.Context,
cfg *config.Config,
) error {
job := func() {
jobCtx := context.Background()
if err := runOnceFunc(jobCtx, cfg); err != nil {
slog.Error("backup job failed", "error", err)
}
}
scheduler, err := newScheduler(cfg.Backup.Cron, job)
if err != nil {
return err
}
healthzServer, err := newHealthz(cfg.Healthz.Port)
if err != nil {
slog.Error("failed to bind healthz server", "error", err)
osExitFunc(1)
return nil
}
go func() {
if err := healthzServer.Start(); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("healthz server error", "error", err)
}
}()
scheduler.Start()
sigCtx, stop := signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT)
defer stop()
<-sigCtx.Done()
shutdownTimeout := cfg.ShutdownTimeout
if shutdownTimeout <= 0 {
shutdownTimeout = 30 * time.Second
}
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
_ = healthzServer.Stop(shutdownCtx)
stoppedCtx := scheduler.Stop()
select {
case <-stoppedCtx.Done():
slog.Info("graceful shutdown complete")
case <-time.After(shutdownTimeout):
slog.Warn("forcing exit, backup job still running")
osExitFunc(0)
}
return nil
}