When a website has unusual cron job needs, we use EasyCron to run the job. Many of our QA tasks require once-a-minute jobs to run on Pantheon multidev environments.
A typical example is automated messaging for our legal aid website client. When someone is filling out a legal form the site will remind them to complete it if they haven’t worked on it for a while, say 10 days. For our QA tests, we have a debug mode that changes days to minutes, and with EasyCron running, QA is possible in a reasonable time frame.
But it’s easy to forget to manually create the job on EasyCron (“This failed! I never got the email!”). So we added a step in our CircleCI deploy script that grabs environment variables and makes a cURL request to the EasyCron API. Cron is running before our QA person is at their desk.
Configuration
In the CircleCI project settings we have environment variables for our EasyCron token and Pantheon site name, and CircleCI provides the Git branch name. Our CircleCI build script has logic to create a new multidev or use an existing one when we push a commit to Bitbucket. If we are creating a new multidev, then the following bash command is executed within the Circle CI build script to add the cron job:
CURL_RESULT=$(curl -X POST https://www.easycron.com/rest/add -d "token=$EASYCRON_TOKEN" -d "cron_expression=0-59/1 5-20 * * 1,2,3,4,5" -d "cron_job_name=$CIRCLE_BRANCH $PANTHEON_SITE_NAME" -d "auth_user=user" -d "auth_pw=password" -d "group_id=12345" -d "url=https://$CIRCLE_BRANCH-$PANTHEON_SITE_NAME.pantheonsite.io/cron/Kxxxxxxxxxxx...xxxxxxxxxxA")
In this script you would use your own cron_expression, auth_user, auth_pass, group_id and url. The group_id is an EasyCron attribute for organizing jobs. We print the cURL result to check that the job has been created.
Clean Up
Now that we’re creating EasyCron jobs automatically, we would rather delete them automatically as well. To handle this, we have a PHP script that runs when we commit to master. It uses the EasyCron API to get a list of all the jobs in our group. It then deletes any job that’s currently failing and has been failing for the last 15 hours. The thinking is that Pantheon is not likely to be down for 15 hours straight, so this should mean that the multidev has been deleted, and we’re safe to remove the EasyCron job. Note that in this context failing means that EasyCron can’t connect, not that there’s some failure while running cron on the multidev.
Here’s the PHP script that we’re using. We have the code in .circleci/scripts/easycroncleanup.php and it’s called from within in our .circleci/config.yml file.
<?php
// Only run when deploying master on oursite.
if ($_ENV['TERMINUS_SITE'] != oursite' || $_ENV['CIRCLE_BRANCH'] != 'master') {
echo "On branch " . $_ENV['TERMINUS_SITE'] . '.' . $_ENV['CIRCLE_BRANCH'] . ". Not cleaning up old EasyCron jobs.\n\r";
return;
}
echo "Checking for old EasyCron jobs to delete.\n\r";
$list = call('list', ['group_id' => 12345]);
foreach ($list['cron_jobs'] as $job) {
// This means the job is currently failing and
// has 15 hours worth of failures, based on our
// cron frequency.
if ($job['number_failed_time'] > 900) {
// Some string checking to be sure we don’t delete
// a couple of important jobs.
if (stristr($job['cron_job_name'], 'live') === FALSE &&
stristr($job['cron_job_name'], 'training') === FALSE) {
call('delete', ['id' => $job['cron_job_id']]);
$name = $job['cron_job_name'];
echo "EasyCron job $name deleted. Too many failures.\n\r";
}
}
}
function call($method, $arguments = array()) {
$token = $_ENV['EASYCRON_TOKEN'];
$parameters[] = "token=" . $token;
foreach ($arguments as $name => $value) {
$parameters[] = $name . '=' . urlencode($value);
}
$url = 'https://www.easycron.com/rest/' . $method . '?' . implode('&', $parameters);
$result = file_get_contents($url);
if ($result) {
return json_decode($result, TRUE);
}
else {
return $result;
}
}
Happy cronning!