From 099beda22d0c133aade6da19ed55d55778388435 Mon Sep 17 00:00:00 2001 From: Madhu Kanoor Date: Fri, 12 Jan 2024 11:40:31 -0500 Subject: [PATCH] feat: skip-audit-events option (#642) When running source activation in production mode we want to have an option to skip sending of audit events to the server In test mode for source activation like regular activation we need to be able to send audit events https://issues.redhat.com/browse/AAP-19479 --- CHANGELOG.md | 1 + ansible_rulebook/action/helper.py | 4 ++ ansible_rulebook/cli.py | 7 +++ ansible_rulebook/conf.py | 1 + docs/usage.rst | 3 +- tests/e2e/test_skip_audit_events.py | 82 +++++++++++++++++++++++++++++ tests/e2e/utils.py | 3 ++ 7 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/test_skip_audit_events.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9b0125..e011f2a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Support for standalone boolean in conditions - Add basic auth to controller - Use token for websocket authentication +- skip-audit-events to disable sending audit events to server ### Changed - Generic print as well as printing of events use new banner style diff --git a/ansible_rulebook/action/helper.py b/ansible_rulebook/action/helper.py index 9c2d9902..de198b8d 100644 --- a/ansible_rulebook/action/helper.py +++ b/ansible_rulebook/action/helper.py @@ -71,6 +71,8 @@ def __init__(self, metadata: Metadata, control: Control, action: str): async def send_status(self, data: Dict, obj_type: str = "Action") -> None: """Send Action status information on the queue""" + if settings.skip_audit_events: + return payload = { "type": obj_type, "action": self.action, @@ -88,6 +90,8 @@ async def send_status(self, data: Dict, obj_type: str = "Action") -> None: async def send_default_status(self): """Send default action status information on the queue""" + if settings.skip_audit_events: + return await self.send_status( { "run_at": run_at(), diff --git a/ansible_rulebook/cli.py b/ansible_rulebook/cli.py index db16b107..1b623ede 100644 --- a/ansible_rulebook/cli.py +++ b/ansible_rulebook/cli.py @@ -213,6 +213,12 @@ def get_parser() -> argparse.ArgumentParser: default="false", action="store_true", ) + parser.add_argument( + "--skip-audit-events", + action="store_true", + default=settings.skip_audit_events, + help="Don't send audit events to the server", + ) return parser @@ -274,6 +280,7 @@ def update_settings(args: argparse.Namespace) -> None: settings.websocket_token_url = args.websocket_token_url settings.websocket_access_token = args.websocket_access_token settings.websocket_refresh_token = args.websocket_refresh_token + settings.skip_audit_events = args.skip_audit_events def main(args: List[str] = None) -> int: diff --git a/ansible_rulebook/conf.py b/ansible_rulebook/conf.py index 15c04110..1429be37 100644 --- a/ansible_rulebook/conf.py +++ b/ansible_rulebook/conf.py @@ -27,6 +27,7 @@ def __init__(self): self.websocket_token_url = None self.websocket_access_token = None self.websocket_refresh_token = None + self.skip_audit_events = False settings = _Settings() diff --git a/docs/usage.rst b/docs/usage.rst index 32db0a5e..e757f26f 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -9,7 +9,7 @@ The `ansible-rulebook` CLI supports the following options: usage: ansible-rulebook [-h] [-r RULEBOOK] [-e VARS] [-E ENV_VARS] [-v] [--version] [-S SOURCE_DIR] [-i INVENTORY] [-W WEBSOCKET_URL] [--websocket-ssl-verify WEBSOCKET_SSL_VERIFY] [--websocket-token-url WEBSOCKET_TOKEN_URL] [--websocket-access-token WEBSOCKET_ACCESS_TOKEN] [--websocket-refresh-token WEBSOCKET_REFRESH_TOKEN] [--id ID] [-w] [-T PROJECT_TARBALL] [--controller-url CONTROLLER_URL] [--controller-token CONTROLLER_TOKEN] [--controller-ssl-verify CONTROLLER_SSL_VERIFY] - [--print-events] [--heartbeat n] [--execution-strategy sequential|parallel] + [--print-events] [--heartbeat n] [--execution-strategy sequential|parallel] [--skip-audit-events] optional arguments: -h, --help show this help message and exit @@ -51,6 +51,7 @@ The `ansible-rulebook` CLI supports the following options: --heartbeat Send heartbeat to the server after every n seconds. Default is 0, no heartbeat is sent --execution-strategy sequential|parallel. The default execution strategy is sequential. + --skip-audit-events Skip sending audit events to the EDA server, default is false To get help from `ansible-rulebook` run the following: diff --git a/tests/e2e/test_skip_audit_events.py b/tests/e2e/test_skip_audit_events.py new file mode 100644 index 00000000..1d511b52 --- /dev/null +++ b/tests/e2e/test_skip_audit_events.py @@ -0,0 +1,82 @@ +""" +Module with tests for websockets +""" +import asyncio +import logging +from functools import partial + +import pytest +import websockets.server as ws_server + +from . import utils + +LOGGER = logging.getLogger(__name__) +DEFAULT_TIMEOUT = 15 + + +@pytest.mark.e2e +@pytest.mark.asyncio +async def test_skip_audit_events(): + """ + Verify that ansible-rulebook can skip sending + audit events but still keep sending heartbeat + data + """ + # variables + host = "127.0.0.1" + endpoint = "/api/ws2" + proc_id = "42" + port = 31415 + rulebook = utils.BASE_DATA_PATH / "rulebooks/test_match_multiple_rules.yml" + websocket_address = f"ws://127.0.0.1:{port}{endpoint}" + cmd = utils.Command( + rulebook=rulebook, + websocket=websocket_address, + proc_id=proc_id, + heartbeat=2, + skip_audit_events=True, + ) + + # run server and ansible-rulebook + queue = asyncio.Queue() + handler = partial(utils.msg_handler, queue=queue) + async with ws_server.serve(handler, host, port): + LOGGER.info(f"Running command: {cmd}") + proc = await asyncio.create_subprocess_shell( + str(cmd), + cwd=utils.BASE_DATA_PATH, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + await asyncio.wait_for(proc.wait(), timeout=DEFAULT_TIMEOUT) + assert proc.returncode == 0 + + # Verify data + assert not queue.empty() + + action_counter = 0 + session_stats_counter = 0 + stats = None + while not queue.empty(): + data = await queue.get() + assert data["path"] == endpoint + data = data["payload"] + + if data["type"] == "Action": + action_counter += 1 + + if data["type"] == "SessionStats": + session_stats_counter += 1 + stats = data["stats"] + assert stats["ruleSetName"] == "Test match multiple rules" + assert stats["numberOfRules"] == 2 + assert stats["numberOfDisabledRules"] == 0 + assert data["activation_instance_id"] == proc_id + + assert stats["rulesTriggered"] == 2 + assert stats["eventsProcessed"] == 5 + assert stats["eventsMatched"] == 1 + + assert session_stats_counter >= 2 + assert action_counter == 0 diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index 0b28ade2..767a35ad 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -42,6 +42,7 @@ class Command: heartbeat: int = 0 execution_strategy: Optional[str] = None hot_reload: bool = False + skip_audit_events: bool = False def __post_init__(self): # verbosity overrides verbose and debug @@ -96,6 +97,8 @@ def to_list(self) -> List: result.extend(["--execution-strategy", self.execution_strategy]) if self.hot_reload: result.append("--hot-reload") + if self.skip_audit_events: + result.append("--skip-audit-events") return result