Skip to content

Commit

Permalink
Merge pull request #38 from GCS-de/master
Browse files Browse the repository at this point in the history
SIGUSR2 now triggers a graceful shutdown and reloads the crontab configuration
  • Loading branch information
krallin authored Feb 8, 2019
2 parents ed81bc7 + 62c14a9 commit 328679f
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 32 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ a container to behave:
- `SIGTERM` triggers a graceful shutdown (and so does `SIGINT`, which you can
deliver via CTRL+C when used interactively)
- Job return codes and schedules are logged to `stdout` / `stderr`
- `SIGUSR2` triggers a graceful shutdown and reloads the crontab configuration

## How does it work? ##

Expand Down Expand Up @@ -204,6 +205,18 @@ WARN[2017-07-11T12:24:32+02:00] job took too long to run: it should have started
```


## Reload crontab

Send `SIGUSR2` to Supercronic to reload the crontab:

```bash
# docker environment (Supercronic needs to be PID 1 in the container)
docker kill --signal=USR2 <container id>

# shell
kill -USR2 <pid>
```

## Testing your crontab

Use the `-test` flag to prompt Supercronic to verify your crontab, but not
Expand Down
44 changes: 44 additions & 0 deletions integration/reload.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bats

setup() {
CRONTAB_FILE="$(mktemp)"
TEST_FILE="$(mktemp)"

export TEST_FILE
}

teardown() {
rm "$TEST_FILE"
}

wait_for() {
for i in $(seq 0 5); do
if "$@" > /dev/null 2>&1; then
return 0
fi

sleep 1
done

return 1
}

grep_test_file() {
grep -- "$1" "$TEST_FILE"
}

@test "it reloads on SIGUSR2" {
echo '* * * * * * * echo a > "$TEST_FILE"' > "$CRONTAB_FILE"

"${BATS_TEST_DIRNAME}/../supercronic" "$CRONTAB_FILE" 3>&- &
PID="$!"

wait_for grep_test_file a

echo '* * * * * * * echo b > "$TEST_FILE"' > "$CRONTAB_FILE"
kill -s USR2 "$PID"
wait_for grep_test_file b

kill -s TERM "$PID"
wait
}
2 changes: 2 additions & 0 deletions integration/test.bats
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/env bats

function run_supercronic() {
local crontab="$1"
local timeout="${2:-1s}"
Expand Down
73 changes: 41 additions & 32 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,6 @@ func main() {
}

crontabFileName := flag.Args()[0]
logrus.Infof("read crontab: %s", crontabFileName)

tab, err := readCrontabAtPath(crontabFileName)

if err != nil {
logrus.Fatal(err)
return
}

if *test {
logrus.Info("crontab is valid")
os.Exit(0)
return
}

var sentryHook *logrus_sentry.SentryHook
if sentryDsn != "" {
Expand All @@ -100,31 +86,54 @@ func main() {
}
}

var wg sync.WaitGroup
exitCtx, notifyExit := context.WithCancel(context.Background())
for true {
logrus.Infof("read crontab: %s", crontabFileName)
tab, err := readCrontabAtPath(crontabFileName)

if err != nil {
logrus.Fatal(err)
break
}

if *test {
logrus.Info("crontab is valid")
os.Exit(0)
break
}

for _, job := range tab.Jobs {
cronLogger := logrus.WithFields(logrus.Fields{
"job.schedule": job.Schedule,
"job.command": job.Command,
"job.position": job.Position,
})
var wg sync.WaitGroup
exitCtx, notifyExit := context.WithCancel(context.Background())

cron.StartJob(&wg, tab.Context, job, exitCtx, cronLogger)
}
for _, job := range tab.Jobs {
cronLogger := logrus.WithFields(logrus.Fields{
"job.schedule": job.Schedule,
"job.command": job.Command,
"job.position": job.Position,
})

cron.StartJob(&wg, tab.Context, job, exitCtx, cronLogger)
}

termChan := make(chan os.Signal, 1)
signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM)
termChan := make(chan os.Signal, 1)
signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)

termSig := <-termChan
termSig := <-termChan

logrus.Infof("received %s, shutting down", termSig)
notifyExit()
if termSig == syscall.SIGUSR2 {
logrus.Infof("received %s, reloading crontab", termSig)
} else {
logrus.Infof("received %s, shutting down", termSig)
}
notifyExit()

logrus.Info("waiting for jobs to finish")
wg.Wait()
logrus.Info("waiting for jobs to finish")
wg.Wait()

logrus.Info("exiting")
if termSig != syscall.SIGUSR2 {
logrus.Info("exiting")
break
}
}
}

func readCrontabAtPath(path string) (*crontab.Crontab, error) {
Expand Down

0 comments on commit 328679f

Please sign in to comment.