Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ jobs:
- none
database:
- internal
iop:
- enabled
include:
- certificate_source: default
security: fapolicyd
Expand All @@ -54,7 +56,7 @@ jobs:
security: none
database: external
runs-on: ubuntu-24.04
name: "Tests (certificate source: ${{ matrix.certificate_source }}, database: ${{ matrix.database }}, security mode: ${{ matrix.security }})"
name: "Tests (certificate source: ${{ matrix.certificate_source }}, database: ${{ matrix.database }}, security mode: ${{ matrix.security }}, iop: ${{ matrix.iop }})"
steps:
- uses: actions/checkout@v6
- name: Set up Python
Expand Down Expand Up @@ -108,6 +110,10 @@ jobs:
- name: Add optional feature - foreman_azure_rm and foreman_google
run: |
./foremanctl deploy --add-feature foreman_azure_rm --add-feature foreman_google
- name: Enable iop
if: matrix.iop == 'enabled'
run: |
./foremanctl deploy --add-feature iop
Comment on lines +113 to +116
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calls to foremanctl deploy are expensive (adds ca 2 minutes to the execution), so I wonder if it'd be smarter to have fewer of those "add optional feature" steps and use something like {{ matrix.iop == 'enabled' && '--add-feature iop' || '' }} inside an existing step

- name: Run tests
run: |
./forge test --pytest-args="--certificate-source=${{ matrix.certificate_source }} --database-mode=${{ matrix.database }}"
Expand All @@ -127,7 +133,7 @@ jobs:
if: ${{ always() }}
uses: actions/upload-artifact@v6
with:
name: sosreport-${{ matrix.certificate_source }}-${{ matrix.security }}-${{ matrix.database }}
name: sosreport-${{ matrix.certificate_source }}-${{ matrix.security }}-${{ matrix.database }}-${{ matrix.iop }}
path: sos/
- name: Setup upterm session
if: ${{ failure() }}
Expand Down
18 changes: 18 additions & 0 deletions src/playbooks/deploy/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@
- "../../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
Expand All @@ -30,6 +44,10 @@
- pulp
- foreman
- role: systemd_target
- role: iop_core
when:
- "'iop' in enabled_features"
- database_mode == 'internal'
- role: foreman_proxy
when:
- "'foreman-proxy' in enabled_features"
Expand Down
1 change: 1 addition & 0 deletions src/requirements.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
collections:
- community.general
- community.postgresql
- community.crypto
- ansible.posix
Expand Down
9 changes: 9 additions & 0 deletions src/roles/iop_advisor/defaults/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
iop_advisor_container_image: "quay.io/iop/advisor-backend"
iop_advisor_container_tag: "foreman-3.16"

iop_advisor_database_name: advisor_db
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these vars can be deleted as they are in src/vars/database_iop.yml.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Role defaults are not the same as playbook defaults. We try to follow Ansible best practices by ensuring these are defined at the role level. We then define them at a higher level for our use case to provide coordination of values and advanced workflows.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Very good to know. I'll try to implement the same reasoning in #356.

iop_advisor_database_user: advisor_user
iop_advisor_database_password: CHANGEME
iop_advisor_database_host: host.containers.internal
iop_advisor_database_port: 5432
28 changes: 28 additions & 0 deletions src/roles/iop_advisor/handlers/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
- name: Check if advisor backend api service exists
ansible.builtin.systemd:
name: iop-service-advisor-backend-api
register: iop_advisor_api_service_status
failed_when: false
listen: restart advisor

- name: Restart advisor backend api service if it exists
ansible.builtin.systemd:
name: iop-service-advisor-backend-api
state: restarted
when: iop_advisor_api_service_status.status is defined and iop_advisor_api_service_status.status.LoadState != "not-found"
listen: restart advisor

- name: Check if advisor backend service exists
ansible.builtin.systemd:
name: iop-service-advisor-backend-service
register: iop_advisor_service_status
failed_when: false
listen: restart advisor

- name: Restart advisor backend service if it exists
ansible.builtin.systemd:
name: iop-service-advisor-backend-service
state: restarted
when: iop_advisor_service_status.status is defined and iop_advisor_service_status.status.LoadState != "not-found"
listen: restart advisor
136 changes: 136 additions & 0 deletions src/roles/iop_advisor/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
- name: Pull Advisor Backend container image
containers.podman.podman_image:
name: "{{ iop_advisor_container_image }}:{{ iop_advisor_container_tag }}"
state: present

