During a lunch break in Munich last week, Michael mentioned Ansible rulebooks, and I realized I had not taken the time to look into them.
Rulebooks are the system by which Ansible is told which events to use in Event-Driven Ansible. They are written in YAML and contain three main components: sources which define the event sources to be used, rules which define conditionals matched from sources, and actions which trigger what should occur when a condition is met.
Here’s a small example I’ve cobbled together to test Event-Driven Ansible (EDA).
- name: Rulebook to do something
hosts: localhost
gather_facts: false
sources:
- ansible.eda.webhook:
host: 127.0.0.1
port: 6000
- ansible.eda.file_watch:
path: "files/"
recursive: false
ignore_regexes: [ '.*\.o' ]
rules:
- name: Launch playbook on start cmd
condition: event.payload.cmd == 'start'
action:
run_playbook:
name: jp01.yml
extra_vars:
dessert: "{{ event.payload.data }}"
home: "{{ HOME }}" # from environment
person: "{{ person }}" # from vars
- name: trigger on range
condition: event.change == 'created' # 'modified'
action:
run_module:
name: copy
module_args:
src: "{{ event.src_path }}"
dest: "/tmp/files"
My rulebook defines two sources: the first listens to HTTP webhooks on port 6000, and the second watches (requires pip install watchdog
) a directory for new files.
Then I define two rules:
- the first matches the
cmd
element in the HTTP payload to the wordstart
and performs an action on match. The actionrun_playbook
launches the specified playbook using the inventory we giveansible-rulebook
. - the second matches when the event indicates a file has changed in the directory and invokes an Ansible module (
copy
) to copy new discovered file to a particular destination.
I launch the rulebook:
$ ansible-rulebook -i inventory \
-r rulebook.yml \
--vars v.yml \
-E HOME \
--print-events
I then POST a webhook and create a new file in the watched directory:
$ curl -H 'Content-type: application/json' \
-d "$(jo cmd='start' data='chocolate mousse')" \
http://localhost:6000
$ ls > $dir/files/n01
On the console I can observe the events and what they trigger:
{ 'meta': { 'endpoint': '',
'headers': { 'Accept': '*/*',
'Content-Length': '41',
'Content-Type': 'application/json',
'Host': 'localhost:6000',
'User-Agent': 'curl/7.87.0'},
'received_at': '2023-08-14T11:09:59.195936Z',
'source': { 'name': 'ansible.eda.webhook',
'type': 'ansible.eda.webhook'},
'uuid': 'c5241a36-3c11-403e-9899-1c43209c858f'},
'payload': {'cmd': 'start', 'data': 'chocolate mousse'}}
PLAY [localhost] ***************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "**** From /Users/jpm. Would you like some chocolate mousse, Jane?"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
{ 'change': 'created',
'meta': { 'received_at': '2023-08-14T11:10:17.550916Z',
'source': { 'name': 'ansible.eda.file_watch',
'type': 'ansible.eda.file_watch'},
'uuid': '6878c7a9-0454-414b-a820-0ec788da599f'},
'root_path': 'files/',
'src_path': '/Users/jpm/take/training/rulebook/files/n01',
'type': 'FileCreatedEvent'}
localhost | CHANGED => {
"changed": true,
"checksum": "fe142a4c6a82a55a1c7156fda0056c9f479c5b0c",
"dest": "/tmp/files/n01",
"gid": 20,
"group": "staff",
"md5sum": "1033373ec84317c9920fcf8e11635e1e",
"mode": "0644",
"owner": "jpm",
"size": 39689,
"src": "/Users/jpm/.ansible/tmp/ansible-tmp-1692011418.0594718-31843-165094790951764/source",
"state": "file",
"uid": 501
}
Internally ansible-rulebook
uses the Java Drools rule engine and ansible-runner to actually invoke Ansible content. Rulebooks can gather facts which can be used in conditions. ansible-rulebook
appears to ignore ansible.cfg
as far as I can tell, and it requires me passing it an inventory explicitly. As shown in my example above, I can pass variables to it (from JSON or YAML files), and have it import environment variables – both useful for introducing API keys and whatnot.
I’ve only just begun toying with Event-Driven Ansible (and have reported an issue with watch_file
which may not even be one), but it appears to work quite well so far.
I think this will be most interesting when interfacing with, say, repository pushes etc. which can then trigger Ansible playbook runs.
Further reading: