Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex-Izquierdo authored Aug 2, 2023
2 parents 6972c6e + 27269d1 commit 118d3eb
Show file tree
Hide file tree
Showing 27 changed files with 513 additions and 328 deletions.
12 changes: 8 additions & 4 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
name: Integration tests

name: Run integration tests
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

# Run the integration tests every 8 hours.
# This will help to identify faster if
# there is a CI failure related to a
# change in any dependency.
schedule:
- cron: '0 */8 * * *'
jobs:
integration:
runs-on: ubuntu-latest
Expand All @@ -22,7 +26,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.8"
python-version: "3.9"

- name: Install package dependencies
run: |
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
---
on: [push, pull_request]
name: Linters
name: Run linters
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
# Run the linters tests every 8 hours.
# This will help to identify faster if
# there is a CI failure related to a
# change in any dependency.
schedule:
- cron: '0 */8 * * *'
jobs:
black:
runs-on: ubuntu-latest
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
name: Run Ansible tests

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

# Run the unit tests every 8 hours.
# This will help to identify faster if
# there is a CI failure related to a
# change in any dependency.
schedule:
- cron: '0 */8 * * *'
jobs:
tests:
runs-on: ubuntu-latest
Expand All @@ -15,7 +19,6 @@ jobs:
python-version:
- "3.10"
- "3.9"
- "3.8"

defaults:
run:
Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# this template can be adjusted as needed for your CI environment. the paths in the `commands` of each tox env linter can be changed if the tox file sits in a different location than expected in this template
# Recommended usage of this file is detailed in https://github.com/ansible/eda-partner-testing/blob/main/README.md.
# The linter paths can be changed, but may result in false passes.
# {posargs} in this case would be the path to collection root relative from the .github/workflows dir (`../..`)

[tox]
envlist = ruff, darglint, pylint-event-source, pylint-event-filter
# envlist = ruff, darglint, pylint-event-source, pylint-event-filter
envlist = ruff
requires =
ruff
darglint
Expand All @@ -15,9 +19,8 @@ commands = ruff check --select ALL --ignore INP001 -q {posargs}/extensions/eda/p
deps = darglint
commands = darglint -s numpy -z full {posargs}/extensions/eda/plugins

# depending on what kind of plugins you have, remove the line you don't need (i.e event_sources or event_filters, remove the pylint call for the other one)
# depending on how your collection and repo is structured, you may need to change the path to each type of plugin
# if pylint warns about missing __init__.py files in directories, there's no need to include them if you ensure that the paths in the below pylint `commands` point directly to the *.py files under the event_source/ and event_filter/ dirs, as shown in the template path here

# If you dont have any event_source or event_filter plugins, remove the corresponding testenv
[testenv:pylint-event-source]
deps = pylint
commands = pylint {posargs}/extensions/eda/plugins/event_source/*.py --output-format=parseable -sn --disable R0801
Expand Down
24 changes: 24 additions & 0 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: Tox
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
# Run the tox tests every 8 hours.
# This will help to identify faster if
# there is a CI failure related to a
# change in any dependency.
schedule:
- cron: '0 */8 * * *'
jobs:
tox:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install deps
run: python -m pip install tox
- name: Move to tox conf file and run tox
run: |
cd .github/workflows
python -m tox -- ../..
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

