diff --git a/README.md b/README.md index bc1929b1d769790232e1b893983dcf5083c0c61b..22daca1aa581aba25f0ce7172af9ad3760c9324c 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,10 @@ and this dataset source: - name: db2 ``` +## Runtime signals + +The agent will reload its configuration on SIGHUP, and it will +immediately trigger all backup jobs upon receiving SIGUSR1. # TODO diff --git a/agent.go b/agent.go index 9bed899b321cc618ed02cdfa2d71fcfa552660e1..6b9c707cc333d0eee8b315a9eeca2235c56307a8 100644 --- a/agent.go +++ b/agent.go @@ -95,3 +95,8 @@ func makeSchedule(ctx context.Context, m Manager, sourceSpecs []SourceSpec, host return sched, merr.OrNil() } + +// RunNow starts all jobs right now, regardless of their schedule. +func (a *Agent) RunNow() { + a.sched.RunNow() +} diff --git a/cmd/tabacco/agent.go b/cmd/tabacco/agent.go index bab39eaa952d1b02ec26470c6cda270b76087fea..e30e8ccf49eb6b69e1ba6289b37f95f784f833a9 100644 --- a/cmd/tabacco/agent.go +++ b/cmd/tabacco/agent.go @@ -72,12 +72,23 @@ func (c *agentCommand) Execute(ctx context.Context, f *flag.FlagSet, args ...int return subcommands.ExitFailure } + // Build the Agent, and hook SIGUSR1 so that it triggers all + // the backup jobs immediately (useful for emergencies or + // debugging purposes). agent, err := tabacco.NewAgent(ctx, configMgr, store) if err != nil { log.Printf("error: %v", err) return subcommands.ExitFailure } defer agent.Close() // nolint + usr1Ch := make(chan os.Signal, 1) + go func() { + for range usr1Ch { + log.Printf("SIGUSR1 received, starting all jobs immediately") + agent.RunNow() + } + }() + signal.Notify(usr1Ch, syscall.SIGUSR1) log.Printf("backup agent started") diff --git a/jobs/scheduler.go b/jobs/scheduler.go index ee73731050ff8f46a0cb24d6339905621bcfd264..27efdf76bf689548c69e632ae712329ed1e2270f 100644 --- a/jobs/scheduler.go +++ b/jobs/scheduler.go @@ -194,6 +194,21 @@ func (s *Scheduler) Stop() { close(s.notifyCh) } +// RunNow starts all jobs right now, regardless of their schedule. +func (s *Scheduler) RunNow() { + s.mx.Lock() + defer s.mx.Unlock() + if s.cur == nil { + return + } + + for _, entry := range s.cur.c.Entries() { + go func(entry *cron.Entry) { + entry.Job.Run() + }(entry) + } +} + // CronJobStatus represents the status of a job, either scheduled, // running, or terminated in the past. type CronJobStatus struct {