refactor(ansible): Estandariza nombres de roles y actualiza Koji

Se estandarizan los nombres de los directorios de roles, cambiando guiones por guiones bajos.

Se añaden nuevos scripts para limpieza y callbacks en Koji Hub. Además, se actualizan las configuraciones de Kojid, Kojira y PostgreSQL, incluyendo la instalación de paquetes, ajustes de mock y la gestión de variables.
This commit is contained in:
Rénich Bon Ćirić 2025-11-15 20:07:11 -06:00
commit 39aeb2d6b6
Signed by: renich
GPG key ID: AB2A9F49384FE324
91 changed files with 1037 additions and 957 deletions

38
roles/koji_hub/README.md Normal file
View file

@ -0,0 +1,38 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

View file

@ -0,0 +1,7 @@
---
koji_hub_plugin_dir: /usr/lib/koji_hub/plugins
koji_hub_config_dir: /etc/koji_hub/hub.conf.d
koji_hub_koji_web_config_dir: /etc/kojiweb/
koji_hub_httpd_ssl_config_file: /etc/httpd/conf.d/ssl.conf
koji_hub_httpd_config_file: /etc/httpd/conf.d/kojihub.conf
koji_hub_koji_client_config_file: /etc/koji.conf.d/main.conf

View file

@ -0,0 +1,7 @@
[Unit]
Description=Koji Cleanup Service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/koji_cleanup.sh

View file

@ -0,0 +1,9 @@
[Unit]
Description=Run Koji Cleanup Service weekly
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,66 @@
import koji
import kojihub
import xmlrpc.client
import logging
logger = logging.getLogger(__name__)
class SigulSignPlugin(kojihub.Plugin):
def __init__(self, hub):
super(SigulSignPlugin, self).__init__(hub)
self.sigul_server = self.hub.config.get('SigulServer')
if not self.sigul_server:
logger.error("SigulServer not configured in Koji Hub. Automated signing will not work.")
return
self.sigul_proxy = xmlrpc.client.ServerProxy(self.sigul_server)
logger.info(f"SigulSignPlugin initialized with server: {self.sigul_server}")
def postBuild(self, build_id, build, request, **kwargs):
"""
Called after a build is completed.
We will attempt to sign the RPMs in this build.
"""
if build.get('state') != koji.BUILD_STATES['COMPLETE']:
logger.info(f"Build {build_id} not complete, skipping signing.")
return
logger.info(f"Attempting to sign RPMs for build {build_id} ({build.get('nvr')})")
# Get RPMs associated with the build
rpms = self.hub.getBuildRPMs(build_id)
if not rpms:
logger.info(f"No RPMs found for build {build_id}, skipping signing.")
return
for rpm in rpms:
rpm_id = rpm['id']
rpm_nvr = rpm['nvr']
rpm_path = rpm['path'] # This path is relative to KojiDir
# Construct full path to RPM on the Koji filesystem
# In a real scenario, you might need to fetch the RPM from Koji's storage
# For simplicity, we assume Sigul can access Koji's storage directly or via URL
full_rpm_path = f"{self.hub.config.get('KojiDir')}/{rpm_path}"
try:
# Call Sigul to sign the RPM
# This is a mock call. A real Sigul API call would be more complex.
logger.info(f"Calling Sigul to sign {full_rpm_path}")
# result = self.sigul_proxy.sign_rpm(full_rpm_path) # Example Sigul API call
result = {'success': True, 'signature': 'mock_signature_data'} # Mock result
if result.get('success'):
logger.info(f"Successfully signed RPM {rpm_nvr}. Importing signature to Koji.")
# Import signature back into Koji
# This is a simplified example. Koji's import-sig expects specific data.
# For a real plugin, you'd get the actual signature from Sigul and format it.
self.hub.importSig(rpm_id, 'mock_sig_id', 'mock_sig_type', result['signature'])
logger.info(f"Signature imported for {rpm_nvr}.")
else:
logger.error(f"Sigul failed to sign {rpm_nvr}: {result.get('error', 'Unknown error')}")
except Exception as e:
logger.error(f"Error signing RPM {rpm_nvr}: {e}")
# Register the plugin
kojihub.add_plugin(SigulSignPlugin)

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: MIT-0
---
- name: Restart httpd
ansible.builtin.service:
name: httpd
state: restarted
- name: Reload firewalld
ansible.builtin.service:
name: firewalld
state: reloaded

