Deployment

Jobber is written in Go, and it should be possible to compile and run it on any Unix/Linux system.

Installation from a Package

The easiest way to install Jobber is via a binary package.

Compilation and Installation from Source

Instead of installing a binary package, you can certainly compile and install Jobber yourself.

You need Go 1.8 or later to compile Jobber. So, install Go, pick a place for your workspace (say, /path/to/your/workspace), and ensure that the environment variable GOPATH includes a path to your workspace. Then:

cd /path/to/your/workspace
mkdir -p src/github.com/dshearer
cd src/github.com/dshearer
git clone https://github.com/dshearer/jobber.git
cd jobber
git checkout v1.3.3
make check
sudo make install DESTDIR=/

This will install the following:

  • /usr/local/bin/jobber
  • /usr/local/libexec/jobbermaster
  • /usr/local/libexec/jobberrunner

Finally, do something to ensure that jobbermaster runs as a daemon (as root) when your computer boots. Note that jobbermaster does not daemonize itself, but that should not be a problem if you use systemd; otherwise, daemonize(1) could help.

Putting Jobber to Work

As with cron, each user can have its own set of jobs, which will be run under that user’s privileges. A user’s jobs are defined in a jobfile — a file named “.jobber” in the user’s home directory.

IMPORTANT: Jobfiles must be owned by the user who owns the home directory that contains them, and their permissions must not allow the owning group or any other users to write to them (i.e., the permission bits must exclude 022).

Here's an example that defines two jobs:

[prefs]
  notifyProgram: /home/foobar/handleError.sh

[jobs]
- name: DailyBackup
  cmd: backup daily
  time: 0 0 13
  onError: Stop
  notifyOnError: true
  notifyOnFailure: false

- name: OffsiteBackup
  cmd: |
    mount /backup/offsite
    mount /backup/linux/weekly
    backup-offsite /backup/linux/weekly/backup-0 linux || exit 1
    umount /backup/linux/weekly
    umount /backup/offsite
  time: 0 0 14 * * 1
  onError: Stop

As shown, there are two sections: prefs and jobs . The contents of both sections are YAML documents.

Time Strings

Field time specifies the schedule at which a job is run, in a manner similar to how cron jobs’ schedules are specified: with a space-separated list of up to six specifiers, each one of which constrains a different component of time (second, minute, hour, etc.). Schematically:

sec min hour month_day month week_day

A job is scheduled thus: the next run time is the time that satisfies the second, minute, hour, and month specifiers and at least one of the month-day and weekday specifiers.

Important: If a specifier is one of the random specifiers (beginning with R ), a random value is chosen when the jobfile is loaded, and stays the same until the jobfile is loaded again.

Each specifier can take one of the following forms: (“a”, “b”, “c”, and “n” are placeholders for arbitrary numerals.)

Specifier Form What It Matches
* Any value
a The value a
*/n Every a-th value — e.g., */25 in the second specifier would match 0, 25, and 50, whereas in the month specifier it would match 1 and 26.
a,b,c,... The values a, b, c, ... — e.g., 10, 20, 30, 40 matches 10, 20, 30, and 40.
a-b Any of the values between and including a and b
R A random value
Ra-b A random value between and including a and b

The specifiers have different permitted values for the placeholders in the specifier forms:

Specifier Values for “a”, “b”, and “c” Values for “n”
sec 0 thru 59 1 thru 59
min 0 thru 59 1 thru 59
hour 0 thru 23 1 thru 23
month_day 1 thru 31 1 thru 30
month 1 thru 12 1 thru 11
week_day 0 (Sunday) thru 6 (Saturday) 1 thru 5

The default value for time is * * * * * * .

Error-handling

When a job (or, specifically, the shell code that the job runs) exited with a non-0 status, we call this a “job error”.

One of Jobber’s main features is that you can control how Jobber handles job errors. You do this by setting the onError field in a job’s definition to one of these values:

Value Effect when a job error happens
Stop Stop scheduling runs of this job.
Backoff Schedule runs of this job according to an exponential backoff algorithm. If a later run of the job succeeds, jobber resumes scheduling this job normally; but if the job continues to err, Jobber will eventually stop scheduling it.
Continue Continue scheduling this job normally.

When Jobber stops scheduling runs of a job due to one or more job errors, we call this a “job failure”.

If you would like Jobber to notify you when a job error or job failure occurs, do the following:

  1. Write a notify program that does whatever you want done after a job error or job failure
  2. Set the notifyProgram field in the prefs section of the Jobber file to the path to your notify program

The input that Jobber will send to the notify program is described elsewhere.

Job Status Notifications

