Deployment

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

Note that, although Jobber can be used instead of cron, it is certainly possible to run both at the same time.

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 a recent version of Go 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
go get github.com/dshearer/jobber
cd src/github.com/dshearer/jobber
git checkout v1.2
make GO_WKSPC=/path/to/your/workspace

Jobber consists of two binaries: jobber and jobberd. jobberd is a daemon that runs jobs; jobber is a client with which the user can interact with jobbberd. After compiling, you’ll find them in /path/to/your/workspace/bin.

Now:

  1. Make a new, non-login user called “jobber_client”.
  2. Change jobber’s permissions to 04755, and its owner to jobber_client:root.
  3. Change jobberd’s permissions to 0755, and its owner to root:root.
  4. Move jobber and jobberd someplace more appropriate (e.g., /usr/local/bin and /usr/local/libexec respectively).
  5. Ensure that jobberd will get started when your computer boots. Note that jobberd does not daemonize itself — you will need to run it with something like daemonize(1).

Defining Jobs

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. 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.

Section prefs

This section sets general preferences for how Jobber runs this user’s jobs. At this time, there is only one valid field.

Field Value Default Value
notifyProgram

A path to an executable file.

When this field is set, jobs that notify on error or failure do so by executing the specified file. See below for details.

When it is not set, they notify by sending an email to the owning user, using sendmail.

None.

Section jobs

This section defines the user’s jobs.

As stated above, this section must contain a YAML file that defines a sequence of mappings. Each mapping in this sequence defines a job.

The fields of a job definition:

Field Value Default Value
name The name of the job. N/A
cmd A shell script that is executed when this job runs. Jobber will use the shell at /bin/sh. N/A
time A string specifying when the job should run. See below for details. * * * * * *
onError A string (Stop, Backoff, or Continue) specifying what should happen when the job has an error. See below for details. Continue
notifyOnError A string (true or false) specifying whether jobberd should notify when the job has an error. See below for details. false
notifyOnFailure A string (true or false) specifying whether jobberd should notify when the job fails. See below for details. true

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: it will run at any time that satisfies all of the specifiers sec, min, hour, and month and one of the specifiers month_day and week_day.

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 n-th value — e.g., */25 in the sec 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, ...
a-b Any of the values 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 thru 6 1 thru 5

Error-handling

Some terms: When Jobber runs a job and that job exits with a non-0 status, we call that a job error. When Jobber stops scheduling runs of a job due to one or more job errors, we call that a job failure.

In the onError field in the job’s definition, you can control what Jobber does after a job error by picking one of these values:

Value Effect
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 errors again on several consecutive runs, Jobber stops scheduling it.
Continue Continue scheduling this job normally.

If you would like Jobber to notify you when a job errors or fails, first set notifyOnError or notifyOnFailure to true in the job’s definition in the Jobber file.

Then, write a notify program — a program that does whatever logic you want done after an error or failure. Set the notifyProgram field in the prefs section of the Jobber file to the path to your notify program.

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 contains Good , Backoff , or Failed .

stdout (respectively, stderr ) contains whatever the job wrote to stdout (stderr). If stdout_base64 contains true , 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.

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.