Peel me a Grape :: We make things work

We Make Things Work :: Blog

I want to automate editing crontabs. I often create or edit identical cron entries on different projects or machines, so automating this will save me time.

Quick overview of cron – the manual way

Each line in your crontab is a command to run and configuration for when to run it. Example: the following will clear the apache error log at one minute past midnight each day.
01 00 * * * echo "" > /www/apache/logs/error_log
A common gotcha with cron is that it doesn’t share the same environment as you do when running your shell – in particular the PATH may be different and commands available to you in your shell may be missing on cron’s default PATH. To help with this, cron allows you to add lines to set environment variables. eg.
PATH=/usr/bin:/usr/local/bin

crontab -l outputs the current users cron entries

crontab -e opens the current users crontab for edit (configure editor with EDITOR environment variable)

Add -u user to your crontab commands to avoid accidentally installing as root for example. (eg crontab -u app_user -e)

crontab - allows us to pipe content into the current users crontab, which is allows us to run commands like this, to manipulate crontab using sed.
crontab -l | sed s/PATH=.*/PATH=\\/new\\/vvvpath/ | crontab -

Introducing cronedit

cronedit is a small ruby library that allows us to manipulate crontab using ruby.

gem install cronedit
Here’s a quick taste of what you can do.
cm = CronEdit::Crontab.new 'app_user'
cm.add 'job1', "5,35 0-23/2 * * * echo 'hello world'" 
cm.add 'job2', {:minute=>5, :command=>'echo 42'}
cm.remove 'job3'
cm.commit

Add jobs, either using familiar standard crontab syntax, or using a ruby hash, perhaps easier to read. An interesting thing to note is how it adds labels to your tasks, using comments in crontab. This is used to identify jobs for removal/replacement.

##__job1__
5,35 0-23/2 * * * echo 'hello world'
One shortcoming of cronedit is that it doesn’t provide a mechanism for adding environment variables (eg. PATH). So either take the hit, and add PATH manually, or take another approach, perhaps something like:
current = %x(crontab -l -u app_user)    
%x(echo "PATH=/usr/bin:/usr/local/bin\n#{cron}" | crontab -)

or more robustly – check first whether it is there already.

def update_cron_environment(user, key,value)
  cron = %x(crontab -l -u #{user})
  pattern = Regexp.new("^#{key}.*")
  if cron =~ pattern
    cron.gsub!(pattern, "#{key}=#{value}" 
  else
   cron = "#{key}=#{value}\n#{cron}" 
  %x(echo "#{cron}" | crontab -)
end

This gives a toolbox to automate editing my crontabs. But what about….

Something Simpler

I’m deploying a rails application, it’s running as a user, used for no other purposes. This works for me.

Keep a file in source control with the app, and on deploy overwrite the current crontab, with the latest version, included with the app. With a crontab file in RAILS_ROOT/config/crontab.txt we can add a simple capistrano task to update the crontab on every deploy.

namespace :cron do
  task :update do
    %x(cat #{release_path}/config/crontab.txt | crontab -u #{user} -)
  end
end
after "deploy:symlink", "cron:update"

If you really can’t stand the crontab syntax, you could combine use this approach, in combination with crondle, a dsl for cron.

blog comments powered by Disqus