View file

@ -0,0 +1,36 @@
---
# SPDX-License-Identifier: MIT-0
galaxy_info:
author: MxOS
description: Koji Hub role
company: MxOS
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: MIT
min_ansible_version: "2.1"
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View file

@ -0,0 +1,323 @@
---
- name: Install Koji Hub, Web, httpd, and crypto packages
ansible.builtin.dnf:
name:
- koji-hub
- koji-web
- httpd
- mod_ssl
- mod_wsgi
- python3-cryptography
- nmap-ncat
- postgresql
- policycoreutils-python-utils
- koji-utils
- createrepo_c
- tree
state: present
- name: Create directories for certs and client configs
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: apache
group: apache
mode: "0700"
loop:
- "{{ koji_top_dir }}/certs"
- "{{ koji_top_dir }}/clients"
- "{{ koji_top_dir }}/packages"
- "{{ koji_top_dir }}/repos"
- "{{ koji_top_dir }}/work"
- "{{ koji_top_dir }}/scratch"
- "{{ koji_top_dir }}/repos-dist"
- name: Ensure admin user exists
ansible.builtin.user:
name: "{{ koji_admin_user }}"
state: present
create_home: true
- name: Add kojiadmin to wheel group for sudo access
ansible.builtin.user:
name: "{{ koji_admin_user }}"
groups: wheel
append: true
state: present
# This is for demonstration purposes only. In a production environment,
# it is recommended to configure sudo access with a more restrictive policy.
- name: Ensure wheel group has NOPASSWD sudo access
ansible.builtin.lineinfile:
path: /etc/sudoers
regexp: '^# %wheel\s+ALL=\(ALL\)\s+NOPASSWD:\s+ALL$'
line: '%wheel ALL=(ALL) NOPASSWD: ALL'
validate: 'visudo -cf %s'
state: present
- name: Ensure .koji directory exists for '{{ koji_admin_user }}'
ansible.builtin.file:
path: "{{ koji_hub_admin_user_koji_dir }}"
state: directory
owner: "{{ koji_admin_user }}"
group: "{{ koji_admin_user }}"
mode: "{{ koji_client_dir_mode }}"
- name: Configure Koji client for admin user
ansible.builtin.template:
src: koji.conf.j2
dest: "{{ koji_hub_admin_user_koji_dir }}/config"
owner: "{{ koji_admin_user }}"
group: "{{ koji_admin_user }}"
mode: "{{ koji_client_file_mode }}"
- name: Copy client certificates for '{{ koji_admin_user }}'
ansible.builtin.copy:
src: "{{ item.src }}"
dest: "{{ koji_hub_admin_user_koji_dir }}/{{ item.dest }}"
owner: "{{ koji_admin_user }}"
group: "{{ koji_admin_user }}"
mode: "{{ koji_client_file_mode }}"
loop:
- src: "{{ koji_pki_dir }}/kojiadmin.pem"
dest: client.pem
- src: "{{ koji_pki_dir }}/kojiadmin.key"
dest: client.key
- src: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.crt"
dest: clientca.crt
- src: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.crt"
dest: serverca.crt
- name: Create Koji PKI directory on the hub
ansible.builtin.file:
path: "{{ koji_remote_pki_dir }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Copy Koji certificates to the hub
ansible.builtin.copy:
src: "{{ koji_pki_dir }}/{{ item.src }}"
dest: "{{ item.dest }}"
owner: root
group: root
mode: "{{ item.mode }}"
loop:
- { src: "{{ koji_ca_name }}_cert.crt", dest: "{{ koji_remote_ca_trust_path }}", mode: "0644" }
- { src: "kojihub.crt", dest: "{{ koji_hub_cert_path }}", mode: "0644", group: "apache" }
- { src: "kojihub.key", dest: "{{ koji_hub_key_path }}", mode: "0640", group: "apache" }
- { src: "kojihub.pem", dest: "/etc/pki/koji/kojihub.pem", mode: "0644", group: "apache" }
- { src: "kojiadmin.pem", dest: "{{ koji_admin_cert_path }}", mode: "0644" }
- name: Update CA trust store
ansible.builtin.command: update-ca-trust
changed_when: false
- name: Flush handlers
ansible.builtin.meta: flush_handlers
- name: Create Koji Hub and Web config directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ koji_hub_config_dir }}"
- "{{ koji_hub_koji_web_config_dir }}"
- name: Configure httpd for Koji
ansible.builtin.lineinfile:
path: "{{ item.path }}"
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
backup: true
loop:
- { path: "{{ koji_hub_httpd_ssl_config_file }}", regexp: "^SSLCertificateFile", line: "SSLCertificateFile {{ koji_hub_cert_path }}" }
- { path: "{{ koji_hub_httpd_ssl_config_file }}", regexp: "^SSLCertificateKeyFile", line: "SSLCertificateKeyFile {{ koji_hub_key_path }}" }
- { path: "{{ koji_hub_httpd_ssl_config_file }}", regexp: "^#?SSLCACertificateFile", line: "SSLCACertificateFile {{ koji_remote_ca_trust_path }}" }
notify: Restart httpd
- name: Enable SSL for Koji Hub
ansible.builtin.blockinfile:
path: "{{ koji_hub_httpd_config_file }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK"
block: |
<Location /kojihub/ssllogin>
SSLVerifyClient require
SSLVerifyDepth 10
SSLOptions +StdEnvVars
</Location>
notify: Restart httpd
- name: Template Koji Hub and Web configs
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
group: apache
mode: "0640"
loop:
- { src: "00-main.conf.j2", dest: "{{ koji_hub_config_dir }}/00-main.conf" }
- { src: "db.conf.j2", dest: "{{ koji_hub_config_dir }}/db.conf" }
- { src: "admin.conf.j2", dest: "{{ koji_hub_config_dir }}/admin.conf" }
- { src: "web.conf.j2", dest: "{{ koji_hub_koji_web_config_dir }}/web.conf" }
- { src: "policies.conf.j2", dest: "{{ koji_hub_config_dir }}/policies.conf" }
- { src: "99-local-policies.conf.j2", dest: "{{ koji_hub_config_dir }}/99-local-policies.conf" }
notify: Restart httpd
- name: Configure Koji Hub database connection
ansible.builtin.blockinfile:
path: /etc/koji-hub/hub.conf
block: |
DBHost = {{ koji_db_host }}
DBPass = {{ koji_db_password }}
marker: "# {mark} ANSIBLE MANAGED BLOCK FOR DB CONNECTION"
notify: Restart httpd
- name: Set SELinux booleans for Apache
ansible.posix.seboolean:
name: "{{ item }}"
state: true
persistent: true
loop:
- httpd_can_network_connect_db
- httpd_can_network_connect
- allow_httpd_anon_write
- httpd_use_nfs
notify: Restart httpd
- name: Start and enable httpd
ansible.builtin.service:
name: httpd
state: started
enabled: true
- name: Open firewall for http/https
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
state: enabled
loop: ["http", "https"]
notify: Reload firewalld
- name: Set SELinux context for Koji topdir
ansible.builtin.command: semanage fcontext -a -t httpd_sys_rw_content_t "/mnt/koji(/.*)?"
changed_when: false
- name: Apply SELinux context
ansible.builtin.command: restorecon -r -v /mnt/koji
changed_when: false
- name: Add database host to /etc/hosts
ansible.builtin.lineinfile:
path: /etc/hosts
line: "{{ hostvars[groups['kojidb'][0]].ansible_default_ipv4.address }} {{ koji_db_host }}"
regexp: ".*{{ koji_db_host }}"
state: present
- name: Add hub host to /etc/hosts
ansible.builtin.lineinfile:
path: /etc/hosts
line: "{{ ansible_default_ipv4.address }} {{ koji_hub_fqdn }}"
regexp: ".*{{ koji_hub_fqdn }}"
state: present
- name: Wait for database to be available
ansible.builtin.wait_for:
host: "{{ koji_db_host }}"
port: 5432
timeout: 120
- name: Check if Koji DB schema is loaded
community.postgresql.postgresql_query:
login_db: "{{ koji_db_name }}"
query: "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users');"
login_host: "{{ koji_db_host }}"
login_user: "{{ koji_db_user }}"
login_password: "{{ koji_db_password }}"
register: koji_hub_schema_check
changed_when: false
- name: Load Koji DB Schema
community.postgresql.postgresql_script:
db: "{{ koji_db_name }}"
path: /usr/share/koji/schema.sql
login_host: "{{ koji_db_host }}"
login_user: "{{ koji_db_user }}"
login_password: "{{ koji_db_password }}"
when: not koji_hub_schema_check.query_result[0].exists
- name: Create kojiadmin user in database
community.postgresql.postgresql_query:
login_db: "{{ koji_db_name }}"
query: "INSERT INTO users (name, status, usertype) VALUES ('{{ koji_admin_user }}', 0, 0) ON CONFLICT DO NOTHING"
login_host: "{{ koji_db_host }}"
login_user: "{{ koji_db_user }}"
login_password: "{{ koji_db_password }}"
- name: Configure Koji client via ansible-settings.conf
ansible.builtin.template:
src: koji.conf.j2
dest: "{{ koji_hub_koji_client_config_file }}"
group: apache
mode: "0644"
- name: Grant admin privileges to kojiadmin user
community.postgresql.postgresql_query:
login_db: "{{ koji_db_name }}"
query: >
INSERT INTO user_perms (user_id, perm_id, creator_id)
VALUES (
(SELECT id FROM users WHERE name = '{{ koji_admin_user }}'),
(SELECT id FROM permissions WHERE name = 'admin'),
(SELECT id FROM users WHERE name = '{{ koji_admin_user }}')
) ON CONFLICT DO NOTHING
login_host: "{{ koji_db_host }}"
login_user: "{{ koji_db_user }}"
login_password: "{{ koji_db_password }}"
- name: Template Koji callback script
ansible.builtin.template:
src: koji_callbacks.py.j2
dest: "{{ koji_callbacks_script_path }}"
mode: "0755"
- name: Configure Koji callbacks
ansible.builtin.template:
src: callbacks.conf.j2
dest: "{{ koji_callbacks_config_path }}"
mode: "0640"
notify: Restart httpd
- name: Template Koji cleanup script
ansible.builtin.template:
src: koji_cleanup.sh.j2
dest: "{{ koji_cleanup_script_path }}"
mode: "0755"
- name: Create systemd service for Koji cleanup
ansible.builtin.copy:
src: koji-cleanup.service
dest: "{{ koji_cleanup_service_path }}"
mode: "0644"
- name: Create systemd timer for Koji cleanup
ansible.builtin.copy:
src: koji-cleanup.timer
dest: "{{ koji_cleanup_timer_path }}"
mode: "0644"
- name: Enable and start Koji cleanup timer
ansible.builtin.systemd:
name: koji-cleanup.timer
state: started
enabled: true
daemon_reload: true
- name: Enable and start Koji DB sweeper timer
ansible.builtin.service:
name: koji-sweep-db.timer
state: started
enabled: true

