Skip to content

Commit

Permalink
feat: support files and directories for inventory
Browse files Browse the repository at this point in the history
This is based on PR ansible#523
  • Loading branch information
mkanoor committed Jun 29, 2023
1 parent 4b2e365 commit c4edbf5
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 102 deletions.
10 changes: 8 additions & 2 deletions ansible_rulebook/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from ansible_rulebook.engine import run_rulesets, start_source
from ansible_rulebook.job_template_runner import job_template_runner
from ansible_rulebook.rule_types import RuleSet, RuleSetQueue
from ansible_rulebook.util import load_inventory
from ansible_rulebook.validators import Validate
from ansible_rulebook.websocket import (
request_workload,
Expand All @@ -41,6 +40,7 @@
from .exception import (
ControllerNeededException,
InventoryNeededException,
InventoryNotFound,
RulebookNotFoundException,
WebSocketExchangeException,
)
Expand Down Expand Up @@ -80,7 +80,7 @@ async def run(parsed_args: argparse.ArgumentParser) -> None:
parsed_args, startup_args.variables
)
if parsed_args.inventory:
startup_args.inventory = load_inventory(parsed_args.inventory)
startup_args.inventory = parsed_args.inventory
startup_args.project_data_file = parsed_args.project_tarball
startup_args.controller_url = parsed_args.controller_url
startup_args.controller_token = parsed_args.controller_token
Expand Down Expand Up @@ -239,6 +239,12 @@ def validate_actions(startup_args: StartupArgs) -> None:
"which needs inventory to be defined"
)

