5

I am having problems scheduling a manage.py celery call myapp.tasks.mytask with my user crontab, in that when cron tries to run the job, it gets this in stderr (which gets mailed to me, as /var/mail/kal)

Unknown command: 'celery'
Type 'manage.py help' for usage.

The same command works completely from a regular bash login shell, but it won't work in crontab.

I am doing this on Debian wheezy:

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 7.0 (wheezy)
Release:        7.0
Codename:       wheezy

I have read many similar questions on StackOverflow and tried many of the suggested solutions. None of them have worked for me so far. Here are the solutions I have tried so far:

First, I made sure to specify relevant environment variables in the crontab:

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

NOTE: these stay in place in all of the following solutions.

1. Using full paths to the python executable and manage.py scripts

* * * * * /home/kal/.virtualenvs/foo_dev/bin/python /home/kal/foo/manage.py celery call myapp.tasks.mytask

2. cd'ing into the project path first

* * * * * cd /home/kal/foo && /home/kal/.virtualenvs/foo_dev/bin/python ./manage.py celery call myapp.tasks.mytask

3. Wrapping everything in a bash script

Content of ~/mytask.sh:

#!/usr/bin/env bash
source /home/kal/.virtualenvs/foo_dev/bin/activate;
cd /home/kal/foo;
./manage.py celery call myapp.tasks.mytask;

The crontab line:

* * * * * ~/mytask.sh

I even modified myproj/settings.py to output sys.path and sys.executable to stderr and compared the output between cron and the login shell, and they are exactly the same:

Output from cron job:

sys.executable:
    /home/kal/.virtualenvs/foo_dev/bin/python

Content of sys.path:
    /home/kal/foo
    /home/kal/.virtualenvs/foo_dev/src/bootstrap
    /home/kal/.virtualenvs/foo_dev/src/django-json-rpc
    /home/kal/.virtualenvs/foo_dev/lib/python2.7
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/plat-linux2
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/lib-tk
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/lib-old
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/lib-dynload
    /usr/lib/python2.7
    /usr/lib/python2.7/plat-linux2
    /usr/lib/python2.7/lib-tk
    /home/kal/.virtualenvs/foo_dev/local/lib/python2.7/site-packages
    /home/kal/foo

Output from Bash login shell:

sys.executable:
    /home/kal/.virtualenvs/foo_dev/bin/python

Content of sys.path:
    /home/kal/foo
    /home/kal/.virtualenvs/foo_dev/src/bootstrap
    /home/kal/.virtualenvs/foo_dev/src/django-json-rpc
    /home/kal/.virtualenvs/foo_dev/lib/python2.7
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/plat-linux2
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/lib-tk
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/lib-old
    /home/kal/.virtualenvs/foo_dev/lib/python2.7/lib-dynload
    /usr/lib/python2.7
    /usr/lib/python2.7/plat-linux2
    /usr/lib/python2.7/lib-tk
    /home/kal/.virtualenvs/foo_dev/local/lib/python2.7/site-packages
    /home/kal/foo

I am completely baffled.

Kal
  • 1,707
  • 15
  • 29
  • http://stackoverflow.com/questions/3287038/cron-and-virtualenv?rq=1 – John Mee Dec 12 '13 at 04:26
  • @JohnMee I have tried that as well. Same results. – Kal Dec 12 '13 at 04:35
  • Do you have multiple settings files? Make sure you are using the correct one. – Burhan Khalid Dec 12 '13 at 04:38
  • What user is the cron process running as? and does that user have permission to do all the things the script does? – John Mee Dec 12 '13 at 05:57
  • @JohnMee Good question. Turns out there is no USER environment variable when the cron job runs. But why?? I'm already using the user crontab, not the root one. I have been doing `crontab -e`, not `sudo crontab -e`. And if I try `sudo crontab -e`, the content is clearly different. – Kal Dec 12 '13 at 06:26
  • @JohnMee So there is no `USER` environment variable, but there is `LOGNAME`, which correctly identifies as me (`kal`). – Kal Dec 12 '13 at 06:51

4 Answers4

3

I found the cause of the problem.

It is very very subtle.

The problem is two fold:

  1. There is no USER environment variable in a cron job; only LOGNAME;
  2. When manage.py is run with a management command specified, Django quietly fails over to blank settings if an exception is raised during the import of the settings module.

My settings module was trying to reference os.environ['USER'], which doesn't exist in cron's environment. So importing the settings module causes an exception to be raised, and Django quietly fails over to blank settings, which means blank INSTALLED_APPS and no celery command!

Kal
  • 1,707
  • 15
  • 29
2

Forget cron. Use the celerybeat_scheduler.

John Mee
  • 50,179
  • 34
  • 152
  • 186
  • I have actually tried using celerybeat, too. Using the official init.d script from the github repo, but I get the same problem (`Unknown command: 'celery'`). – Kal Dec 12 '13 at 04:24
0

Make sure djcelery is in INSTALLED_APPS in settings.py as seen at https://pypi.python.org/pypi/django-celery

INSTALLED_APPS += ("djcelery", )
import djcelery
djcelery.setup_loader()
Matt Williamson
  • 39,165
  • 10
  • 64
  • 72
0

You can use fabric to get this up and running. Your virtualenv is not activating perhaps but with a tool like fabric you issue a command

def reset_app_local(appname):
    run("source ~/env/bin/activate && python manage.py celery call myapp.tasks.mytask ",shell="/bin/bash")

And run the following if you run it on local server.

def reset_app_local(appname):
    local("source ~/env/bin/activate && python manage.py celery call myapp.tasks.mytask ",shell="/bin/bash")
shaytac
  • 3,789
  • 9
  • 36
  • 44