--- # Playbook: app/configure_app.yml # Purpose: Configure OS + app runtime on app project guests created via provision_vms.yml # Targets: app_all or per-project group created dynamically # Tags: app, configure # # Usage: # - Run one project: ansible-playbook -i inventories/production playbooks/app/site.yml -e app_project=projectA # - Run all projects: ansible-playbook -i inventories/production playbooks/app/site.yml - name: Build dynamic inventory from app_projects (so configure can run standalone) hosts: localhost connection: local gather_facts: false vars: selected_projects: >- {{ (app_projects | dict2items | map(attribute='key') | list) if (app_project is not defined or app_project | length == 0) else [app_project] }} app_bootstrap_user_default: root # If true, configure plays will use vault_lxc_root_password for initial SSH bootstrap. bootstrap_with_root_password_default: false tasks: - name: Validate requested project exists ansible.builtin.assert: that: - app_project is not defined or app_project in app_projects fail_msg: "Requested app_project={{ app_project }} does not exist in app_projects." - name: Add each project/env host (by static IP) to dynamic inventory ansible.builtin.add_host: name: "{{ app_projects[item.0].envs[item.1].name | default(item.0 ~ '-' ~ item.1) }}" groups: - "app_all" - "app_{{ item.0 }}_all" - "app_{{ item.0 }}_{{ item.1 }}" ansible_host: "{{ (app_projects[item.0].envs[item.1].ip | string).split('/')[0] }}" ansible_user: "{{ app_bootstrap_user | default(app_bootstrap_user_default) }}" ansible_password: >- {{ vault_lxc_root_password if ((bootstrap_with_root_password | default(bootstrap_with_root_password_default) | bool) and (vault_lxc_root_password | default('') | length) > 0) else omit }} app_project: "{{ item.0 }}" app_env: "{{ item.1 }}" loop: "{{ selected_projects | product(['dev', 'qa', 'prod']) | list }}" when: - app_projects[item.0] is defined - app_projects[item.0].envs[item.1] is defined - (app_projects[item.0].envs[item.1].ip | default('')) | length > 0 - name: Configure app guests (base OS + app setup) hosts: >- {{ ('app_' ~ app_project ~ '_all') if (app_project is defined and app_project | length > 0) else 'app_all' }} become: true gather_facts: true tasks: - name: Build project/env effective variables ansible.builtin.set_fact: project_def: "{{ app_projects[app_project] }}" env_def: "{{ app_projects[app_project].envs[app_env] }}" when: app_project is defined and app_env is defined - name: Configure base OS ansible.builtin.include_role: name: base_os vars: base_os_backend_port: "{{ (project_def.backend_port | default(app_backend_port)) if project_def is defined else app_backend_port }}" base_os_frontend_port: "{{ (project_def.frontend_port | default(app_frontend_port)) if project_def is defined else app_frontend_port }}" base_os_enable_backend: "{{ project_def.components.backend | default(true) }}" base_os_enable_frontend: "{{ project_def.components.frontend | default(true) }}" base_os_user: "{{ project_def.os_user | default(appuser_name) }}" base_os_user_ssh_public_key: "{{ project_def.os_user_ssh_public_key | default(appuser_ssh_public_key | default('')) }}" # Only override when explicitly provided (avoids self-referential recursion) base_os_packages: "{{ project_def.base_os_packages if (project_def is defined and project_def.base_os_packages is defined) else omit }}" - name: Configure POTE (python/venv + cron) ansible.builtin.include_role: name: pote vars: pote_git_repo: "{{ project_def.repo_url }}" pote_git_branch: "{{ env_def.branch }}" pote_user: "{{ project_def.os_user | default('poteapp') }}" pote_group: "{{ project_def.os_user | default('poteapp') }}" pote_app_dir: "{{ project_def.repo_dest | default('/home/' ~ (project_def.os_user | default('poteapp')) ~ '/pote') }}" pote_env: "{{ app_env }}" pote_db_host: "{{ env_def.pote_db_host | default(project_def.pote_db_host | default('localhost')) }}" pote_db_name: "{{ env_def.pote_db_name | default(project_def.pote_db_name | default('potedb')) }}" pote_db_user: "{{ env_def.pote_db_user | default(project_def.pote_db_user | default('poteuser')) }}" pote_smtp_host: "{{ env_def.pote_smtp_host | default(project_def.pote_smtp_host | default('mail.levkin.ca')) }}" pote_smtp_port: "{{ env_def.pote_smtp_port | default(project_def.pote_smtp_port | default(587)) }}" pote_smtp_user: "{{ env_def.pote_smtp_user | default(project_def.pote_smtp_user | default('')) }}" pote_from_email: "{{ env_def.pote_from_email | default(project_def.pote_from_email | default('')) }}" pote_report_recipients: "{{ env_def.pote_report_recipients | default(project_def.pote_report_recipients | default('')) }}" when: app_project == 'pote' - name: Configure app layout + deploy + systemd ansible.builtin.include_role: name: app_setup vars: app_repo_url: "{{ project_def.repo_url }}" app_repo_dest: "{{ project_def.repo_dest | default('/srv/app') }}" app_repo_branch: "{{ env_def.branch }}" # app_env is already set per-host via add_host (dev/qa/prod) app_owner: "{{ project_def.os_user | default(appuser_name) }}" app_group: "{{ project_def.os_user | default(appuser_name) }}" app_backend_port: "{{ project_def.backend_port | default(app_backend_port) }}" app_frontend_port: "{{ project_def.frontend_port | default(app_frontend_port) }}" app_enable_backend: "{{ project_def.components.backend | default(true) }}" app_enable_frontend: "{{ project_def.components.frontend | default(true) }}" app_backend_install_cmd: "{{ project_def.deploy.backend_install_cmd | default(app_backend_install_cmd) }}" app_backend_migrate_cmd: "{{ project_def.deploy.backend_migrate_cmd | default(app_backend_migrate_cmd) }}" app_backend_start_cmd: "{{ project_def.deploy.backend_start_cmd | default(app_backend_start_cmd) }}" app_frontend_install_cmd: "{{ project_def.deploy.frontend_install_cmd | default(app_frontend_install_cmd) }}" app_frontend_build_cmd: "{{ project_def.deploy.frontend_build_cmd | default(app_frontend_build_cmd) }}" app_frontend_start_cmd: "{{ project_def.deploy.frontend_start_cmd | default(app_frontend_start_cmd) }}" app_env_vars: "{{ env_def.env_vars | default({}) }}" when: app_project != 'pote'