- name: Create podman secret for advisor database username
containers.podman.podman_secret:
name: iop-service-advisor-backend-database-username
data: "{{ iop_advisor_database_user }}"
notify: restart advisor

- name: Create podman secret for advisor database password
containers.podman.podman_secret:
name: iop-service-advisor-backend-database-password
data: "{{ iop_advisor_database_password }}"
notify: restart advisor

- name: Create podman secret for advisor database name
containers.podman.podman_secret:
name: iop-service-advisor-backend-database-name
data: "{{ iop_advisor_database_name }}"
notify: restart advisor

- name: Create podman secret for advisor database host
containers.podman.podman_secret:
name: iop-service-advisor-backend-database-host
data: "{{ iop_advisor_database_host }}"
notify: restart advisor

- name: Create podman secret for advisor database port
containers.podman.podman_secret:
name: iop-service-advisor-backend-database-port
data: "{{ iop_advisor_database_port }}"
notify: restart advisor

- name: Deploy Advisor Backend API Container
containers.podman.podman_container:
name: iop-service-advisor-backend-api
image: "{{ iop_advisor_container_image }}:{{ iop_advisor_container_tag }}"
state: quadlet
command: sh -c "./container_init.sh && api/app.sh"
network:
- iop-core-network
env:
DJANGO_SESSION_KEY: "UNUSED"
BOOTSTRAP_SERVERS: "iop-core-kafka:9092"
ADVISOR_ENV: "prod"
LOG_LEVEL: "INFO"
USE_DJANGO_WEBSERVER: "false"
CLOWDER_ENABLED: "false"
WEB_CONCURRENCY: "2"
ENABLE_AUTOSUB: "true"
TASKS_REWRITE_INTERNAL_URLS: "true"
TASKS_REWRITE_INTERNAL_URLS_FOR: "internal.localhost"
ENABLE_INIT_CONTAINER_MIGRATIONS: "true"
ENABLE_INIT_CONTAINER_IMPORT_CONTENT: "true"
IMAGE: "latest"
ALLOWED_HOSTS: "*"
INVENTORY_SERVER_URL: "http://iop-core-host-inventory-api:8081/api/inventory/v1"
ADVISOR_DB_SSL_MODE: "disable"
PORT: "8000"
REGISTRY_AUTH_FILE: "/etc/foreman/registry-auth.json"
secrets:
- 'iop-service-advisor-backend-database-username,type=env,target=ADVISOR_DB_USER'
- 'iop-service-advisor-backend-database-password,type=env,target=ADVISOR_DB_PASSWORD'
- 'iop-service-advisor-backend-database-name,type=env,target=ADVISOR_DB_NAME'
- 'iop-service-advisor-backend-database-host,type=env,target=ADVISOR_DB_HOST'
- 'iop-service-advisor-backend-database-port,type=env,target=ADVISOR_DB_PORT'
quadlet_options:
- |
[Unit]
Description=Advisor Backend API
After=iop-core-kafka.service
Wants=iop-core-kafka.service
[Service]
Restart=on-failure
[Install]
WantedBy=default.target

- name: Deploy Advisor Backend Service Container
containers.podman.podman_container:
name: iop-service-advisor-backend-service
image: "{{ iop_advisor_container_image }}:{{ iop_advisor_container_tag }}"
state: quadlet
command: pipenv run python service/service.py
network:
- iop-core-network
env:
BOOTSTRAP_SERVERS: "iop-core-kafka:9092"
ADVISOR_DB_SSL_MODE: "disable"
DISABLE_WEB_SERVER: "true"
REGISTRY_AUTH_FILE: "/etc/foreman/registry-auth.json"
secrets:
- 'iop-service-advisor-backend-database-username,type=env,target=ADVISOR_DB_USER'
- 'iop-service-advisor-backend-database-password,type=env,target=ADVISOR_DB_PASSWORD'
- 'iop-service-advisor-backend-database-name,type=env,target=ADVISOR_DB_NAME'
- 'iop-service-advisor-backend-database-host,type=env,target=ADVISOR_DB_HOST'
- 'iop-service-advisor-backend-database-port,type=env,target=ADVISOR_DB_PORT'
quadlet_options:
- |
[Unit]
Description=Advisor Backend Service
After=iop-core-kafka.service
Wants=iop-core-kafka.service
[Service]
Restart=on-failure
[Install]
WantedBy=default.target

- name: Run daemon reload to make Quadlet create the service files
ansible.builtin.systemd:
daemon_reload: true

- name: Start Advisor Backend API service
ansible.builtin.systemd:
name: iop-service-advisor-backend-api
enabled: true
state: started