You can arrange for Jobber to send notifications when certain events happen for a particular job:

Job success The job (or, specifically, the shell script that the job runs) exited with status 0
Job error The job (or, specifically, the shell script that the job runs) exited with a non-0 status
Job failure Jobber stopped scheduling runs of the job due to one or more job errors

By default, these notifications consist of emails to the user account that owns the job. Jobber uses sendmail to do this.

Also, by default, Jobber will send notifications when any job fails. To disable this for a particular job, set notifyOnFailure in that job’s definition to false .

To enable notifications when a particular job succeeds, set notifyOnSuccess to true in that job’s definition.

To enable notifications when a particular job errs, set notifyOnError to true in that job’s definition.

For example, in the following, for DailyBackup Jobber will send notifications only on job error, while for OffsiteBackup it will send notifications only on job failure:

[prefs]
...

[jobs]
- name: DailyBackup
  notifyOnError: true
  notifyOnFailure: false
  ...

- name: OffsiteBackup
  onError: Stop
  ...

Notify Programs

Rather than having Jobber notify by sending emails, you can have it call an arbitrary program, called a “notify program”.

To do so, in the prefs section of your jobfile define notifyProgram to be a path to your notify program, thus:

[prefs]
notifyProgram: /home/dylan/myNotifyProgram.sh

(Make sure the notify program is executable.)

When Jobber calls your notify program, it will write to stdin a JSON document like this:

{  
   "job":{  
      "command":"/home/dylan/some_program",
      "name":"MyHourlyJob",
      "notifyOnError":true,
      "notifyOnFailure":false,
      "onError":"Stop",
      "status":"Failed",
      "time":"0 0 * * * *"
   },
   "startTime":"May 29 15:25:10 2017",
   "stderr":"Some errors\n",
   "stderr_base64":false,
   "stdout":"Quotes: \"adf\"\n",
   "stdout_base64":false,
   "succeeded":false,
   "user":"dylan"
}

The fields in the job object are from the job’s definition, except for status, which indicates whether and how Jobber will schedule runs of the job in the future. Its possible values are Good , Backoff , or Failed .

stdout (respectively, stderr ) contains whatever the job wrote to stdout (stderr). If stdout_base64 (stderr_base64) contains false , then the job wrote only UTF-8 bytes to stdout (stderr), and stdout ( stderr ) contains a string with those bytes; otherwise, there were some non-UTF-8 bytes and stdout ( stderr ) contains a Base64 encoding of the output.

Keeping a Log of Job Runs

The jobber log command prints the run log — i.e., a log containing info about previous runs. By default, the run log only contains the 100 most recent runs, and it is not written to disk, which means that it will be lost when the Jobber service stops or restarts.

Here is an example of keeping the run log in memory:

[prefs]
runLog:
    type: memory
    maxLen: 500

With this, Jobber will keep log entries for no more than the 500 most recent runs of any of the user’s jobs.

Here is an example of keeping the run log on disk:

[prefs]
runLog:
    type: file
    path: /var/log/jobber-runs
    maxFileLen: 100m
    maxHistories: 2

With this, Jobber will write run log entries to /var/log/jobber-runs. /var/log/jobber-runs will grow to no more than 100 MB, and Jobber will rotate it when it gets too big. Jobber will keep up to two of these rotated “history” files (named “jobber-runs.1” and “jobber-runs.2”). When it is necessary to delete a history file, the oldest one will get the axe.

Loading Jobs

After you've created a user’s jobfile, log in as that user and do:

jobber reload

You can also reload all users’ jobfiles by logging in as root and doing:

jobber reload -a

Listing Jobs

You can list the jobs for a particular user by logging in as that user and doing

jobber list

This command also shows you the status of each job — that is, whether the job is being scheduled as normal, the exponential backoff algorithm is being applied, or the job has failed.

As with the reload command, you can do the same for all users by adding the -a option as root.

Listing Runs

You can see a list of recent runs of any jobs for a particular user by logging in as that user and doing

jobber log

As with the other commands, you can do the same for all users by adding the -a option as root.

Testing Jobs

If you'd like to test out a job, do

jobber test JOB_NAME

Jobber will immediately run that job, tell you whether it succeeded, and show you its output.

Pausing and Resuming Jobs

You can temporarily stop Jobber from running a job thus:

jobber pause JOB_NAME

And of course you can resume it:

jobber resume JOB_NAME

NOTE: If a job is paused when the Jobber service stops, then when the Jobber service starts again the job will be scheduled normally.

Printing a Job’s Command

You can print a job’s command (the shell script that it executes) thus:

jobber cat JOB_NAME