oJob
oJob is an automated OpenAF job executor. It basically tries to modularize and organize script processing hiding most of the complexity of handling execution dependencies, parallel processing, running scripts as daemons, remote coordinated job execution, reusable modules for multiple targets, etc.
A oJob is mainly defined by a YAML file (although it can also be defined in JSON) that looks like a shopping list:
jobs:
- name: Get eggs
- name: Get sugar
- name: Get floor
- name: Make cake
deps:
- Get eggs
- Get sugar
- Get floor
todo:
- Get eggs
- Get sugar
- Get floor
- Make cake
From this yaml file oJob already understands that we need to somehow get eggs, sugar and floor (even in parallel if possible) in order to be ready to prepare a cake.
So basically you have a jobs section that defines each job (for the sake of simplicity of the example we didn’t define how to execute each job) and if they have dependencies to start. Then you have a to do section that defines, by order, what needs to happen.
To run it you would just execute:
ojob myrecipe.yaml
In the next chapters will go through how you can add arguments to each job (e.g. how many eggs, how much sugar, …), how you can have jobs chains (e.g. job “Get ingredients” → “Make cake” → “Serve cake” for just a to do “Make cake”), how you can make a production line of cakes with array arguments and more.
Jobs
A job is simple module unit in oJob, identified by an unique name that can be plug in different ways to reach the end goal. As input they accept arguments, as output they execute the actions you define:
jobs:
- name: Say hello
exec: >
print("Hello " + args.who);
todo:
- name: Say hello
args:
who: world
As you can see on the example above, the todo list can also contain arguments for a job. And inside a job you have the “args” map available with the args provide in the todo.
Each job definition has the following possible entries:
Entry | Type | Mandatory? | Description |
---|---|---|---|
name | string | yes | The unique name of the job. |
args | map/array | no | A map of arguments to be used by the job. It will be available on openaf code as a “args” map. If instead of a map is defined as an array it will execute the job for each array entry that will represent each job execution map of arguments. |
exec | string | no | OpenAF code that gets executed for this job. If it throws an exception the job will fail otherwise it will be considered successful. |
deps | array | no | An array of job dependencies where you refer other jobs by their unique name or a map with the following entries: name, onSuccess, onFail. The onSuccess and onFail can be used to define OpenAF code to execute on the success or failure of the job dependency executions. If the code returns true it will be consider as a satisfied dependency allowing the current job definition to execute, if false it will be consider that the current job definition can’t be executed. Additionally theses functions can access the current “args”, “job” map and run “id”. |
type | string | no | The oJob type. By default they are of type “single”. But you can specify other types of jobs like shutdown, periodic and “jobs”. See more on the job types sub-chapter. |
typeArgs | map | no | A map of arguments to be provided for each job type. Common to all is the typeArg ‘single’ which tries to force the job to run in a single thread if args is an array. See more on the job types sub-chapter. |
from | string/array | no | The name of a job (or ordered list) whose execution will be prefixed (like a document header) with the current job execution (e.g. if you want all job executions to execute another job initially). |
to | string/array | no | The name of a job (or ordered list) whose execution will be suffixed (like a document footer) with the current job execution (e.g. if you want all job executions to execute another job when they end). |
help | string | no | A help text to be presented whenever you execute “ojob -jobhelp ‘My job’“ |
lang | string | no | The executing language for the “exec” entry. By default languages ‘js’ (default), ‘python’ and ‘shell’ can be used but custom languages can be added using the ojob.langs entry or the typeArgs.langs entry. For ‘js’ and ‘python’ job arguments will be available in the “args” variable for other shell based languages job arguments will be converted to environment variables. |
catch | string | no | Code for a try/catch function that will get executed if the main job execution code throws an exception. Receives, as parameters: “args”, “job”, “id”, “deps” and the “exception”. |
help | map | no | Provides a help message to be used with the help option, as a string, or the oJob help (on a job named “Help” with a “text” string field, an array “expects” field where each entry should be a “name” string, a “desc” string and an “example” string. |
each | string | no | Can be a string or an array representing other jobs names that will be called whenever the function “each” is used inside the main execution code providing a map (each function will receive the map as args). See the help for $doA2B for more. |
lang | string | no | Convinient string or map shortcut to job’s generic typeArgs langs |
Job types
The most used type of job is “simple”. All job types, including “simple” have the following typeArgs:
typeArgs entry | Type | Mandatory? | Description |
---|---|---|---|
timeout | number | no | _The time interval, in milliseconds, for the entire job execution or between stopWhen function executions. If no stopWhen function is provided and the timeout time is exceeded the job will fail indicating that it exceed the timeout. _ |
stopWhen | string | no | Function string that will be evaluated continuously or every timeout time interval. If the function returns true the current job execution will be terminated. |
langs | map | no | String with language or Map with lang (language name) and shell (language shell command) to be used as the executing language for the “exec” entry. Job arguments will be available as environment variables. Optionally using “langFn” the execution code to handle will be provided as the variable “code”. |
execJs | String | no | Reads the OpenAF javascript execution code from an external filepath. |
execRequire | String | no | Loads an external filepath OpenAF javascript execution code file where the function with the job name will be executed with the current args. |
execPy | String | no | Reads a Python execution code from an external filepath. |
file | String | no | Reads the execution code from an external filepath. |
lock | String | no | Specifies a lock name to ensure a job execution is not concurrent. |
lockCh | String | no | Specifies the lock channel to use (defaults to simple “oJob::locks”) |
Then for other types there are specific typeArgs available:
periodic
This job when included on a todo list will execute on a separate thread the job periodically until the ojob ends or it’s stopped (if ojob.daemon is set to true). You can optionally provide a cron like expression time. To set this you can provide as typeArgs:
typeArgs entry | Type | Mandatory? | Description |
---|---|---|---|
timeInterval | number | no | The time interval in milliseconds between each job execution periodic execution. |
cron | string | no | If defined, a minutes-based (e.g. * * * * *) or seconds-based (e.g. * * * * * *) cron-like expression to indicate if the job should run between. This cron expression will be checked for on the start of each timeInterval if defined otherwise a single thread will wait for the next scheduled cron event. |
cronCheck | map | no | If defined, creates a channel based tracking to eventually persist if this job definition has executed or not within the provided cron expression (for example: if it was supposed to run every hour but once restart didn’t run for 2 hours it will automatically try to execute the job). The map should be composed of: active (boolean); ch (string), indicating the channel to be used (defaults to oJob::cron); retries (number), the number of retries if the job execution fails; cron (string), a different cron expression to determin if an execution is missing or not. |
waitForFinish | boolean | no | If defined and set to true the timeInterval is considered from the end of execution (even if bigger than the defined timeInterval). Thus preventing more than one concurrent execution to occur. If not defined or set to false the timeInterval is considered from the beginning of execution. So even if the first execution didn’t end and second will start if the timeInterval has reach it’s end. |
Example:
ojob:
daemon: true
jobs:
# Say the time regularly
- name : Say the time
type : periodic
typeArgs :
waitForFinish: false
cron : "*/5 * * * * *"
exec : >
print(new Date());
todo:
- Say the time
subscribe
This job type will execute whenever a set/setAll/unSet operation occurs in a channel (OpenAF channel subscriber). The job execution will have access to the operation, key(s) and value(s) affected.
Example:
jobs:
- name : Print stuff
type : subscribe
typeArgs:
chSubscribe: myChannel
exec : |
sprint(args);
// args.ch - channel name
// args.op - operation (set/unset/setall)
// args.k - the key (set/unset) or key fields to use (setall)
// args.v - the value (set/unset) or array of values (setall)
todo:
- Print stuff
typeArgs entry | Type | Mandatory? | Description |
---|---|---|---|
chSubscribe | String | Yes | The channel name to subscribe for changes. |
shutdown
This is equivalent to single but it will only be triggered on the end of the oJob script or if a shutdown signal is received. It’s ideal use is closing open connections, saving data before a crash, etc… It has no typeArgs available.
Example:
jobs:
- name: Do stuff
exec: >
print("I'm doing stuff...");
// connectionStart()
- name: Close
type: shutdown
exec: >
print("I'm going to die now... bye!");
// connectionClose()
todo:
- Close
- Do stuff
Why isn’t there a “startup” type? That’s because you control what runs first in the todo list and with job dependencies or you can use the sequence mode (see oJob settings).
jobs
“The mother of all jobs” in the sense that will execute any other provided ojob yaml file with it’s jobs and todo. The arguments received by this job will be passed through as arguments all jobs in the ojob yaml file. It has one typeArgs: file. This argument is the file path to the other ojob yaml file.
jobs:
- name : Do stuff for a date
type : jobs
typeArgs:
file: lotsOfStuff.yaml
args :
dateToProcess: 20170101
- name : Done
deps :
- Do stuff for a date
exec : >
print("Finally... everything is done.");
todo:
- Do stuff for a date
- Done
Todo
This is where you define the list of what needs to be done (e.g. to do). Let’s consider the example:
jobs:
- name: Get eggs
- name: Get sugar
- name: Get floor
- name: Make cake
deps:
- Get eggs
- Get sugar
- Get floor
todo:
- Get eggs
- Get sugar
- Get floor
- Make cake
oJob will try to “Get eggs”, “Get sugar”, “Get floor” and “Make cake” in parallel. Since “Make cake” depends on the success of “Get eggs”, “Get sugar”, “Get floor” it won’t start until all of them end successfully. This behavior can be changed by the ojob.sequential = true where oJob will honor the order of the todo list and execute subsequent job only when the previous and ended it’s execution (but this doesn’t disable the job dependencies check).
But each job execution might require different arguments. In this case, different quantities. So on “todo” you can specify arguments to execute each job:
todo:
- name: Get eggs
args:
howMany: 2
size : L
- name: Get sugar
args:
cups: 1.5
- Get floor
- Make cake
If you instead of a map you provide a list/array then the job will be execute with each list of arguments provided on the list:
todo:
- name: Get eggs
args:
howMany: 8
size : L
- name: Get sugar
args:
cups: 5
- Get floor
- name: Make cake
args:
- name : Chocolate cake
typeOfChocolate: milk
- name : Dark chocolate cake
typeOfChocolate: dark
Include
This is where you can include other ojob files with more job definitions. This allows for having modular ojob files with different type of jobs. It’s basically an array/list of file paths. The files will be searched on the current path and on the installed oPacks. So if you install oJob-common, for example, you can include existing job definitions in other ojob files:
include:
- oJobSSH.yaml
jobs:
- name: print remote cpuinfo
to : SSH Exec
args:
host : some.host
login: mylogin
pass : mypassword
cmd : >
cat /proc/cpuinfo
todo:
- print remote cpuinfo
oJob
This is where you set specific settings on how the ojob should run. Here is a set of list of possible entries per category:
Logging
Funcitonality to control OpenAF and oJob logging:
Entry | Type | Description |
---|---|---|
logToFile | map | Map with the same options as ow.ch.utils.setLogToFile |
log | map | Map with the same options as setLog |
recordLog | boolean | Determines if it should use startLog to log into the __log channel. |
logArgs | boolean | If true the arguments provided to each job execution will be logged (defaults to false). |
logToConsole | boolean | If false ojob logging won’t be output to the console (defaults to true). |
logLimit | number | The number of internal execution logs per job that should be kept (default to 100). |
logJobs | boolean | If false no job start, end or error will be logged. (default to true). |
metrics | map | Can be a boolean to indicate that metrics should be collected or a map with the metrics channel “chName” and “period” for collecting (see more in ow.metrics.startCollecting). Optionally custom metric functions can be added with a “add” map where each represents the metric name and the value the corresponding function code (see more in ow.metrics.add help) |
Execution control
Functionality to control oJob’s job execution:
Entry | Type | Description |
---|---|---|
unique | map | Map to ensure that only one instance of ojob runs. The map can define a pidFile (defaults to ojob.pid and will be evaluated as code) and killPrevious (defaults to false). |
sequential | boolean | If true each todo entry will be executed only when the previous has ended it’s execution. Otherwise it will try to execute in parallel. |
daemon | boolean | If true the ojob will behave like a daemon. It will only exit if the process is killed or if executed with the arguments stop, forcestop or restart. |
numThreads | number | Force the maximum number of threads to use. If not defined it will try to automatically assess an ideal number of threads to use. |
async | boolean | Runs all jobs asynchronously (defaults to false) |
depsTimeout | number | If defined establishes a maximum time interval, in ms, that each job will wait for the corresponding job dependencies to be fullfilled. If the time is exceeded the corresponding job will fail. |
checkStall | map | Checks if job execution is stalled “everySeconds” (defaults to every 60 seconds) by executing “checkFunc” (stops oJob if returns true) or “killAfterSeconds” seconds have pass since startup. |
tags | array | An array of string tags to easily identify this oJob among others. |
Note: Use sequential = true and numThreads = 1 for a total sequential execution. Keep in mind that if numThreads is not defined or greater than 1 multiple job executions can still occur despite sequential = true (for example when the job args is an array). The sequential parameter will just ensure that jobs listed in the todo list are executed in the order of the todo list instead of being launched all in parallel.
Languages support
Functionality related with language support:
Entry | Type | Description |
---|---|---|
langs | array | List of maps with two entries: lang (a name) and shell (the command to execute). If the job has the “lang” entry it will invoke the “shell” command to execute the “exec” entry. Job arguments will be passed as environment variables. Optionally using “langFn” the execution code to handle will be provided as the variable “code”. |
OpenAF functionality
Functionality related with OpenAF:
Entry | Type | Description |
---|---|---|
opacks | array | Array of oPacks needed for the oJob execution (if possible, it will try to install the corresponding oPack if not available). If the array entry is a map it’s possible to provide a version rule string value (e.g. >= 20200101) |
loadLibs | array | Array of OpenAF javascript libs to preload (equivalent to “loadLib”) |
loads | array | Array of OpenAF javascript to preload (equivalent to “load”) |
argsFromEnvs | boolean | If true all the current environment variables will be converted into entries in the args map. |
Channels
This is actually a map to define the channel use in the ojob:
Entry | Type | Description |
---|---|---|
expose | boolean | Determines if channels should be exposed externally. |
port | number | If expose = true then defines the host port to use to expose channels. |
host | string | If expose = true then defines the network interface on which to expose channels. |
keyStorePath | string | If expose = true then defines where SSL keys are stored to use HTTPS. |
keyPassword | string | If expose = true and a keyStorePath was defined, defines the password to use to open the corresponding key store. |
permissions | string | General permissions for anonymous access (r = read, w = write). Defaults to “r”. |
list | array | If expose = true specifies which channels should be exposed (default is all). |
auth | array | Array of maps to provide http authentication when accessing channels. Each map should have a login, a pass and permissions. |
create | array | An array of maps containing the channel “type”, the “compress” create option and the given necessary “options”. |
peers | array | Establishes peer relationships for all the “list” channels (requires port to be defined) with the provided url array list (the uri expected should included the channel name). |
clusters | map | Setups an OpenAF cluster with the provided name, checkPeriod, host, port, nodeTimeoutm numberOfTries, tryTimeout and discovery map entries. |
Advanced preprocessing
Entry | Type | Description |
---|---|---|
conAnsi | boolean | Advanced option to turn off ansi characters use. |
conWidth | number | Advanced option to override screen size detection. |
init
For organization proposes if a init entry is used all mapped values will be available during oJob execution in args.init map entry.
oJob command-line
To execute a ojob you can simple execute:
ojob myrecipe.yaml
You can add arguments that will be available to all jobs in the file as arguments:
ojob myrecipe.yaml typeOfChocolate=dark nameOfCake=My\ dark\ cake
You can also use the following extra options:
Option | Description |
---|---|
-compile | Processes all includes and the current file to produce a single YAML output. |
-tojson | Processes all includes and the current file to produce a single JSON output. Useful to debug any YAML parsing issues. |
-jobs | List all the jobs available including the ones defined in includes. |
-todo | List the final todo list after processing all includes and the current file. |
-deps | Tries to provide a human readable representation of the dependencies between all jobs in includes and on the current file. |
-jobhelp | Display any available help information for a job. Can include extra help of jobs defined in “from” an “to”. Example: “ojob example.yaml -jobhelp oJob sh” |
-nocolor | Removes all processing to determine screen size and use ansi colors. |