Add custom Ansible filter plugin get_category_entries

This commit introduces a new Ansible filter plugin named
'get_category_entries', which returns all role names under the
roles/ directory that start with a given prefix.

Additionally, unit tests (unittest framework) have been added under
tests/unit/filterplugins/ to ensure correct behavior, including:

- Returns empty list when roles/ directory is missing
- Correctly filters and sorts by prefix
- Ignores non-directory entries
- Supports custom roles_path argument
- Returns all roles when prefix is empty

Reference: https://chatgpt.com/share/68a2f1ab-1fe8-800f-b22a-28c1c95802c2
This commit is contained in:
Kevin Veen-Birkenbach 2025-08-18 11:27:26 +02:00
parent a5941763ff
commit 29f50da226
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
2 changed files with 116 additions and 0 deletions

View File

@ -0,0 +1,31 @@
# Custom Ansible filter to get all role names under "roles/" with a given prefix.
import os
def get_category_entries(prefix, roles_path="roles"):
"""
Returns a list of role names under the given roles_path
that start with the specified prefix.
:param prefix: String prefix to match role names.
:param roles_path: Path to the roles directory (default: 'roles').
:return: List of matching role names.
"""
if not os.path.isdir(roles_path):
return []
roles = []
for entry in os.listdir(roles_path):
full_path = os.path.join(roles_path, entry)
if os.path.isdir(full_path) and entry.startswith(prefix):
roles.append(entry)
return sorted(roles)
class FilterModule(object):
""" Custom filters for Ansible """
def filters(self):
return {
"get_category_entries": get_category_entries
}

View File

@ -0,0 +1,85 @@
# Unit tests for the get_category_entries Ansible filter plugin (unittest version).
import os
import unittest
import tempfile
import shutil
from pathlib import Path
from filter_plugins.get_category_entries import get_category_entries
class TestGetCategoryEntries(unittest.TestCase):
def setUp(self):
# Create an isolated temp directory for each test
self._tmpdir = tempfile.TemporaryDirectory()
self.tmp = Path(self._tmpdir.name)
def tearDown(self):
# Clean up the temp directory
self._tmpdir.cleanup()
def test_returns_empty_when_roles_dir_missing(self):
"""If the roles directory does not exist, the filter must return an empty list."""
missing_dir = self.tmp / "no_such_roles_dir"
self.assertFalse(missing_dir.exists())
self.assertEqual(get_category_entries("docker-", roles_path=str(missing_dir)), [])
def test_matches_prefix_and_sorts(self):
"""
The filter should return only directory names starting with the prefix,
and the result must be sorted.
"""
roles_dir = self.tmp / "roles"
roles_dir.mkdir()
# Create role directories
(roles_dir / "docker-nginx").mkdir()
(roles_dir / "docker-postgres").mkdir()
(roles_dir / "web-app-keycloak").mkdir()
(roles_dir / "docker-redis").mkdir()
# A file that should be ignored
(roles_dir / "docker-file").write_text("not a directory")
result = get_category_entries("docker-", roles_path=str(roles_dir))
self.assertEqual(result, ["docker-nginx", "docker-postgres", "docker-redis"])
def test_ignores_non_directories(self):
"""Non-directory entries under roles/ must be ignored."""
roles_dir = self.tmp / "roles"
roles_dir.mkdir()
(roles_dir / "docker-engine").mkdir()
(roles_dir / "docker-engine.txt").write_text("file, should be ignored")
result = get_category_entries("docker-", roles_path=str(roles_dir))
self.assertEqual(result, ["docker-engine"])
def test_respects_custom_roles_path(self):
"""When roles_path is provided, the filter should use it instead of 'roles'."""
custom_roles = self.tmp / "custom" / "rolesdir"
custom_roles.mkdir(parents=True)
(custom_roles / "docker-a").mkdir()
(custom_roles / "docker-b").mkdir()
(custom_roles / "other-c").mkdir()
result = get_category_entries("docker-", roles_path=str(custom_roles))
self.assertEqual(result, ["docker-a", "docker-b"])
def test_empty_prefix_returns_all_roles_sorted(self):
"""If an empty prefix is passed, the filter should return all role directories (sorted)."""
roles_dir = self.tmp / "roles"
roles_dir.mkdir()
(roles_dir / "a-role").mkdir()
(roles_dir / "c-role").mkdir()
(roles_dir / "b-role").mkdir()
result = get_category_entries("", roles_path=str(roles_dir))
self.assertEqual(result, ["a-role", "b-role", "c-role"])
if __name__ == "__main__":
unittest.main()