diff --git a/roles/srv-web-proxy-domain/tasks/main.yml b/roles/srv-web-proxy-domain/tasks/main.yml index e307e328..472a70ec 100644 --- a/roles/srv-web-proxy-domain/tasks/main.yml +++ b/roles/srv-web-proxy-domain/tasks/main.yml @@ -2,12 +2,30 @@ include_role: name: srv-web-composer -- name: "copy nginx domain configuration to {{ configuration_destination }}" +- name: "Copy nginx config to {{ configuration_destination }}" template: - src: "{{ vhost_template_src }}" + src: "{{ vhost_template_src }}" dest: "{{ configuration_destination }}" + register: nginx_conf notify: restart nginx +- name: "Check if {{ domains | get_domain(application_id) }} is reachable (only if config unchanged)" + uri: + url: "{{ domains | get_url(application_id, web_protocol) }}" + register: site_check + failed_when: false + changed_when: false + when: not nginx_conf.changed + +- name: Restart nginx if site is down + command: + cmd: "true" + notify: restart nginx + when: + - not nginx_conf.changed + - site_check.status is defined + - not site_check.status in [200,301,302] + - name: "set oauth2_proxy_application_id (Needed due to lazzy loading issue)" set_fact: oauth2_proxy_application_id: "{{ application_id }}" diff --git a/roles/web-app-pgadmin/tasks/main.yml b/roles/web-app-pgadmin/tasks/main.yml index ddade973..95caa68a 100644 --- a/roles/web-app-pgadmin/tasks/main.yml +++ b/roles/web-app-pgadmin/tasks/main.yml @@ -11,5 +11,5 @@ http_port: "{{ ports.localhost.http[application_id] }}" - name: "configure pgadmin servers" - include_tasks: config/main.yml + include_tasks: configuration.yml when: applications[application_id].server_mode | bool \ No newline at end of file diff --git a/tests/integration/test_include_import_tasks_existence.py b/tests/integration/test_include_import_tasks_existence.py new file mode 100644 index 00000000..1ab3aa88 --- /dev/null +++ b/tests/integration/test_include_import_tasks_existence.py @@ -0,0 +1,102 @@ +import unittest +import os +import glob +import yaml +import re + +class TestIncludeImportTasksExistence(unittest.TestCase): + def setUp(self): + # Determine project root, roles directory and list of YAML files to scan + tests_dir = os.path.dirname(__file__) # .../tests/integration + project_root = os.path.abspath(os.path.join(tests_dir, os.pardir, os.pardir)) + self.project_root = project_root + self.roles_dir = os.path.join(project_root, 'roles') + + self.files_to_scan = [] + for filepath in glob.glob(os.path.join(project_root, '**', '*.yml'), recursive=True): + # Skip .git and tests directories + if '/.git/' in filepath or '/tests/' in filepath: + continue + self.files_to_scan.append(filepath) + + def _collect_task_includes(self, data): + """ + Recursively collect all file references from include_tasks and import_tasks. + Supports scalar syntax, block syntax ({ file: ... }), and block-list syntax. + """ + task_files = [] + if isinstance(data, dict): + for key, val in data.items(): + if key in ('include_tasks', 'import_tasks'): + # Scalar syntax: include_tasks: tasks.yml + if isinstance(val, str): + task_files.append(val) + # Block syntax: include_tasks: { file: tasks.yml } + elif isinstance(val, dict) and 'file' in val: + task_files.append(val['file']) + # Block-list syntax: + elif isinstance(val, list): + for item in val: + if isinstance(item, dict) and 'file' in item: + task_files.append(item['file']) + else: + task_files.extend(self._collect_task_includes(val)) + elif isinstance(data, list): + for item in data: + task_files.extend(self._collect_task_includes(item)) + return task_files + + def test_include_import_tasks_exist(self): + missing = [] + for file_path in self.files_to_scan: + with open(file_path) as f: + try: + documents = list(yaml.safe_load_all(f)) + except yaml.YAMLError: + self.fail(f"Failed to parse YAML in {file_path}") + + file_dir = os.path.dirname(file_path) + # Determine the role context if under roles/ + role_name = None + if self.roles_dir in file_dir: + parts = file_dir.split(os.sep) + idx = parts.index('roles') + if idx + 1 < len(parts): + role_name = parts[idx + 1] + role_path_dir = os.path.join(self.roles_dir, role_name) + + for doc in documents: + for task_ref in self._collect_task_includes(doc): + # Handle special Jinja2 vars + pattern_ref = task_ref + if '{{ role_path }}' in pattern_ref and role_name: + pattern_ref = pattern_ref.replace('{{ role_path }}', role_path_dir) + if '{{ playbook_dir }}' in pattern_ref: + pattern_ref = pattern_ref.replace('{{ playbook_dir }}', self.project_root) + # Replace other Jinja2 expressions with wildcard + pattern_ref = re.sub(r"\{\{.*?\}\}", '*', pattern_ref) + # If no extension, assume .yml + if not os.path.splitext(pattern_ref)[1]: + pattern_ref += '.yml' + + # Prepare search globs + local_glob = os.path.join(file_dir, pattern_ref) + global_glob = os.path.join(self.project_root, pattern_ref) + tasks_dir_glob = os.path.join(self.project_root, 'tasks', pattern_ref) + matches = [] + matches += [p for p in glob.glob(local_glob) if os.path.isfile(p)] + matches += [p for p in glob.glob(global_glob) if os.path.isfile(p)] + matches += [p for p in glob.glob(tasks_dir_glob) if os.path.isfile(p)] + + if not matches: + missing.append((file_path, task_ref)) + + if missing: + messages = [ + f"File '{fp}' references missing task file '{tr}'" + for fp, tr in missing + ] + self.fail("\n".join(messages)) + +if __name__ == '__main__': + unittest.main()