Implemented filter functions to get roles by application_id

This commit is contained in:
Kevin Veen-Birkenbach 2025-07-09 14:52:51 +02:00
parent c9c73cbdb2
commit f3939661e4
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
6 changed files with 175 additions and 7 deletions

View File

@ -49,7 +49,7 @@ More informations about the features you will find [here](docs/overview/Features
### Use it online 🌐
Give CyMaIS a spin at cymais.cloud sign up in seconds, click around, and see how easy infra magic can be! 🚀🔧✨
Give CyMaIS a spin at [CyMaIS.cloud](httpy://cymais.cloud) sign up in seconds, click around, and see how easy infra magic can be! 🚀🔧✨
### Install locally 💻
1. **Install CyMaIS** via [Kevin's Package Manager](https://github.com/kevinveenbirkenbach/package-manager)

View File

@ -0,0 +1,88 @@
# filter_plugins/role_path_by_app_id.py
import os
import glob
import yaml
from ansible.errors import AnsibleFilterError
def abs_role_path_by_application_id(application_id):
"""
Searches all roles/*/vars/main.yml for application_id and returns
the absolute path of the role that matches. Raises an error if
zero or more than one match is found.
"""
base_dir = os.getcwd()
pattern = os.path.join(base_dir, 'roles', '*', 'vars', 'main.yml')
matches = []
for filepath in glob.glob(pattern):
try:
with open(filepath, 'r') as f:
data = yaml.safe_load(f) or {}
except Exception:
continue
if data.get('application_id') == application_id:
role_dir = os.path.dirname(os.path.dirname(filepath))
abs_path = os.path.abspath(role_dir)
matches.append(abs_path)
if len(matches) > 1:
raise AnsibleFilterError(
f"Multiple roles found with application_id='{application_id}': {matches}. "
"The application_id must be unique."
)
if not matches:
raise AnsibleFilterError(
f"No role found with application_id='{application_id}'."
)
return matches[0]
def rel_role_path_by_application_id(application_id):
"""
Searches all roles/*/vars/main.yml for application_id and returns
the relative path (from the project root) of the role that matches.
Raises an error if zero or more than one match is found.
"""
base_dir = os.getcwd()
pattern = os.path.join(base_dir, 'roles', '*', 'vars', 'main.yml')
matches = []
for filepath in glob.glob(pattern):
try:
with open(filepath, 'r') as f:
data = yaml.safe_load(f) or {}
except Exception:
continue
if data.get('application_id') == application_id:
role_dir = os.path.dirname(os.path.dirname(filepath))
rel_path = os.path.relpath(role_dir, base_dir)
matches.append(rel_path)
if len(matches) > 1:
raise AnsibleFilterError(
f"Multiple roles found with application_id='{application_id}': {matches}. "
"The application_id must be unique."
)
if not matches:
raise AnsibleFilterError(
f"No role found with application_id='{application_id}'."
)
return matches[0]
class FilterModule(object):
"""
Provides the filters `abs_role_path_by_application_id` and
`rel_role_path_by_application_id`.
"""
def filters(self):
return {
'abs_role_path_by_application_id': abs_role_path_by_application_id,
'rel_role_path_by_application_id': rel_role_path_by_application_id,
}

View File

@ -3,8 +3,8 @@
src: "{{ item }}"
dest: "{{ docker_compose.files.dockerfile }}"
loop:
- "{{ playbook_dir }}/roles/web-app-{{ application_id }}/templates/Dockerfile.j2"
- "{{ playbook_dir }}/roles/web-app-{{ application_id }}/files/Dockerfile"
- "{{ application_id | abs_role_path_by_application_id }}/templates/Dockerfile.j2"
- "{{ application_id | abs_role_path_by_application_id }}/files/Dockerfile"
notify: docker compose up
register: create_dockerfile_result
failed_when:
@ -20,8 +20,8 @@
notify: docker compose up
register: env_template
loop:
- "{{ playbook_dir }}/roles/web-app-{{ application_id }}/templates/env.j2"
- "{{ playbook_dir }}/roles/web-app-{{ application_id }}/files/env"
- "{{ application_id | abs_role_path_by_application_id }}/templates/env.j2"
- "{{ application_id | abs_role_path_by_application_id }}/files/env"
failed_when:
- env_template is failed
- "'Could not find or access' not in env_template.msg"

View File

@ -1 +1 @@
modifier_javascript_template_file: "{{ playbook_dir }}/roles/web-app-{{ application_id }}/templates/javascript.js.j2"
modifier_javascript_template_file: "{{ application_id | abs_role_path_by_application_id }}/templates/javascript.js.j2"

View File

@ -15,7 +15,7 @@ function getExportName(slug) {
// Root: redirect to your documentation
app.get('/', (req, res) => {
res.redirect('{{ domains | get_url('sphinx', web_protocol) }}/roles/web-app-{{ application_id }}/README.html');
res.redirect('{{ domains | get_url('sphinx', web_protocol) }}/{{ application_id | rel_role_path_by_application_id}}/README.html');
});
// GET /:slug.svg

View File

@ -0,0 +1,80 @@
import os
import tempfile
import shutil
import yaml
import unittest
from ansible.errors import AnsibleFilterError
from filter_plugins.role_path_by_app_id import (
abs_role_path_by_application_id,
rel_role_path_by_application_id,
)
def write_vars_file(base_dir, role_name, app_id):
"""
Helper to create roles/<role_name>/vars/main.yml with application_id
"""
role_vars_dir = os.path.join(base_dir, 'roles', role_name, 'vars')
os.makedirs(role_vars_dir, exist_ok=True)
file_path = os.path.join(role_vars_dir, 'main.yml')
with open(file_path, 'w') as f:
yaml.safe_dump({'application_id': app_id}, f)
return file_path
class TestRolePathByApplicationId(unittest.TestCase):
def setUp(self):
# Create temporary directory for each test and switch cwd
self.tmp_dir = tempfile.mkdtemp()
self.prev_cwd = os.getcwd()
os.chdir(self.tmp_dir)
def tearDown(self):
# Restore cwd and clean up
os.chdir(self.prev_cwd)
shutil.rmtree(self.tmp_dir)
def test_abs_single_match(self):
write_vars_file(self.tmp_dir, 'role_one', 'app123')
write_vars_file(self.tmp_dir, 'role_two', 'other_id')
result = abs_role_path_by_application_id('app123')
expected = os.path.abspath(os.path.join(self.tmp_dir, 'roles', 'role_one'))
self.assertEqual(result, expected)
def test_rel_single_match(self):
write_vars_file(self.tmp_dir, 'role_one', 'app123')
write_vars_file(self.tmp_dir, 'role_two', 'other_id')
result = rel_role_path_by_application_id('app123')
expected = os.path.relpath(os.path.join(self.tmp_dir, 'roles', 'role_one'), self.tmp_dir)
self.assertEqual(result, expected)
def test_abs_no_match(self):
write_vars_file(self.tmp_dir, 'role_one', 'app123')
with self.assertRaises(AnsibleFilterError) as cm:
abs_role_path_by_application_id('nonexistent')
self.assertIn("No role found with application_id='nonexistent'", str(cm.exception))
def test_rel_no_match(self):
write_vars_file(self.tmp_dir, 'role_one', 'app123')
with self.assertRaises(AnsibleFilterError) as cm:
rel_role_path_by_application_id('nonexistent')
self.assertIn("No role found with application_id='nonexistent'", str(cm.exception))
def test_abs_multiple_match(self):
write_vars_file(self.tmp_dir, 'role_one', 'dup_id')
write_vars_file(self.tmp_dir, 'role_two', 'dup_id')
with self.assertRaises(AnsibleFilterError) as cm:
abs_role_path_by_application_id('dup_id')
self.assertIn("Multiple roles found with application_id='dup_id'", str(cm.exception))
def test_rel_multiple_match(self):
write_vars_file(self.tmp_dir, 'role_one', 'dup_id')
write_vars_file(self.tmp_dir, 'role_two', 'dup_id')
with self.assertRaises(AnsibleFilterError) as cm:
rel_role_path_by_application_id('dup_id')
self.assertIn("Multiple roles found with application_id='dup_id'", str(cm.exception))
if __name__ == '__main__':
unittest.main()