if action.action in INVENTORY_ACTIONS and not os.path.exists(
startup_args.inventory
):
raise InventoryNotFound(
f"Inventory {startup_args.inventory} not found"
)
if (
action.action == "run_job_template"
and not startup_args.controller_url
Expand Down
14 changes: 11 additions & 3 deletions ansible_rulebook/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
)
from .job_template_runner import job_template_runner
from .messages import Shutdown
from .util import get_horizontal_rule, run_at
from .util import create_inventory, get_horizontal_rule, run_at

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -360,6 +360,7 @@ async def run_playbook(
temp_dir,
dict(playbook=playbook_name),
hosts,
inventory,
verbosity,
json_mode,
)
Expand Down Expand Up @@ -471,6 +472,7 @@ async def run_module(
module_args=module_args_str,
),
hosts,
inventory,
verbosity,
json_mode,
)
Expand Down Expand Up @@ -503,6 +505,7 @@ async def call_runner(
private_data_dir: str,
runner_args: Dict,
hosts: List,
inventory: str,
verbosity: int = 0,
json_mode: Optional[bool] = False,
):
Expand Down Expand Up @@ -555,6 +558,11 @@ def cancel_callback():
verbosity=verbosity,
event_handler=event_callback,
cancel_callback=cancel_callback,
inventory=os.path.join(
private_data_dir,
"inventory",
os.path.basename(inventory),
),
json_mode=json_mode,
**runner_args,
),
Expand Down Expand Up @@ -623,8 +631,8 @@ async def pre_process_runner(
with open(os.path.join(env_dir, "extravars"), "w") as f:
f.write(yaml.dump(playbook_extra_vars))
os.mkdir(inventory_dir)
with open(os.path.join(inventory_dir, "hosts"), "w") as f:
f.write(inventory)
if inventory:
create_inventory(inventory_dir, inventory)
os.mkdir(project_dir)

logger.debug("project_data_file: %s", project_data_file)
Expand Down
1 change: 1 addition & 0 deletions ansible_rulebook/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def get_parser() -> argparse.ArgumentParser:
"-i",
"--inventory",
help="Inventory",
default=os.environ.get("ANSIBLE_INVENTORY", ""),
)
parser.add_argument(
"-W",
Expand Down
5 changes: 5 additions & 0 deletions ansible_rulebook/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,8 @@ class WebSocketExchangeException(Exception):
class UnsupportedActionException(Exception):

pass


class InventoryNotFound(Exception):

pass
34 changes: 20 additions & 14 deletions ansible_rulebook/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
from packaging.version import InvalidVersion

from ansible_rulebook.conf import settings
from ansible_rulebook.exception import InvalidFilterNameException
from ansible_rulebook.exception import (
InvalidFilterNameException,
InventoryNotFound,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -81,24 +84,14 @@ def substitute_variables(
return value


def load_inventory(inventory_spec: str) -> Any:
if os.path.exists(inventory_spec):
with open(inventory_spec) as f:
inventory_data = f.read()
return inventory_data
return inventory_spec


def collect_ansible_facts(inventory: str) -> List[Dict]:
hosts_facts = []
with tempfile.TemporaryDirectory(
prefix="gather_facts"
) as private_data_dir:
os.mkdir(os.path.join(private_data_dir, "inventory"))
with open(
os.path.join(private_data_dir, "inventory", "hosts"), "w"
) as f:
f.write(inventory)
inventory_dir = os.path.join(private_data_dir, "inventory")
os.mkdir(inventory_dir)
create_inventory(inventory_dir, inventory)

r = ansible_runner.run(
private_data_dir=private_data_dir,
Expand Down Expand Up @@ -244,6 +237,19 @@ async def send_session_stats(event_log: asyncio.Queue, stats: Dict):
)


def create_inventory(runner_inventory_dir: str, inventory: str) -> None:
if os.path.isfile(inventory):
shutil.copy(os.path.abspath(inventory), runner_inventory_dir)
elif os.path.exists(inventory):
shutil.copytree(
os.path.abspath(inventory),
runner_inventory_dir,
dirs_exist_ok=True,
)
else:
raise InventoryNotFound(f"Inventory {inventory} not found")


def _builtin_filter_path(name: str) -> Tuple[bool, str]:
if not name.startswith(EDA_BUILTIN_FILTER_PREFIX):
return False, ""
Expand Down
2 changes: 1 addition & 1 deletion docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The `ansible-rulebook` CLI supports the following options:
-S SOURCE_DIR, --source-dir SOURCE_DIR
Source dir
-i INVENTORY, --inventory INVENTORY
Inventory
Inventory can be a file or a directory
-W WEBSOCKET_URL, --websocket-url WEBSOCKET_ADDRESS
Connect the event log to a websocket
--websocket-ssl-verify How to verify the wss connection
Expand Down
6 changes: 1 addition & 5 deletions tests/e2e/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Module with tests for operators
"""
import logging
import pprint
import re
import subprocess

Expand Down Expand Up @@ -55,9 +54,6 @@ def test_actions_sanity(update_environment):
"DEFAULT_STARTUP_DELAY",
)

with open(inventory) as f:
inventory_data = pprint.pformat(f.read())

LOGGER.info(f"Running command: {cmd}")
result = subprocess.run(
cmd,
Expand Down Expand Up @@ -100,7 +96,7 @@ def test_actions_sanity(update_environment):
with check:
expected_debug_lines = [
"'hosts': ['all']",
f"'inventory': {inventory_data}",
f"'inventory': '{inventory}'",
"'project_data_file': None,",
"'ruleset': 'Test actions sanity'",
"'source_rule_name': 'debug',",
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
id="successful_rc",
),
pytest.param(
"hello_events_with_var.yml",
"actions/test_run_playbook.yml",
Path("nonexistent.yml"),
"0.1",
1,
Expand Down
69 changes: 38 additions & 31 deletions tests/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import asyncio
import os
import tempfile
from pprint import pprint
from unittest.mock import patch

Expand All @@ -33,7 +34,6 @@
from ansible_rulebook.messages import Shutdown
from ansible_rulebook.rule_types import EventSource, EventSourceFilter
from ansible_rulebook.rules_parser import parse_rule_sets
from ansible_rulebook.util import load_inventory
from ansible_rulebook.validators import Validate


Expand Down Expand Up @@ -250,7 +250,7 @@ async def test_run_rulesets():
event_log,
ruleset_queues,
dict(),
load_inventory("playbooks/inventory.yml"),
"playbooks/inventory.yml",
)

checks = {
Expand Down Expand Up @@ -319,7 +319,9 @@ async def test_run_rules_simple():
queue.put_nowait(dict(i=2))
queue.put_nowait(Shutdown())

await run_rulesets(event_log, ruleset_queues, dict(), "localhost")
await run_rulesets(
event_log, ruleset_queues, dict(), "playbooks/inventory.yml"
)

assert event_log.get_nowait()["type"] == "Action", "0"
assert event_log.get_nowait()["type"] == "Action", "0.2"
Expand Down Expand Up @@ -358,7 +360,7 @@ async def test_run_multiple_hosts():
event_log,
ruleset_queues,
dict(),
load_inventory("playbooks/inventory1.yml"),
"playbooks/inventory1.yml",
)

checks = {
Expand Down Expand Up @@ -390,7 +392,7 @@ async def test_run_multiple_hosts2():
event_log,
ruleset_queues,
dict(),
load_inventory("playbooks/inventory1.yml"),
"playbooks/inventory1.yml",
)

assert event_log.get_nowait()["type"] == "Action", "1"
Expand All @@ -417,7 +419,7 @@ async def test_run_multiple_hosts3():
event_log,
ruleset_queues,
dict(),
load_inventory("playbooks/inventory.yml"),
"playbooks/inventory.yml",
)

assert event_log.get_nowait()["type"] == "Action", "1"
Expand All @@ -435,7 +437,9 @@ async def test_filters():
queue.put_nowait(dict(i=2))
queue.put_nowait(Shutdown())

await run_rulesets(event_log, ruleset_queues, dict(), "localhost")
await run_rulesets(
event_log, ruleset_queues, dict(), "playbooks/inventory.yml"
)

assert event_log.get_nowait()["type"] == "Action", "0"
assert event_log.get_nowait()["type"] == "Action", "0.2"
Expand Down Expand Up @@ -472,7 +476,7 @@ async def test_run_rulesets_on_hosts():
event_log,
ruleset_queues,
dict(),
load_inventory("playbooks/inventory1.yml"),
"playbooks/inventory1.yml",
)

checks = {
Expand All @@ -491,30 +495,33 @@ async def test_run_assert_facts():
inventory = dict(
all=dict(hosts=dict(localhost=dict(ansible_connection="local")))
)
queue = ruleset_queues[0][1]
queue.put_nowait(dict())
queue.put_nowait(dict(i=1, meta=dict(hosts="localhost")))
queue.put_nowait(Shutdown())
await run_rulesets(
event_log,
ruleset_queues,
dict(Naboo="naboo"),
yaml.dump(inventory),
)
with tempfile.NamedTemporaryFile(mode="w+") as temp:
temp.write(yaml.dump(inventory))
temp.flush()
queue = ruleset_queues[0][1]
queue.put_nowait(dict())
queue.put_nowait(dict(i=1, meta=dict(hosts="localhost")))
queue.put_nowait(Shutdown())
await run_rulesets(
event_log,
ruleset_queues,
dict(Naboo="naboo"),
temp.name,
)

assert event_log.get_nowait()["type"] == "EmptyEvent", "0"
assert event_log.get_nowait()["type"] == "Action", "0.1"
assert event_log.get_nowait()["type"] == "Job", "1.0"
for i in range(47):
assert event_log.get_nowait()["type"] == "AnsibleEvent", f"1.{i}"

event = event_log.get_nowait()
assert event["type"] == "Action", "2.1"
assert event["action"] == "run_playbook", "2.2"
assert event["rc"] == 0, "2.3"
assert event["status"] == "successful", "2.4"
assert event_log.get_nowait()["type"] == "Shutdown", "4"
assert event_log.empty()
assert event_log.get_nowait()["type"] == "EmptyEvent", "0"
assert event_log.get_nowait()["type"] == "Action", "0.1"
assert event_log.get_nowait()["type"] == "Job", "1.0"
for i in range(47):
assert event_log.get_nowait()["type"] == "AnsibleEvent", f"1.{i}"

event = event_log.get_nowait()
assert event["type"] == "Action", "2.1"
assert event["action"] == "run_playbook", "2.2"
assert event["rc"] == 0, "2.3"
assert event["status"] == "successful", "2.4"
assert event_log.get_nowait()["type"] == "Shutdown", "4"
assert event_log.empty()


@pytest.mark.asyncio
Expand Down
Loading

0 comments on commit c4edbf5

Please sign in to comment.