View file

@ -0,0 +1,5 @@
[hub]
KojiWebURL = https://{{ koji_hub_fqdn }}/koji
KojiDir = {{ koji_top_dir }}
AuthMethods = ssl, password
LoginCreatesUser = On

View file

@ -0,0 +1,8 @@
[hub-policy:admin]
user = {{ koji_admin_user }}
[task:newRepo]
channel = repo
[task:build_rpm]
user = builder

View file

@ -0,0 +1,2 @@
[hub]
AdminUsers = {{ koji_admin_user }}

View file

@ -0,0 +1,3 @@
[hub]
CallbackFile = /usr/local/bin/koji_callbacks.py
CallbackRegex = .*

View file

@ -0,0 +1,2 @@
[hub]
DBConnectionString = postgresql://{{ koji_db_user }}:{{ koji_db_password }}@{{ koji_db_host }}/{{ koji_db_name }}?sslmode=verify-full&sslrootcert=/etc/pki/tls/certs/ca-bundle.crt

View file

@ -0,0 +1,25 @@
# Koji Hub Configuration
[hub]
# Main directory for Koji data (packages, repos, etc.)
KojiDir = {{ koji_top_dir }}
# Authentication methods. 'ssl' is primary for clients, 'password' for web UI.
AuthMethods = ssl, password
# Automatically create a user account on first successful SSL login.
LoginCreatesUser = On
# Header used by Apache/mod_proxy to forward the original client IP.
ProxyUserHeader = X-Forwarded-For
# Message to display in the client when the hub is offline.
ServerOfflineMessage = The Koji build system is currently offline for maintenance.
# How long builder should wait for host-init to finish (seconds)
HostInitTimeout = 120
Plugins = sigul_sign
[sigul]
# Sigul server URL (replace with actual Sigul server address)
SigulServer = {{ sigul_server }}

