User Crontab install buildout recipe
Project description
======================
z3c.recipe.usercrontab
======================
The problem
===========
When deploying applications, it can be useful to have maintenance
tasks be started periodically. On Unix platforms this is usually done
using ``cron`` which starts `cronjobs`. Adding cronjobs to the
system-wide cron directory (for example by placing a file in
``/etc/cron.d``) can be handled using the ``zc.recipe.deployment``
package, but it does not support adding cronjobs by normal
users. (as ``/etc/cron.d`` usually is world-writable).
The solution
============
``z3c.recipe.usercrontab`` interfaces with cron using ``crontab(1)``,
and allows normal users to install their own cronjobs. This is done by
having buildout add and remove cronjobs when installing and
uninstalling packages.
How to use it
=============
To use ``z3c.recipe.usercrontab`` you need to add the following to
your buildout.cfg::
[mycronjob]
recipe = z3c.recipe.usercrontab
times = 0 12 * * *
command = echo nothing happens at noon
and finally add ``mycronjob`` to the ``parts`` line(s) of your
buildout.cfg
Detailed documentation
======================
# Copyright (c) 2009 Zope Foundation and contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
The recipe z3c.recipe.usercrontab is a small recipe to facilitate the
installing of cronjobs into user crontabs.
>>> from z3c.recipe.usercrontab.usercrontab import UserCrontabManager
>>> c = UserCrontabManager()
For these tests, we fake a crontab by filling the list of cron entries
for this object::
>>> c.crontab = [ 'MAILTO=""', '@reboot echo "No-one will see this"']
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
Now, we're adding a method to it using the official way::
>>> c.add_entry('@reboot echo "example.com gets spammed!"',
... MAILTO="example@example.com")
The object also has a convenient __repr__, so we can test its output::
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=example@example.com
@reboot echo "example.com gets spammed!"
Adding another entry with yet another MAILTO line is placed at the end::
>>> c.add_entry('@reboot echo "example.com gets spammed twice!"',
... MAILTO="twice@example.com")
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=example@example.com
@reboot echo "example.com gets spammed!"
MAILTO=twice@example.com
@reboot echo "example.com gets spammed twice!"
When another entry is made with the same MAILTO, the MAILTO clause is
not repeated again::
>>> c.add_entry('@reboot echo "twice@example.com gets spammed twice!"',
... MAILTO="twice@example.com")
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=example@example.com
@reboot echo "example.com gets spammed!"
MAILTO=twice@example.com
@reboot echo "twice@example.com gets spammed twice!"
@reboot echo "example.com gets spammed twice!"
Removing entries also works, and removes superfluous environment variables::
>>> c.del_entry('@reboot echo "example.com gets spammed!"') == 1
True
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=twice@example.com
@reboot echo "twice@example.com gets spammed twice!"
@reboot echo "example.com gets spammed twice!"
Removing entries does not remove too much::
>>> c.del_entry('@reboot echo "twice@example.com gets spammed twice!"') == 1
True
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=twice@example.com
@reboot echo "example.com gets spammed twice!"
Removing the last entry also removes the dangling MAILTO line::
>>> c.del_entry('@reboot echo "example.com gets spammed twice!"') == 1
True
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
Removing the final entry removes the remaining MAILTO line, leaving us
with an empty list::
>>> c.del_entry('@reboot echo "No-one will see this"') == 1
True
>>> len(c.crontab)
0
Adding an entry without a MAILTO environment line also doesn't put in
an empty one::
>>> c.add_entry('@reboot echo "Someone will see this"')
>>> print c
@reboot echo "Someone will see this"
Adding an entry with an empty MAILTO line adds it at the end, so the
first entry is not disturbed::
>>> c.add_entry('@reboot echo "No-one will see this"', MAILTO="")
>>> print c
@reboot echo "Someone will see this"
MAILTO=""
@reboot echo "No-one will see this"
Next, test the read_crontab and write_crontab methods; we'll use
``cat`` and a temporary file to not modifiy the crontab of the user
running these tests::
>>> import tempfile
>>> t = tempfile.NamedTemporaryFile('w')
>>> crontestfile = t.name
>>> t.write("#dummy\n")
>>> c = UserCrontabManager(readcrontab="cat %s" % crontestfile,
... writecrontab="cat >%s" % crontestfile)
>>> c.read_crontab()
>>> a = repr(c)
>>> c.add_entry('# improbable entry')
>>> c.write_crontab()
>>> c.read_crontab()
>>> b =repr(c)
>>> a == b
False
Now, delete this entry again and make sure the old crontab is restored::
>>> c.del_entry('# improbable entry') == 1
True
>>> c.write_crontab()
>>> c.read_crontab()
>>> b = repr(c)
>>> a == b
True
Do the buildout shuffle::
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = foo
...
... [foo]
... recipe = z3c.recipe.usercrontab
... times = # @reboot
... command = echo nothing happens
... readcrontab = cat %(crontest)s
... writecrontab = cat >%(crontest)s
...
... [bar]
... recipe = z3c.recipe.usercrontab
... times = # @reboot
... command = echo nothing happens
... readcrontab = cat %(crontest)s
... writecrontab = cat >%(crontest)s
... ''' % ( { 'crontest': crontestfile } ))
>>> import os
>>> print system(os.path.join('bin', 'buildout'))
Installing foo.
<BLANKLINE>
Check that it really was added to the crontab::
>>> c.read_crontab()
>>> b = repr(c)
>>> a == b
False
>>> '# @reboot\techo nothing happens' in c.crontab
True
>>> 'WARNING=The entries below were generated by buildout, do not modify' in
c.crontab
True
Uninstall the recipe::
>>> print system(os.path.join('bin', 'buildout')+' buildout:parts=')
Uninstalling foo.
Running uninstall recipe.
<BLANKLINE>
And check that its entry was removed (i.e., the contents of the
crontab are the same as when this test was started; in any case, the
teardown from the testrunner makes sure the old situation is
restored)::
>>> c.read_crontab()
>>> b = repr(c)
>>> a == b
True
Now, break it by adding the same crontab entry twice::
>>> print system(os.path.join('bin', 'buildout')+' "buildout:parts=foo bar"')
Installing foo.
Installing bar.
<BLANKLINE>
>>> print system(os.path.join('bin', 'buildout')+' buildout:parts=') #
doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
Uninstalling bar.
Running uninstall recipe.
bar: FATAL ERROR: Found more than one matching crontab-entry during uninstall;
please resolve manually.
Matched lines: # @reboot echo nothing happens
While:
Installing.
Uninstalling bar.
<BLANKLINE>
An internal error occured due to a bug in either zc.buildout or in a
recipe being used:
Traceback (most recent call last):
...
RuntimeError: Found more than one matching crontab-entry during uninstall
<BLANKLINE>
Manually fix it by removing the offending lines::
>>> c.read_crontab()
>>> c.del_entry("# @reboot\techo nothing happens")
2
>>> c.write_crontab()
And now we can uninstall again (albeit with some warnings)::
>>> print system(os.path.join('bin', 'buildout')+' buildout:parts=') # doctest:
Uninstalling bar.
Running uninstall recipe.
bar: WARNING: Did not find a crontab-entry during uninstall; please check
manually if everything was removed correctly
Uninstalling foo.
Running uninstall recipe.
foo: WARNING: Did not find a crontab-entry during uninstall; please check
manually if everything was removed correctly
<BLANKLINE>
z3c.recipe.usercrontab
======================
The problem
===========
When deploying applications, it can be useful to have maintenance
tasks be started periodically. On Unix platforms this is usually done
using ``cron`` which starts `cronjobs`. Adding cronjobs to the
system-wide cron directory (for example by placing a file in
``/etc/cron.d``) can be handled using the ``zc.recipe.deployment``
package, but it does not support adding cronjobs by normal
users. (as ``/etc/cron.d`` usually is world-writable).
The solution
============
``z3c.recipe.usercrontab`` interfaces with cron using ``crontab(1)``,
and allows normal users to install their own cronjobs. This is done by
having buildout add and remove cronjobs when installing and
uninstalling packages.
How to use it
=============
To use ``z3c.recipe.usercrontab`` you need to add the following to
your buildout.cfg::
[mycronjob]
recipe = z3c.recipe.usercrontab
times = 0 12 * * *
command = echo nothing happens at noon
and finally add ``mycronjob`` to the ``parts`` line(s) of your
buildout.cfg
Detailed documentation
======================
# Copyright (c) 2009 Zope Foundation and contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
The recipe z3c.recipe.usercrontab is a small recipe to facilitate the
installing of cronjobs into user crontabs.
>>> from z3c.recipe.usercrontab.usercrontab import UserCrontabManager
>>> c = UserCrontabManager()
For these tests, we fake a crontab by filling the list of cron entries
for this object::
>>> c.crontab = [ 'MAILTO=""', '@reboot echo "No-one will see this"']
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
Now, we're adding a method to it using the official way::
>>> c.add_entry('@reboot echo "example.com gets spammed!"',
... MAILTO="example@example.com")
The object also has a convenient __repr__, so we can test its output::
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=example@example.com
@reboot echo "example.com gets spammed!"
Adding another entry with yet another MAILTO line is placed at the end::
>>> c.add_entry('@reboot echo "example.com gets spammed twice!"',
... MAILTO="twice@example.com")
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=example@example.com
@reboot echo "example.com gets spammed!"
MAILTO=twice@example.com
@reboot echo "example.com gets spammed twice!"
When another entry is made with the same MAILTO, the MAILTO clause is
not repeated again::
>>> c.add_entry('@reboot echo "twice@example.com gets spammed twice!"',
... MAILTO="twice@example.com")
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=example@example.com
@reboot echo "example.com gets spammed!"
MAILTO=twice@example.com
@reboot echo "twice@example.com gets spammed twice!"
@reboot echo "example.com gets spammed twice!"
Removing entries also works, and removes superfluous environment variables::
>>> c.del_entry('@reboot echo "example.com gets spammed!"') == 1
True
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=twice@example.com
@reboot echo "twice@example.com gets spammed twice!"
@reboot echo "example.com gets spammed twice!"
Removing entries does not remove too much::
>>> c.del_entry('@reboot echo "twice@example.com gets spammed twice!"') == 1
True
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
MAILTO=twice@example.com
@reboot echo "example.com gets spammed twice!"
Removing the last entry also removes the dangling MAILTO line::
>>> c.del_entry('@reboot echo "example.com gets spammed twice!"') == 1
True
>>> print c
MAILTO=""
@reboot echo "No-one will see this"
Removing the final entry removes the remaining MAILTO line, leaving us
with an empty list::
>>> c.del_entry('@reboot echo "No-one will see this"') == 1
True
>>> len(c.crontab)
0
Adding an entry without a MAILTO environment line also doesn't put in
an empty one::
>>> c.add_entry('@reboot echo "Someone will see this"')
>>> print c
@reboot echo "Someone will see this"
Adding an entry with an empty MAILTO line adds it at the end, so the
first entry is not disturbed::
>>> c.add_entry('@reboot echo "No-one will see this"', MAILTO="")
>>> print c
@reboot echo "Someone will see this"
MAILTO=""
@reboot echo "No-one will see this"
Next, test the read_crontab and write_crontab methods; we'll use
``cat`` and a temporary file to not modifiy the crontab of the user
running these tests::
>>> import tempfile
>>> t = tempfile.NamedTemporaryFile('w')
>>> crontestfile = t.name
>>> t.write("#dummy\n")
>>> c = UserCrontabManager(readcrontab="cat %s" % crontestfile,
... writecrontab="cat >%s" % crontestfile)
>>> c.read_crontab()
>>> a = repr(c)
>>> c.add_entry('# improbable entry')
>>> c.write_crontab()
>>> c.read_crontab()
>>> b =repr(c)
>>> a == b
False
Now, delete this entry again and make sure the old crontab is restored::
>>> c.del_entry('# improbable entry') == 1
True
>>> c.write_crontab()
>>> c.read_crontab()
>>> b = repr(c)
>>> a == b
True
Do the buildout shuffle::
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = foo
...
... [foo]
... recipe = z3c.recipe.usercrontab
... times = # @reboot
... command = echo nothing happens
... readcrontab = cat %(crontest)s
... writecrontab = cat >%(crontest)s
...
... [bar]
... recipe = z3c.recipe.usercrontab
... times = # @reboot
... command = echo nothing happens
... readcrontab = cat %(crontest)s
... writecrontab = cat >%(crontest)s
... ''' % ( { 'crontest': crontestfile } ))
>>> import os
>>> print system(os.path.join('bin', 'buildout'))
Installing foo.
<BLANKLINE>
Check that it really was added to the crontab::
>>> c.read_crontab()
>>> b = repr(c)
>>> a == b
False
>>> '# @reboot\techo nothing happens' in c.crontab
True
>>> 'WARNING=The entries below were generated by buildout, do not modify' in
c.crontab
True
Uninstall the recipe::
>>> print system(os.path.join('bin', 'buildout')+' buildout:parts=')
Uninstalling foo.
Running uninstall recipe.
<BLANKLINE>
And check that its entry was removed (i.e., the contents of the
crontab are the same as when this test was started; in any case, the
teardown from the testrunner makes sure the old situation is
restored)::
>>> c.read_crontab()
>>> b = repr(c)
>>> a == b
True
Now, break it by adding the same crontab entry twice::
>>> print system(os.path.join('bin', 'buildout')+' "buildout:parts=foo bar"')
Installing foo.
Installing bar.
<BLANKLINE>
>>> print system(os.path.join('bin', 'buildout')+' buildout:parts=') #
doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
Uninstalling bar.
Running uninstall recipe.
bar: FATAL ERROR: Found more than one matching crontab-entry during uninstall;
please resolve manually.
Matched lines: # @reboot echo nothing happens
While:
Installing.
Uninstalling bar.
<BLANKLINE>
An internal error occured due to a bug in either zc.buildout or in a
recipe being used:
Traceback (most recent call last):
...
RuntimeError: Found more than one matching crontab-entry during uninstall
<BLANKLINE>
Manually fix it by removing the offending lines::
>>> c.read_crontab()
>>> c.del_entry("# @reboot\techo nothing happens")
2
>>> c.write_crontab()
And now we can uninstall again (albeit with some warnings)::
>>> print system(os.path.join('bin', 'buildout')+' buildout:parts=') # doctest:
Uninstalling bar.
Running uninstall recipe.
bar: WARNING: Did not find a crontab-entry during uninstall; please check
manually if everything was removed correctly
Uninstalling foo.
Running uninstall recipe.
foo: WARNING: Did not find a crontab-entry during uninstall; please check
manually if everything was removed correctly
<BLANKLINE>
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Close
Hashes for z3c.recipe.usercrontab-0.3.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | f6f13fc546a3de9e012361cf86cc10ec55fdf81d161a319bfb195e65a0ce4f26 |
|
MD5 | c81a04a6741aa90aa759979f5e74a8f3 |
|
BLAKE2b-256 | d097d92248351859c0ad01662f07888f489a9ccc4db4e8b8213d0ed0d1b0f2c2 |