This collection contains event source plugins, event filters and example rulebooks to be used with [ansible-rulebook](https://ansible-rulebook.readthedocs.io/en/stable/).

<p style="text-align: center" align="center">
<a href="https://github.com/ansible/event-driven-ansible/actions?workflow=integration-tests"><img height="20px" src="https://github.com/ansible/event-driven-ansible/actions/workflows/integration-tests.yml/badge.svg?event=schedule"/> </a>
<a href="https://github.com/ansible/event-driven-ansible/actions?workflow=linters"><img height="20px" src="https://github.com/ansible/event-driven-ansible/actions/workflows/linters.yml/badge.svg?event=schedule"/> </a>
<a href="https://github.com/ansible/event-driven-ansible/actions?workflow=tests"><img height="20px" src="https://github.com/ansible/event-driven-ansible/actions/workflows/tests.yml/badge.svg?event=schedule"/> </a>
<a href="https://github.com/ansible/event-driven-ansible/actions?workflow=tox"><img height="20px" src="https://github.com/ansible/event-driven-ansible/actions/workflows/tox.yml/badge.svg?event=schedule"/> </a>
</p>

## Install

Install the ansible.eda collection with the Ansible Galaxy CLI:
Expand Down
14 changes: 7 additions & 7 deletions extensions/eda/plugins/event_filter/dashes_to_underscores.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
"""
dashes_to_underscores.py:
"""dashes_to_underscores.py.
An event filter that changes dashes in keys to underscores.
For instance, the key X-Y becomes the new key X_Y.
Arguments:
---------
* overwrite: Overwrite the values if there is a collision with a new key.
"""

import multiprocessing as mp


def main(event, overwrite=True):
def main(event: dict, overwrite: bool = True) -> dict: # noqa: FBT001, FBT002
"""Change dashes in keys to underscores."""
logger = mp.get_logger()
logger.info("dashes_to_underscores")
q = []
Expand All @@ -24,10 +27,7 @@ def main(event, overwrite=True):
if "-" in key:
new_key = key.replace("-", "_")
del o[key]
if new_key in o and overwrite:
o[new_key] = value
logger.info("Replacing %s with %s", key, new_key)
elif new_key not in o:
if (new_key in o and overwrite) or (new_key not in o):
o[new_key] = value
logger.info("Replacing %s with %s", key, new_key)

Expand Down
26 changes: 16 additions & 10 deletions extensions/eda/plugins/event_filter/insert_hosts_to_meta.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""
insert_hosts_to_meta.py
"""insert_hosts_to_meta.py.
An ansible-rulebook event filter that extract hosts from the event data and
insert them to the meta dict. Ansible-rulebook will limit an ansible action
running on hosts in the meta dict.
Arguments:
---------
host_path: The json path inside the event data to find hosts.
Do nothing if the key is not present or does exist in event
path_separator: The separator to interpret host_path. Default to "."
Expand All @@ -16,24 +16,28 @@
parameter is not present.
Example:
-------
- ansible.eda.insert_hosts_to_meta
host_path: app.target
path_separator: .
host_separator: ;
"""

from typing import Any, Dict
from __future__ import annotations

from typing import Any

import dpath


def main(
event: Dict[str, Any],
host_path: str = None,
host_separator: str = None,
event: dict[str, Any],
host_path: str | None = None,
host_separator: str | None = None,
path_separator: str = ".",
) -> Dict[str, Any]:
) -> dict[str, Any]:
"""Extract hosts from event data and insert into meta dict."""
if not host_path:
return event

Expand All @@ -45,12 +49,14 @@ def main(

if isinstance(hosts, str):
hosts = hosts.split(host_separator) if host_separator else [hosts]
elif isinstance(hosts, list) or isinstance(hosts, tuple):
elif isinstance(hosts, (list, tuple)): # noqa: UP038
for h in hosts:
if not isinstance(h, str):
raise TypeError(f"{h} is not a valid hostname")
msg = f"{h} is not a valid hostname"
raise TypeError(msg)
else:
raise TypeError(f"{hosts} is not a valid hostname")
msg = f"{hosts} is not a valid hostname"
raise TypeError(msg)

if "meta" not in event:
event["meta"] = {}
Expand Down
36 changes: 17 additions & 19 deletions extensions/eda/plugins/event_filter/json_filter.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
"""
json_filter.py: An event filter that filters keys out of events.
"""json_filter.py: An event filter that filters keys out of events.
Includes override excludes.
This is useful to exclude information from events that is unneeded by the rule
engine.
Arguments:
---------
* exclude_keys = a list of strings or patterns to remove
* include_keys = a list of strings or patterns to keep even if it matches
exclude_keys patterns.
"""

from __future__ import annotations

import fnmatch


def matches_include_keys(include_keys, s):
for pattern in include_keys:
if fnmatch.fnmatch(s, pattern):
return True
return False
def _matches_include_keys(include_keys: list, s: str) -> bool:
return any(fnmatch.fnmatch(s, pattern) for pattern in include_keys)


def matches_exclude_keys(exclude_keys, s):
for pattern in exclude_keys:
if fnmatch.fnmatch(s, pattern):
return True
return False
def _matches_exclude_keys(exclude_keys: list, s: str) -> bool:
return any(fnmatch.fnmatch(s, pattern) for pattern in exclude_keys)


def main(event, exclude_keys=None, include_keys=None):
def main(
event: dict,
exclude_keys: list | None = None,
include_keys: list | None = None,
) -> dict:
"""Filter keys out of events."""
if exclude_keys is None:
exclude_keys = []

Expand All @@ -42,13 +44,9 @@ def main(event, exclude_keys=None, include_keys=None):
o = q.pop()
if isinstance(o, dict):
for i in list(o.keys()):
if i in include_keys:
if (i in include_keys) or _matches_include_keys(include_keys, i):
q.append(o[i])
elif matches_include_keys(include_keys, i):
q.append(o[i])
elif i in exclude_keys:
del o[i]
elif matches_exclude_keys(exclude_keys, i):
elif (i in exclude_keys) or _matches_exclude_keys(exclude_keys, i):
del o[i]
else:
q.append(o[i])
Expand Down
7 changes: 3 additions & 4 deletions extensions/eda/plugins/event_filter/noop.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
noop.py: An event filter that does nothing to the input.
"""
"""noop.py: An event filter that does nothing to the input."""


def main(event):
def main(event: dict) -> dict:
"""Return the input."""
return event
29 changes: 20 additions & 9 deletions extensions/eda/plugins/event_filter/normalize_keys.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
"""
normalize_keys.py:
"""normalize_keys.py.
An event filter that changes keys that contain non alpha numeric or
underscore to undersocres.
For instance, the key server-name becomes the new key server_name
If there are consecutive non alpa numeric or under score, they would
be coalesced into a single underscore
For instance the key server.com/&abc becomes server_com_abc
instead of server_com__abc
instead of server_com__abc.
If there is a existing key with the normalized name, it will get overwritten
by default. If you don't want to over write it you can pass in overwrite: False
The default value of overwrite is True.
Arguments:
---------
* overwrite: Overwrite the values if there is a collision with a new key.
Usage in a rulebook, a filter is usually attached to a source in the rulebook:
Expand All @@ -36,34 +37,44 @@
"""

import logging
import multiprocessing as mp
import re

normalize_regex = re.compile("[^0-9a-zA-Z_]+")


def main(event, overwrite=True):
def main(event: dict, overwrite: bool = True) -> dict: # noqa: FBT001, FBT002
"""Change keys that contain non-alphanumeric characters to underscores."""
logger = mp.get_logger()
logger.info("normalize_keys")
return _normalize_embedded_keys(event, overwrite, logger)


def _normalize_embedded_keys(obj, overwrite, logger):
def _normalize_embedded_keys(
obj: dict,
overwrite: bool, # noqa: FBT001
logger: logging.Logger,
) -> dict:
if isinstance(obj, dict):
new_dict = dict()
new_dict = {}
original_keys = list(obj.keys())
for key in original_keys:
new_key = normalize_regex.sub("_", key)
if new_key == key or new_key not in original_keys:
new_dict[new_key] = _normalize_embedded_keys(
obj[key], overwrite, logger
obj[key],
overwrite,
logger,
)
elif new_key in original_keys and overwrite:
new_dict[new_key] = _normalize_embedded_keys(
obj[key], overwrite, logger
obj[key],
overwrite,
logger,
)
logger.warning("Replacing existing key %s", new_key)
return new_dict
elif isinstance(obj, list):
if isinstance(obj, list):
return [_normalize_embedded_keys(item, overwrite, logger) for item in obj]
return obj
Loading

0 comments on commit 118d3eb

Please sign in to comment.