Skip to content

Commit 2e8c78a

Browse files
authored
Add email-on-fail to cron job (#1241)
* Add email-on-fail to cron job * Update changelog
1 parent 72d523b commit 2e8c78a

File tree

4 files changed

+99
-20
lines changed

4 files changed

+99
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
- Ensure merge tables are declared during file insertion #1205
88
- Update URL for DANDI Docs #1210
9-
- Improve cron job documentation and script #1226
9+
- Improve cron job documentation and script #1226, #1241
1010
- Only add merge parts to `source_class_dict` if present in codebase #1237
1111

1212
### Pipelines
@@ -18,7 +18,8 @@
1818
- Allow population of missing `PositionIntervalMap` entries during population
1919
of `DLCPoseEstimation` #1208
2020
- Spikesorting
21-
- Fix compatibility bug between v1 pipeline and `SortedSpikesGroup` unit filtering #1238
21+
- Fix compatibility bug between v1 pipeline and `SortedSpikesGroup` unit
22+
filtering #1238
2223

2324
## [0.5.4] (December 20, 2024)
2425

maintenance_scripts/README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,40 @@ regularly as cron jobs.
2626
1. Clone the repository to the desired location.
2727
2. Set up a config file by copying `dj_local_conf_example.json` to
2828
`dj_local_conf.json` and filling in the necessary information.
29-
3. Set up a cron job to run `run_jobs.sh` at the desired interval by running
29+
3. Copy the `example.env` file to `.env` in the `maintenance_scripts` directory
30+
and fill in the necessary information, including...
31+
- `SPYGLASS_CONDA_ENV`: the name of the conda environment with Spyglass and
32+
DataJoint installed.
33+
- `SPYGLASS_REPO_PATH`: the path to the Spyglass repository.
34+
- `SPYGLASS_LOG`: the path to the log file.
35+
- Optional email settings. If not set, email notifications will not be sent.
36+
- `SPYGLASS_EMAIL_SRC`: The email address from which to send notifications.
37+
- `SPYGLASS_EMAIL_PASS`: the password for the email address.
38+
- `SPYGLASS_EMAIL_DEST`: the email address to which to send notifications.
39+
4. Set up a cron job to run `run_jobs.sh` at the desired interval by running
3040
`crontab -e` and adding the script.
3141

42+
Note that the log file will automatically be truncated to `SPYGLASS_MAX_LOG`
43+
lines on each run. 1000 lines should be sufficient.
44+
45+
### Example Cron Job
46+
3247
In the following example, the script is set to run every Monday at 4:00 AM.
3348

3449
```text
3550
0 4 * * 1 /path/to/run_jobs.sh
3651
```
52+
53+
### Email Service
54+
55+
The script uses `curl` to send email notifications on failure. While this can
56+
work with
57+
[many email services](https://everything.curl.dev/usingcurl/smtp.html), Gmail is
58+
a common choice. To use Gmail, you will need to ...
59+
60+
1. Turn on [2-step verification](https://myaccount.google.com/security-checkup)
61+
2. Turn on [less secure apps](https://myaccount.google.com/lesssecureapps)
62+
3. Create an [app password](https://myaccount.google.com/apppasswords)
63+
64+
`curl` will not work with your master Gmail password, so you will need to use
65+
the app password instead.

maintenance_scripts/example.env

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# NOTE: Use quotes for strings with spaces
2+
export SPYGLASS_CONDA_ENV=spyglass # name of conda environment
3+
export SPYGLASS_REPO_PATH=/home/franklab/spyglass # path to spyglass repo
4+
export SPYGLASS_LOG=/home/franklab/spyglass.log # log file
5+
export SPYGLASS_EMAIL_SRC=[email protected] # optional, email sender
6+
export SPYGLASS_EMAIL_PASS="example password" # optional, email password
7+
export SPYGLASS_EMAIL_DEST=[email protected] # optional, email recipient
8+
export SPYGLASS_MAX_LOG=1000 # number of lines to keep in log

maintenance_scripts/run_jobs.sh

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,52 @@
11
#!/bin/bash
2+
# AUTHOR: Chris Brozdowski
3+
# DATE: 2025-02-20
4+
#
5+
# 1. Go to SPYGLASS_REPO_PATH and pull the latest changes from the master branch
6+
# 2. Test the SPYGLASS_CONDA_ENV conda environment
7+
# 3. Test the connection to the database
8+
# 4. Run the cleanup script
9+
#
10+
# This script is intended to be run as a cron job, weekly or more frequently.
11+
# It will store a log of its output in SPYGLASS_LOG.
12+
# If any of the operations fail, an email will be sent to SPYGLASS_EMAIL_DEST
213

3-
# SETUP:
4-
# 1. Create a conda environment with datajoint installed.
5-
# 2. Edit the variables below to match your setup.
6-
# 3. Set up the cron job (See README.md)
7-
# Note that the log file will be truncated to the last 1000 lines.
14+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15+
source "$SCRIPT_DIR/.env" # load environment variables from this directory
816

9-
SPYGLASS_CONDA_ENV=spyglass
10-
SPYGLASS_REPO_PATH=/home/franklab/spyglass
11-
SPYGLASS_LOG=/home/franklab/spyglass/spyglass.log
17+
if [[ -z "${SPYGLASS_CONDA_ENV}" \
18+
|| -z "${SPYGLASS_REPO_PATH}" \
19+
|| -z "${SPYGLASS_LOG}" ]]; then
20+
echo "Error: SPYGLASS_CONDA_ENV, SPYGLASS_REPO_PATH,
21+
and SPYGLASS_LOG must be set in .env"
22+
exit 1
23+
fi
24+
25+
EMAIL_TEMPLATE=$(cat <<-EOF
26+
From: "Spyglass" <$SPYGLASS_EMAIL_SRC>
27+
To: $SPYGLASS_EMAIL_DEST
28+
Subject: cron fail - $(date "+%Y-%m-%d")
29+
30+
%s
31+
EOF
32+
)
33+
34+
on_fail() { # $1: error message. Echo message and send as email
35+
echo "Error: $1"
36+
if [ -z "$SPYGLASS_EMAIL_SRC" ]; then
37+
return 1 # No email source, so don't send an email
38+
fi
39+
local error_msg="$1"
40+
local content
41+
content=$(printf "$EMAIL_TEMPLATE" "$error_msg")
42+
43+
curl -o /dev/null --ssl-reqd \
44+
--url "smtps://smtp.gmail.com:465" \
45+
--user "${SPYGLASS_EMAIL_SRC}:${SPYGLASS_EMAIL_PASS}" \
46+
--mail-from "$SPYGLASS_EMAIL_SRC" \
47+
--mail-rcpt "$SPYGLASS_EMAIL_DEST" \
48+
-T <(echo "$content")
49+
}
1250

1351
exec >> $SPYGLASS_LOG 2>&1
1452

@@ -17,30 +55,33 @@ echo "SPYGLASS CRON JOB START: $(date +"%Y-%m-%d %H:%M:%S")"
1755

1856
# Run from the root of the spyglass repository
1957
cd $SPYGLASS_REPO_PATH || \
20-
{ echo "Error: Could not change to the spyglass directory"; exit 1; }
58+
{ on_fail "Could not find repo path: $SPYGLASS_REPO_PATH"; exit 1; }
59+
2160

2261
# Update the spyglass repository
23-
git pull https://github.com/LorenFrankLab/spyglass.git master > /dev/null || \
24-
{ echo "Error: $PWD Could not update the spyglass repository"; exit 1; }
62+
git pull --quiet \
63+
https://github.com/LorenFrankLab/spyglass.git master > /dev/null || \
64+
{ on_fail "Could not update the spyglass repo $PWD"; exit 1; }
2565

2666
# Test conda environment
2767
if ! conda env list | grep -q $SPYGLASS_CONDA_ENV; then
28-
echo "Error: Conda environment $SPYGLASS_CONDA_ENV not found"
29-
exit 1
68+
on_fail "Conda environment $SPYGLASS_CONDA_ENV not found"
69+
exit 1
3070
fi
3171

3272
# convenience function to run a command in the spyglass conda environment
3373
conda_run() { conda run --name $SPYGLASS_CONDA_ENV "$@"; }
3474

3575
# Test connection to the database
36-
conda_run python -c "import datajoint as dj; dj.conn()" > /dev/null || \
37-
{ echo "Error: Could not connect to the database"; exit 1; }
76+
CONN_TEST="import datajoint as dj; dj.logger.setLevel('ERROR'); dj.conn()"
77+
conda_run python -c "$CONN_TEST" > /dev/null || \
78+
{ on_fail "Could not connect to the database"; exit 1; }
3879

3980
# Run cleanup script
4081
conda_run python maintenance_scripts/cleanup.py
4182

4283
echo "SPYGLASS CRON JOB END"
4384

4485
# truncate long log file
45-
tail -n 1000 "$SPYGLASS_LOG" > "${SPYGLASS_LOG}.tmp" && \
46-
mv "${SPYGLASS_LOG}.tmp" "$SPYGLASS_LOG"
86+
tail -n ${SPYGLASS_MAX_LOG:-1000} "$SPYGLASS_LOG" > "${SPYGLASS_LOG}.tmp" \
87+
&& mv "${SPYGLASS_LOG}.tmp" "$SPYGLASS_LOG"

0 commit comments

Comments
 (0)