From dd10a99b1d5adb7d29e4e118683c2c125fa9fa77 Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Tue, 9 Jun 2026 15:39:12 -0400 Subject: [PATCH 1/2] Centralize database configuration into a single reusable list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a canonical `databases` list in database.yml with feature tags, replacing scattered conditional enumeration across consumers. A computed `all_databases` filters by enabled_features, and custom Jinja2 filters derive postgresql_databases/postgresql_users from it — eliminating static list duplication and the merge pre_tasks in deploy.yaml. Co-Authored-By: Claude Opus 4.6 --- .../playbooks/deploy-dev/deploy-dev.yaml | 4 - src/filter_plugins/foremanctl.py | 15 +++ src/playbooks/checks/checks.yaml | 5 +- src/playbooks/deploy/deploy.yaml | 16 +-- .../tasks/check.yaml | 18 +-- .../check_database_connection/tasks/main.yaml | 26 +--- src/roles/checks/defaults/main.yml | 2 + src/vars/database.yml | 119 ++++++++++++++++-- src/vars/database_iop.yml | 61 --------- 9 files changed, 141 insertions(+), 125 deletions(-) create mode 100644 src/roles/checks/defaults/main.yml delete mode 100644 src/vars/database_iop.yml diff --git a/development/playbooks/deploy-dev/deploy-dev.yaml b/development/playbooks/deploy-dev/deploy-dev.yaml index caf89c1d9..52e6a12e5 100644 --- a/development/playbooks/deploy-dev/deploy-dev.yaml +++ b/development/playbooks/deploy-dev/deploy-dev.yaml @@ -37,10 +37,6 @@ when: - "'iop' in enabled_features" block: - - name: Include iop databases - ansible.builtin.include_vars: - file: "../../../src/vars/database_iop.yml" - - name: Combine lists ansible.builtin.set_fact: postgresql_databases: "{{ postgresql_databases + iop_postgresql_databases }}" diff --git a/src/filter_plugins/foremanctl.py b/src/filter_plugins/foremanctl.py index 25c9ebc1c..aa135a9f6 100644 --- a/src/filter_plugins/foremanctl.py +++ b/src/filter_plugins/foremanctl.py @@ -115,6 +115,19 @@ def has_feature(features, feature): return feature in features or any(f.startswith(feature + '/') for f in features) +def to_postgresql_databases(databases): + return [{'name': db['database'], 'owner': db['user']} for db in databases] + + +def to_postgresql_users(databases): + seen = {} + for db in databases: + name = db['user'] + if name not in seen: + seen[name] = {'name': name, 'password': db['password']} + return list(seen.values()) + + class FilterModule(object): '''foremanctl filters''' @@ -128,4 +141,6 @@ def filters(self): 'list_all_features': list_all_features, 'invalid_features': invalid_features, 'has_feature': has_feature, + 'to_postgresql_databases': to_postgresql_databases, + 'to_postgresql_users': to_postgresql_users, } diff --git a/src/playbooks/checks/checks.yaml b/src/playbooks/checks/checks.yaml index 95863cc8e..0f126e611 100644 --- a/src/playbooks/checks/checks.yaml +++ b/src/playbooks/checks/checks.yaml @@ -4,6 +4,9 @@ gather_facts: true vars_files: - "../../vars/defaults.yml" + - "../../vars/flavors/{{ flavor }}.yml" - "../../vars/database.yml" roles: - - checks + - role: checks + vars: + checks_databases: "{{ all_databases }}" diff --git a/src/playbooks/deploy/deploy.yaml b/src/playbooks/deploy/deploy.yaml index 837d36c98..a70d57371 100644 --- a/src/playbooks/deploy/deploy.yaml +++ b/src/playbooks/deploy/deploy.yaml @@ -12,23 +12,11 @@ - "../../vars/database.yml" - "../../vars/foreman.yml" - "../../vars/base.yaml" - pre_tasks: - - name: Add iop databases - when: - - "'iop' in enabled_features" - - database_mode == 'internal' - block: - - name: Include iop databases - ansible.builtin.include_vars: - file: "../../vars/database_iop.yml" - - - name: Combine lists - ansible.builtin.set_fact: - postgresql_databases: "{{ postgresql_databases + iop_postgresql_databases }}" - postgresql_users: "{{ postgresql_users + iop_postgresql_users }}" roles: - role: pre_install - role: checks + vars: + checks_databases: "{{ all_databases }}" - role: certificates when: "certificates_source in ['default', 'custom_server']" - role: certificate_checks diff --git a/src/roles/check_database_connection/tasks/check.yaml b/src/roles/check_database_connection/tasks/check.yaml index 8df0196d4..facb79be9 100644 --- a/src/roles/check_database_connection/tasks/check.yaml +++ b/src/roles/check_database_connection/tasks/check.yaml @@ -1,7 +1,7 @@ - name: Store CA cert to a temporary file when: - - db_item.ca_cert is defined - - db_item.ca_cert is truthy + - db_item.ssl_ca is defined + - db_item.ssl_ca is truthy block: - name: Create temporary file ansible.builtin.tempfile: @@ -12,7 +12,7 @@ - name: Write CA cert to temporary file ansible.builtin.copy: dest: "{{ _check_database_connection_ca_cert.path }}" - src: "{{ db_item.ca_cert }}" + src: "{{ db_item.ssl_ca }}" mode: '0640' - name: Check database connectivity to {{ db_item.name }} @@ -20,27 +20,27 @@ login_host: "{{ db_item.host }}" login_user: "{{ db_item.user }}" login_password: "{{ db_item.password }}" - login_db: "{{ db_item.dbname }}" + login_db: "{{ db_item.database }}" ca_cert: "{{ _check_database_connection_ca_cert.path | default(omit) }}" - ssl_mode: "{{ db_item.sslmode | default(omit) }}" + ssl_mode: "{{ db_item.ssl_mode | default(omit) }}" register: check_database_connection_ping_result ignore_errors: true - name: Delete temporary CA cert file when: - - db_item.ca_cert is defined - - db_item.ca_cert is truthy + - db_item.ssl_ca is defined + - db_item.ssl_ca is truthy block: - name: Delete temporary file ansible.builtin.file: state: absent path: "{{ _check_database_connection_ca_cert.path }}" -- name: Assert database is reachable for {{ db_item.name }} +- name: Assert database is reachable for {{ db_item.name }} ansible.builtin.assert: that: - check_database_connection_ping_result.is_available fail_msg: > - Cannot connect to {{ db_item.name }} database '{{ db_item.dbname }}' at {{ db_item.host }}. + Cannot connect to {{ db_item.name }} database '{{ db_item.database }}' at {{ db_item.host }}. Please verify the database host, port, name, user, and password. Error: {{ check_database_connection_ping_result.conn_err_msg | default('No error message available.') }} diff --git a/src/roles/check_database_connection/tasks/main.yaml b/src/roles/check_database_connection/tasks/main.yaml index fa24e612e..b81bab9a1 100644 --- a/src/roles/check_database_connection/tasks/main.yaml +++ b/src/roles/check_database_connection/tasks/main.yaml @@ -2,30 +2,8 @@ - name: Check DB ansible.builtin.include_tasks: check.yaml no_log: true - loop: - - name: Foreman - host: "{{ foreman_database_host }}" - user: "{{ foreman_database_user }}" - password: "{{ foreman_database_password }}" - dbname: "{{ foreman_database_name }}" - ca_cert: "{{ foreman_database_ssl_ca | default('') }}" - sslmode: "{{ foreman_database_ssl_mode | default(omit) }}" - - - name: Candlepin - host: "{{ candlepin_database_host }}" - user: "{{ candlepin_database_user }}" - password: "{{ candlepin_database_password }}" - dbname: "{{ candlepin_database_name }}" - ca_cert: "{{ candlepin_database_ssl_ca | default('') }}" - sslmode: "{{ candlepin_database_ssl_mode | default(omit) }}" - - - name: Pulp - host: "{{ pulp_database_host }}" - user: "{{ pulp_database_user }}" - password: "{{ pulp_database_password }}" - dbname: "{{ pulp_database_name }}" - ca_cert: "{{ pulp_database_ssl_ca | default('') }}" - sslmode: "{{ pulp_database_ssl_mode | default(omit) }}" + loop: "{{ checks_databases }}" loop_control: loop_var: db_item + label: "{{ db_item.name }}" when: database_mode == 'external' diff --git a/src/roles/checks/defaults/main.yml b/src/roles/checks/defaults/main.yml new file mode 100644 index 000000000..bdebaac21 --- /dev/null +++ b/src/roles/checks/defaults/main.yml @@ -0,0 +1,2 @@ +--- +checks_databases: [] diff --git a/src/vars/database.yml b/src/vars/database.yml index 236e62421..e788fba22 100644 --- a/src/vars/database.yml +++ b/src/vars/database.yml @@ -35,17 +35,112 @@ foreman_database_port: "{{ database_port }}" foreman_database_ssl_mode: "{{ database_ssl_mode }}" foreman_database_ssl_ca: "{{ database_ssl_ca }}" -postgresql_databases: - - name: "{{ candlepin_database_name }}" - owner: "{{ candlepin_database_user }}" - - name: "{{ foreman_database_name }}" - owner: "{{ foreman_database_user }}" - - name: "{{ pulp_database_name }}" - owner: "{{ pulp_database_user }}" -postgresql_users: - - name: "{{ candlepin_database_user }}" - password: "{{ candlepin_database_password }}" - - name: "{{ foreman_database_user }}" +iop_database_host: host.containers.internal +iop_database_port: 5432 + +iop_inventory_database_host: "{{ iop_database_host }}" +iop_inventory_database_port: "{{ iop_database_port }}" +iop_inventory_database_name: inventory_db +iop_inventory_database_user: inventory_admin +iop_inventory_database_password_file: "{{ obsah_state_path }}/iop-inventory-db-password" +iop_inventory_database_password: "{{ lookup('ansible.builtin.password', iop_inventory_database_password_file, chars=['ascii_letters', 'digits']) }}" + +iop_advisor_database_host: "{{ iop_database_host }}" +iop_advisor_database_port: "{{ iop_database_port }}" +iop_advisor_database_name: advisor_db +iop_advisor_database_user: advisor_user +iop_advisor_database_password_file: "{{ obsah_state_path }}/iop-advisor-db-password" +iop_advisor_database_password: "{{ lookup('ansible.builtin.password', iop_advisor_database_password_file, chars=['ascii_letters', 'digits']) }}" + +iop_remediation_database_host: "{{ iop_database_host }}" +iop_remediation_database_port: "{{ iop_database_port }}" +iop_remediation_database_name: remediations_db +iop_remediation_database_user: remediations_user +iop_remediation_database_password_file: "{{ obsah_state_path }}/iop-remediation-db-password" +iop_remediation_database_password: "{{ lookup('ansible.builtin.password', iop_remediation_database_password_file, chars=['ascii_letters', 'digits']) }}" + +iop_vmaas_database_host: "{{ iop_database_host }}" +iop_vmaas_database_port: "{{ iop_database_port }}" +iop_vmaas_database_name: vmaas_db +iop_vmaas_database_user: vmaas_admin +iop_vmaas_database_password_file: "{{ obsah_state_path }}/iop-vmaas-db-password" +iop_vmaas_database_password: "{{ lookup('ansible.builtin.password', iop_vmaas_database_password_file, chars=['ascii_letters', 'digits']) }}" + +iop_vulnerability_database_host: "{{ iop_database_host }}" +iop_vulnerability_database_port: "{{ iop_database_port }}" +iop_vulnerability_database_name: vulnerability_db +iop_vulnerability_database_user: vulnerability_admin +iop_vulnerability_database_password_file: "{{ obsah_state_path }}/iop-vulnerability-db-password" +iop_vulnerability_database_password: "{{ lookup('ansible.builtin.password', iop_vulnerability_database_password_file, chars=['ascii_letters', 'digits']) }}" + +databases: + - name: foreman + database: "{{ foreman_database_name }}" + host: "{{ foreman_database_host }}" + port: "{{ foreman_database_port }}" + user: "{{ foreman_database_user }}" password: "{{ foreman_database_password }}" - - name: "{{ pulp_database_user }}" + ssl_mode: "{{ foreman_database_ssl_mode }}" + ssl_ca: "{{ foreman_database_ssl_ca }}" + feature: foreman + - name: candlepin + database: "{{ candlepin_database_name }}" + host: "{{ candlepin_database_host }}" + port: "{{ candlepin_database_port }}" + user: "{{ candlepin_database_user }}" + password: "{{ candlepin_database_password }}" + ssl_mode: "{{ candlepin_database_ssl_mode }}" + ssl_ca: "{{ candlepin_database_ssl_ca }}" + feature: katello + - name: pulp + database: "{{ pulp_database_name }}" + host: "{{ pulp_database_host }}" + port: "{{ pulp_database_port }}" + user: "{{ pulp_database_user }}" password: "{{ pulp_database_password }}" + ssl_mode: "{{ pulp_database_ssl_mode }}" + ssl_ca: "{{ pulp_database_ssl_ca }}" + feature: katello + - name: iop_advisor + database: "{{ iop_advisor_database_name }}" + host: "{{ iop_advisor_database_host }}" + port: "{{ iop_advisor_database_port }}" + user: "{{ iop_advisor_database_user }}" + password: "{{ iop_advisor_database_password }}" + feature: iop + - name: iop_inventory + database: "{{ iop_inventory_database_name }}" + host: "{{ iop_inventory_database_host }}" + port: "{{ iop_inventory_database_port }}" + user: "{{ iop_inventory_database_user }}" + password: "{{ iop_inventory_database_password }}" + feature: iop + - name: iop_remediation + database: "{{ iop_remediation_database_name }}" + host: "{{ iop_remediation_database_host }}" + port: "{{ iop_remediation_database_port }}" + user: "{{ iop_remediation_database_user }}" + password: "{{ iop_remediation_database_password }}" + feature: iop + - name: iop_vmaas + database: "{{ iop_vmaas_database_name }}" + host: "{{ iop_vmaas_database_host }}" + port: "{{ iop_vmaas_database_port }}" + user: "{{ iop_vmaas_database_user }}" + password: "{{ iop_vmaas_database_password }}" + feature: iop + - name: iop_vulnerability + database: "{{ iop_vulnerability_database_name }}" + host: "{{ iop_vulnerability_database_host }}" + port: "{{ iop_vulnerability_database_port }}" + user: "{{ iop_vulnerability_database_user }}" + password: "{{ iop_vulnerability_database_password }}" + feature: iop + +all_databases: >- + {{ databases | selectattr('feature', 'in', enabled_features) | list }} + +postgresql_databases: >- + {{ all_databases | to_postgresql_databases }} +postgresql_users: >- + {{ all_databases | to_postgresql_users }} diff --git a/src/vars/database_iop.yml b/src/vars/database_iop.yml deleted file mode 100644 index 9166b923a..000000000 --- a/src/vars/database_iop.yml +++ /dev/null @@ -1,61 +0,0 @@ ---- -iop_database_host: host.containers.internal -iop_database_port: 5432 - -iop_inventory_database_host: "{{ iop_database_host }}" -iop_inventory_database_port: "{{ iop_database_port }}" -iop_inventory_database_name: inventory_db -iop_inventory_database_user: inventory_admin -iop_inventory_database_password_file: "{{ obsah_state_path }}/iop-inventory-db-password" -iop_inventory_database_password: "{{ lookup('ansible.builtin.password', iop_inventory_database_password_file, chars=['ascii_letters', 'digits']) }}" - -iop_advisor_database_host: "{{ iop_database_host }}" -iop_advisor_database_port: "{{ iop_database_port }}" -iop_advisor_database_name: advisor_db -iop_advisor_database_user: advisor_user -iop_advisor_database_password_file: "{{ obsah_state_path }}/iop-advisor-db-password" -iop_advisor_database_password: "{{ lookup('ansible.builtin.password', iop_advisor_database_password_file, chars=['ascii_letters', 'digits']) }}" - -iop_remediation_database_host: "{{ iop_database_host }}" -iop_remediation_database_port: "{{ iop_database_port }}" -iop_remediation_database_name: remediations_db -iop_remediation_database_user: remediations_user -iop_remediation_database_password_file: "{{ obsah_state_path }}/iop-remediation-db-password" -iop_remediation_database_password: "{{ lookup('ansible.builtin.password', iop_remediation_database_password_file, chars=['ascii_letters', 'digits']) }}" - -iop_vmaas_database_host: "{{ iop_database_host }}" -iop_vmaas_database_port: "{{ iop_database_port }}" -iop_vmaas_database_name: vmaas_db -iop_vmaas_database_user: vmaas_admin -iop_vmaas_database_password_file: "{{ obsah_state_path }}/iop-vmaas-db-password" -iop_vmaas_database_password: "{{ lookup('ansible.builtin.password', iop_vmaas_database_password_file, chars=['ascii_letters', 'digits']) }}" - -iop_vulnerability_database_host: "{{ iop_database_host }}" -iop_vulnerability_database_port: "{{ iop_database_port }}" -iop_vulnerability_database_name: vulnerability_db -iop_vulnerability_database_user: vulnerability_admin -iop_vulnerability_database_password_file: "{{ obsah_state_path }}/iop-vulnerability-db-password" -iop_vulnerability_database_password: "{{ lookup('ansible.builtin.password', iop_vulnerability_database_password_file, chars=['ascii_letters', 'digits']) }}" - -iop_postgresql_databases: - - name: "{{ iop_inventory_database_name }}" - owner: "{{ iop_inventory_database_user }}" - - name: "{{ iop_advisor_database_name }}" - owner: "{{ iop_advisor_database_user }}" - - name: "{{ iop_remediation_database_name }}" - owner: "{{ iop_remediation_database_user }}" - - name: "{{ iop_vmaas_database_name }}" - owner: "{{ iop_vmaas_database_user }}" - - name: "{{ iop_vulnerability_database_name }}" - owner: "{{ iop_vulnerability_database_user }}" -iop_postgresql_users: - - name: "{{ iop_inventory_database_user }}" - password: "{{ iop_inventory_database_password }}" - - name: "{{ iop_advisor_database_user }}" - password: "{{ iop_advisor_database_password }}" - - name: "{{ iop_remediation_database_user }}" - password: "{{ iop_remediation_database_password }}" - - name: "{{ iop_vmaas_database_user }}" - password: "{{ iop_vmaas_database_password }}" - - name: "{{ iop_vulnerability_database_user }}" - password: "{{ iop_vulnerability_database_password }}" From 8e4ce37ebe2b0d5a673fb6794b5349d820ac3756 Mon Sep 17 00:00:00 2001 From: "Eric D. Helms" Date: Wed, 10 Jun 2026 06:43:59 -0400 Subject: [PATCH 2/2] Add filter_plugins path to development ansible.cfg The development playbooks load src/vars/database.yml which now uses custom filters (to_postgresql_databases, to_postgresql_users). Without this path, playbooks run from the development directory fail with "No filter named" errors. Co-Authored-By: Claude Opus 4.6 --- development/ansible.cfg | 1 + .../playbooks/deploy-dev/deploy-dev.yaml | 33 +++++++------------ .../remote-database/remote-database.yaml | 2 ++ src/filter_plugins/foremanctl.py | 7 +--- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/development/ansible.cfg b/development/ansible.cfg index 15225a2c6..40fc13d79 100644 --- a/development/ansible.cfg +++ b/development/ansible.cfg @@ -3,4 +3,5 @@ host_key_checking = False stdout_callback=debug stderr_callback=debug roles_path = ./roles:../src/roles +filter_plugins = ../src/filter_plugins display_skipped_hosts = no diff --git a/development/playbooks/deploy-dev/deploy-dev.yaml b/development/playbooks/deploy-dev/deploy-dev.yaml index 52e6a12e5..0871b56fa 100644 --- a/development/playbooks/deploy-dev/deploy-dev.yaml +++ b/development/playbooks/deploy-dev/deploy-dev.yaml @@ -15,33 +15,22 @@ pre_tasks: - name: Set development postgresql databases ansible.builtin.set_fact: - postgresql_databases: - - name: "{{ candlepin_database_name }}" - owner: "{{ candlepin_database_user }}" - - name: "{{ foreman_development_database_name }}" - owner: "{{ foreman_database_user }}" - - name: "{{ foreman_development_database_name }}_test" - owner: "{{ foreman_database_user }}" - - name: "{{ pulp_database_name }}" - owner: "{{ pulp_database_user }}" - postgresql_users: - - name: "{{ candlepin_database_user }}" - password: "{{ candlepin_database_password }}" - - name: "{{ foreman_database_user }}" - password: "{{ foreman_database_password }}" - role_attr_flags: SUPERUSER - - name: "{{ pulp_database_user }}" - password: "{{ pulp_database_password }}" + postgresql_databases: >- + {{ all_databases + | rejectattr('name', 'equalto', 'foreman') + | to_postgresql_databases + + [{'name': foreman_development_database_name, 'owner': foreman_database_user}, + {'name': foreman_development_database_name + '_test', 'owner': foreman_database_user}] }} + postgresql_users: >- + {{ all_databases + | rejectattr('name', 'equalto', 'foreman') + | to_postgresql_users + + [{'name': foreman_database_user, 'password': foreman_database_password, 'role_attr_flags': 'SUPERUSER'}] }} - name: Setup iop requirements when: - "'iop' in enabled_features" block: - - name: Combine lists - ansible.builtin.set_fact: - postgresql_databases: "{{ postgresql_databases + iop_postgresql_databases }}" - postgresql_users: "{{ postgresql_users + iop_postgresql_users }}" - - name: Enable foreman_rh_cloud plugin for iop ansible.builtin.set_fact: foreman_development_enabled_plugins: "{{ foreman_development_enabled_plugins + ['foreman_rh_cloud'] }}" diff --git a/development/playbooks/remote-database/remote-database.yaml b/development/playbooks/remote-database/remote-database.yaml index 0ea469c1d..ab0cce1d9 100644 --- a/development/playbooks/remote-database/remote-database.yaml +++ b/development/playbooks/remote-database/remote-database.yaml @@ -4,6 +4,8 @@ - database become: true vars_files: + - "../../../src/vars/defaults.yml" + - "../../../src/vars/flavors/{{ flavor }}.yml" - "../../../src/vars/database.yml" vars: certificates_hostnames: diff --git a/src/filter_plugins/foremanctl.py b/src/filter_plugins/foremanctl.py index aa135a9f6..9a922bc8a 100644 --- a/src/filter_plugins/foremanctl.py +++ b/src/filter_plugins/foremanctl.py @@ -120,12 +120,7 @@ def to_postgresql_databases(databases): def to_postgresql_users(databases): - seen = {} - for db in databases: - name = db['user'] - if name not in seen: - seen[name] = {'name': name, 'password': db['password']} - return list(seen.values()) + return [{'name': db['user'], 'password': db['password']} for db in databases] class FilterModule(object):