- name: Start Advisor Backend Service
ansible.builtin.systemd:
name: iop-service-advisor-backend-service
enabled: true
state: started

- name: Set up Foreign Data Wrapper for advisor database
ansible.builtin.include_role:
name: iop_fdw
vars:
iop_fdw_database_name: "{{ iop_advisor_database_name }}"
iop_fdw_database_user: "{{ iop_advisor_database_user }}"
iop_fdw_database_password: "{{ iop_advisor_database_password }}"
iop_fdw_remote_database_name: "{{ iop_inventory_database_name }}"
iop_fdw_remote_user: "{{ iop_inventory_database_user }}"
iop_fdw_remote_password: "{{ iop_inventory_database_password }}"
5 changes: 5 additions & 0 deletions src/roles/iop_advisor_frontend/defaults/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
iop_advisor_frontend_container_image: "quay.io/iop/advisor-frontend"
iop_advisor_frontend_container_tag: "foreman-3.16"
iop_advisor_frontend_assets_path: "/var/lib/foreman/public/assets/apps/advisor"
iop_advisor_frontend_source_path: "/srv/dist/."
98 changes: 98 additions & 0 deletions src/roles/iop_advisor_frontend/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
- name: Pull Advisor Frontend container image
containers.podman.podman_image:
name: "{{ iop_advisor_frontend_container_image }}:{{ iop_advisor_frontend_container_tag }}"
state: present

- name: Ensure parent assets directory exists
ansible.builtin.file:
path: /var/lib/foreman/public/assets/apps
state: directory
owner: root
group: root
mode: '0755'

- name: Ensure assets directory exists
ansible.builtin.file:
path: "{{ iop_advisor_frontend_assets_path }}"
state: directory
owner: root
group: root
mode: '0755'

- name: Create temporary container for asset extraction
containers.podman.podman_container:
name: iop-advisor-frontend-temp
image: "{{ iop_advisor_frontend_container_image }}:{{ iop_advisor_frontend_container_tag }}"
state: created

- name: Extract advisor frontend assets from container
containers.podman.podman_container_copy:
container: iop-advisor-frontend-temp
src: "{{ iop_advisor_frontend_source_path }}"
dest: "{{ iop_advisor_frontend_assets_path }}"
from_container: true

- name: Remove temporary container
containers.podman.podman_container:
name: iop-advisor-frontend-temp
state: absent

- name: Set ownership of advisor frontend assets
ansible.builtin.file:
path: "{{ iop_advisor_frontend_assets_path }}"
owner: root
group: root
recurse: true

- name: Set SELinux file context for advisor frontend assets
community.general.sefcontext:
target: "{{ iop_advisor_frontend_assets_path }}(/.*)?"
setype: httpd_exec_t
state: present
when: ansible_facts["selinux"]["status"] == "enabled"

- name: Restore SELinux context for advisor frontend assets
ansible.builtin.command:
cmd: restorecon -R "{{ iop_advisor_frontend_assets_path }}"
when: ansible_facts["selinux"]["status"] == "enabled"
changed_when: false

- name: Ensure Apache SSL config directory exists
ansible.builtin.file:
path: /etc/httpd/conf.d/05-foreman-ssl.d
state: directory
mode: '0755'

- name: Configure Apache for advisor frontend assets
ansible.builtin.copy:
dest: /etc/httpd/conf.d/05-foreman-ssl.d/advisor-frontend.conf
content: |
# IOP Advisor Frontend Assets Configuration
Alias /assets/apps/advisor {{ iop_advisor_frontend_assets_path }}
ProxyPass /assets/apps/advisor !

<LocationMatch "^/assets/apps/advisor">
Options SymLinksIfOwnerMatch
AllowOverride None
Require all granted

# Use standard http expire header for assets instead of ETag
<IfModule mod_expires.c>
Header unset ETag
FileETag None
ExpiresActive On
ExpiresDefault "access plus 1 year"
</IfModule>

# Return compressed assets if they are precompiled
RewriteEngine On
# Make sure the browser supports gzip encoding and file with .gz added
# does exist on disc before we rewrite with the extension
RewriteCond %{HTTP:Accept-Encoding} \b(x-)?gzip\b
RewriteCond %{REQUEST_FILENAME} \.(css|js|svg)$
RewriteCond %{REQUEST_FILENAME}.gz -s
RewriteRule ^(.+) $1.gz [L]
</LocationMatch>
mode: '0644'
notify: "httpd : Restart httpd"
1 change: 1 addition & 0 deletions src/roles/iop_core/defaults/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
---
Loading
Loading