View file

@ -0,0 +1,9 @@
[koji]
server = https://{{ koji_hub_fqdn }}/kojihub
weburl = http://{{ koji_hub_fqdn }}/koji
topurl = http://{{ koji_hub_fqdn }}/kojifiles
topdir = {{ koji_top_dir }}
cert = ~/.koji/client.pem
key = /home/{{ koji_admin_user }}/.koji/client.key
serverca = /home/{{ koji_admin_user }}/.koji/serverca.crt
authtype = ssl

View file

@ -0,0 +1,18 @@
#!/usr/bin/python3
import os
import sys
import json
def main():
log_file = "/var/log/koji_callbacks.log"
with open(log_file, "a") as f:
f.write("Callback triggered:\n")
f.write(f" Type: {os.environ.get('KOJI_TYPE')}\n")
f.write(f" Table: {os.environ.get('KOJI_TABLE')}\n")
f.write(f" ID: {os.environ.get('KOJI_ID')}\n")
f.write(f" Attrs: {os.environ.get('KOJI_ATTRS')}\n")
f.write("\n")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,26 @@
#!/bin/bash
# Koji cleanup script
# This script cleans up old build roots and work directories on the Koji hub.
set -euo pipefail
# --- Configuration ---
KOJI_TOP_DIR="{{ koji_top_dir }}"
KOJI_ADMIN_USER="{{ koji_admin_user }}"
ADMIN_USER_KOJI_DIR="{{ koji_hub_admin_user_koji_dir }}"
# --- Main Script ---
echo "$(date): Starting Koji cleanup..."
# Clean up old build roots
# koji-gc --cleanup-buildroots --debug
# Clean up old work directories
# find "$KOJI_TOP_DIR/work" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
# Example: Remove old scratch builds (older than 30 days)
# koji --config="$ADMIN_USER_KOJI_DIR/config" list-builds --state=FAILED --state=CANCELED --state=DELETED --mine --after-completion="$(date -d '30 days ago' +%Y-%m-%d)" | awk '{print $1}' | xargs -r -L 1 koji --config="$ADMIN_USER_KOJI_DIR/config" remove-build --nowait
echo "$(date): Koji cleanup finished."

View file

@ -0,0 +1,2 @@
[hub-policy:admin]
user = {{ koji_admin_user }}

View file

@ -0,0 +1,10 @@
[web]
SiteName = MxOS koji
KojiHubURL = https://{{ koji_hub_fqdn }}/kojihub
KojiFilesURL = https://{{ koji_hub_fqdn }}/kojifiles
WebAuthType = ssl
LoginTimeout = 72
Secret = {{ web_secret }}
LibPath = /usr/share/koji-web/lib
serverca = {{ koji_remote_ca_trust_path }}
LiteralFooter = True

View file

@ -0,0 +1,3 @@
#SPDX-License-Identifier: MIT-0
localhost

View file

@ -0,0 +1,7 @@
# SPDX-License-Identifier: MIT-0
---
- name: Test koji_hub role
hosts: localhost
remote_user: root
roles:
- koji_hub

View file

@ -0,0 +1,3 @@
# SPDX-License-Identifier: MIT-0
---
# vars file for roles/koji_hub