Skip to content

Commit

Permalink
fix: [AAP-14735] process output from a module (#564)
Browse files Browse the repository at this point in the history
Wraps the module into a playbook and runs the playbook via ansible
runner. This gives us the ability to get the module output.

https://issues.redhat.com/browse/AAP-14735

The following is a sample wrapper for a module, the module_args can be a
string or it can be a dictionary.

```yaml
- gather_facts: false
  hosts: all
  name: wrapper
  tasks:
  - ansible.eda.upcase:
      name: Fred Flintstone
    name: Module wrapper
    register: module_result
  - ansible.builtin.set_fact:
      cacheable: true
      module_result: '{{ module_result }}'
    name: save result
```
  • Loading branch information
mkanoor authored Aug 21, 2023
1 parent c7cf3f4 commit bead378
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 30 deletions.
53 changes: 41 additions & 12 deletions ansible_rulebook/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from .exception import (
ControllerApiException,
JobTemplateNotFoundException,
MissingArtifactKeyException,
PlaybookNotFoundException,
PlaybookStatusNotFoundException,
ShutdownException,
Expand All @@ -54,6 +55,7 @@

KEY_EDA_VARS = "ansible_eda"
INTERNAL_ACTION_STATUS = "successful"
MODULE_OUTPUT_KEY = "module_result"


async def none(
Expand Down Expand Up @@ -446,13 +448,9 @@ async def run_module(
)

logger.info("Calling Ansible runner")
module_args_str = ""
if module_args:
for k, v in module_args.items():
if len(module_args_str) > 0:
module_args_str += " "
module_args_str += f"{k}={v!r}"

host_pattern = ",".join(hosts)
playbook = os.path.join(temp_dir, "wrapper.yml")
_wrap_module_in_playbook(module_name, module_args, host_pattern, playbook)
if retry:
retries = max(retries, 1)
for i in range(retries + 1):
Expand All @@ -467,11 +465,7 @@ async def run_module(
event_log,
job_id,
temp_dir,
dict(
module=module_name,
host_pattern=",".join(hosts),
module_args=module_args_str,
),
dict(playbook=playbook),
hosts,
inventory,
verbosity,
Expand All @@ -496,6 +490,7 @@ async def run_module(
action_run_at,
set_facts,
post_events,
MODULE_OUTPUT_KEY,
)
shutil.rmtree(temp_dir)

Expand Down Expand Up @@ -682,6 +677,7 @@ async def post_process_runner(
run_at: str,
set_facts: Optional[bool] = None,
post_events: Optional[bool] = None,
output_key: Optional[str] = None,
):

rc = int(_get_latest_artifact(private_data_dir, "rc"))
Expand Down Expand Up @@ -720,6 +716,17 @@ async def post_process_runner(
for host_facts in glob.glob(os.path.join(fact_folder, "*")):
with open(host_facts) as f:
fact = json.loads(f.read())
if output_key:
if output_key not in fact:
logger.error(
"The artifacts from the ansible-runner "
"does not have key %s",
output_key,
)
raise MissingArtifactKeyException(
f"Missing key: {output_key} in artifacts"
)
fact = fact[output_key]
fact = _embellish_internal_event(fact, action)
logger.debug("fact %s", fact)
if set_facts:
Expand Down Expand Up @@ -1081,3 +1088,25 @@ def _controller_job_url(data: dict) -> str:
if "id" in data:
return f"{job_template_runner.host}/#/jobs/{data['id']}/details"
return ""


def _wrap_module_in_playbook(module_name, module_args, hosts, playbook):
module_task = {
"name": "Module wrapper",
module_name: module_args,
"register": MODULE_OUTPUT_KEY,
}
result_str = "{{ " + MODULE_OUTPUT_KEY + " }}"
set_fact_task = {
"name": "save result",
"ansible.builtin.set_fact": {
MODULE_OUTPUT_KEY: result_str,
"cacheable": True,
},
}
tasks = [module_task, set_fact_task]
wrapper = [
dict(name="wrapper", hosts=hosts, gather_facts=False, tasks=tasks)
]
with open(playbook, "w") as f:
yaml.dump(wrapper, f)
5 changes: 5 additions & 0 deletions ansible_rulebook/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,8 @@ class UnsupportedActionException(Exception):
class InventoryNotFound(Exception):

pass


class MissingArtifactKeyException(Exception):

pass
8 changes: 4 additions & 4 deletions ansible_rulebook/schema/ruleset_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@
"run_module": {
"type": "object",
"properties": {
"copy_files": {
"type": "boolean"
},
"name": {
"type": "string"
},
Expand Down Expand Up @@ -385,7 +382,10 @@
"type": "number"
},
"module_args": {
"type": "object"
"type": [
"object",
"string"
]
},
"extra_vars": {
"type": "object"
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def test_actions_sanity(update_environment):
), "multiple_action action failed"

assert (
len(result.stdout.splitlines()) == 56
len(result.stdout.splitlines()) == 106
), "unexpected output from the rulebook"


Expand Down
13 changes: 5 additions & 8 deletions tests/examples/29_run_module.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@
condition: event.i == 1
action:
run_module:
post_events: True
name: ansible.eda.upcase
module_args:
name: Fred Flintstone








- name: r2
condition: event.message == "FRED FLINTSTONE"
action:
print_event:
13 changes: 8 additions & 5 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ async def test_29_run_module():

event = event_log.get_nowait()
assert event["type"] == "Job", "0"
for i in range(4):
for i in range(9):
assert event_log.get_nowait()["type"] == "AnsibleEvent", f"0.{i}"

event = event_log.get_nowait()
Expand All @@ -858,6 +858,9 @@ async def test_29_run_module():
assert event["rc"] == 0, "2.1"
assert event["status"] == "successful", "2.2"
event = event_log.get_nowait()
assert event["type"] == "Action"
assert event["action"] == "print_event"
event = event_log.get_nowait()
assert event["type"] == "Shutdown", "8"
assert event_log.empty()

Expand All @@ -881,14 +884,14 @@ async def test_30_run_module_missing():

event = event_log.get_nowait()
assert event["type"] == "Job", "0"
for i in range(4):
for i in range(10):
assert event_log.get_nowait()["type"] == "AnsibleEvent", f"0.{i}"

event = event_log.get_nowait()
assert event["type"] == "Action", "1"
assert event["action"] == "run_module", "2"

assert event["rc"] == 2, "2.1"
assert event["rc"] == 4, "2.1"
assert event["status"] == "failed", "2.2"
event = event_log.get_nowait()
assert event["type"] == "Shutdown", "8"
Expand All @@ -914,7 +917,7 @@ async def test_31_run_module_missing_args():

event = event_log.get_nowait()
assert event["type"] == "Job", "0"
for i in range(4):
for i in range(6):
assert event_log.get_nowait()["type"] == "AnsibleEvent", f"0.{i}"

event = event_log.get_nowait()
Expand Down Expand Up @@ -947,7 +950,7 @@ async def test_32_run_module_fail():

event = event_log.get_nowait()
assert event["type"] == "Job", "0"
for i in range(8):
for i in range(12):
assert event_log.get_nowait()["type"] == "AnsibleEvent", f"0.{i}"

event = event_log.get_nowait()
Expand Down

0 comments on commit bead378

Please sign in to comment.