# Copyright 2019-2024 Chris Croome # # This file is part of the Webarchitects PHP Ansible role. # # The Webarchitects PHP Ansible role is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. # # The Webarchitects PHP Ansible role is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with the Webarchitects PHP Ansible role. If not, see <https://www.gnu.org/licenses/>. --- - name: PHP configuration file edited block: - name: Slurp existing PHP configuration file ansible.builtin.slurp: path: "{{ php_conf_file }}" register: php_conf_file_b64encoded - name: Set a fact for the existing PHP configuration file variables ansible.builtin.set_fact: php_conf_file_existing_vars: "{{ php_conf_file_b64encoded['content'] | ansible.builtin.b64decode | community.general.jc('ini') }}" php_conf_file_no_extra_spaces: "{%- if (php_conf_file | ansible.builtin.dirname | ansible.builtin.split('/') | last) == 'mods-available' -%}true{%- else -%}false{%- endif -%}" - name: Debug the existing PHP configuration file variables ansible.builtin.debug: var: php_conf_file_existing_vars verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: Debug the PHP configuration no extra spaces variable ansible.builtin.debug: var: php_conf_file_no_extra_spaces verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: Set a fact for the existing PHP configuration file sections block: - name: Set a fact to indicate that the existing PHP configuration file has sections ansible.builtin.set_fact: php_conf_file_existing_sections: >- {%- if (php_conf_file_existing_vars | ansible.builtin.json_query('*[]') | map('ansible.builtin.type_debug') | unique)[0] == "dict" -%}true{%- else -%}false{%- endif -%} - name: Debug the existing PHP configuration file sections variables ansible.builtin.debug: var: php_conf_file_existing_sections verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: Debug the existing PHP configuration file sections ansible.builtin.debug: var: php_conf_file_existing_vars.keys() verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" when: php_conf_file_existing_sections | bool when: php_conf_file_existing_vars | length > 0 - name: Set a fact for the proposed PHP configuration file variables ansible.builtin.set_fact: php_conf_file_proposed_vars: "{{ php_config | community.general.json_query(php_conf_file_proposed_vars_json_query) }}" vars: php_conf_file_proposed_vars_json_query: "[?state=='present'].files[]|[?path=='{{ php_conf_file }}'].conf|[0]" - name: Debug the proposed PHP configuration file variables ansible.builtin.debug: var: php_conf_file_proposed_vars verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: The proposed PHP configuration file variables are empty or a dictionary ansible.builtin.assert: that: - ( php_conf_file_proposed_vars | length == 0 ) or ( php_conf_file_proposed_vars | ansible.builtin.type_debug == "dict" ) quiet: "{% if ansible_verbosity == 0 %}true{% else %}false{% endif %}" fail_msg: >- The php_conf_file_proposed_vars variable should be empty or be a dictionary, neither appears to be the case, it is set to {{ php_conf_file_proposed_vars }} - name: Set a fact for the absent PHP configuration file variables ansible.builtin.set_fact: php_conf_file_absent_vars: "{{ php_config | community.general.json_query(php_conf_file_absent_vars_json_query) }}" vars: php_conf_file_absent_vars_json_query: "[?state=='present'].files[]|[?path=='{{ php_conf_file }}'].conf_absent|[0]" - name: Debug the absent PHP configuration file variables ansible.builtin.debug: var: php_conf_file_absent_vars verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: The absent PHP configuration file variables are empty or a dictionary ansible.builtin.assert: that: - ( php_conf_file_absent_vars | length == 0 ) or ( php_conf_file_absent_vars | ansible.builtin.type_debug == "dict" ) quiet: "{% if ansible_verbosity == 0 %}true{% else %}false{% endif %}" fail_msg: >- The php_conf_file_absent_vars variable should be empty or be a dictionary, neither appears to be the case, it is set to {{ php_conf_file_absent_vars }} - name: Set a fact for the proposed PHP configuration file sections block: - name: Set a fact to indicate that the proposed PHP configuration file has sections ansible.builtin.set_fact: php_conf_file_proposed_sections: >- {%- if (php_conf_file_proposed_vars | ansible.builtin.json_query('*[]') | map('ansible.builtin.type_debug') | unique)[0] == "dict" -%}true{%- else -%}false{%- endif -%} - name: Debug the proposed PHP configuration file sections variables ansible.builtin.debug: var: php_conf_file_proposed_sections verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: Debug the proposed PHP configuration file sections ansible.builtin.debug: var: php_conf_file_proposed_vars.keys() verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" when: php_conf_file_proposed_sections | bool - name: When the existing files has sections and proposed config also has sections ansible.builtin.assert: that: - php_conf_file_existing_sections == php_conf_file_proposed_sections quiet: "{% if ansible_verbosity == 0 %}true{% else %}false{% endif %}" fail_msg: >- The php_conf_file_existing_sections and php_conf_file_proposed_sections variables should match when they are defined, they don't, they are set to {{ php_conf_file_existing_sections }} and {{ php_conf_file_proposed_sections }} when: php_conf_file_existing_vars | length > 0 when: php_conf_file_proposed_vars | length > 0 - name: Set facts for the PHP configuration file ansible.builtin.set_fact: php_conf_file_backup: "{{ php_conf_file | ansible.builtin.dirname }}/.{{ php_conf_file | ansible.builtin.basename }}.{{ ansible_date_time.iso8601_basic_short }}.bak" php_conf_file_changed: false php_conf_file_sapi: "{{ php_conf_file | ansible.builtin.split(php_file_path_separator) | community.general.json_query('[4]') }}" php_conf_file_version: "{{ php_conf_file | ansible.builtin.split(php_file_path_separator) | community.general.json_query('[3]') }}" - name: Debug the PHP configuration file backup path ansible.builtin.debug: var: php_conf_file_backup verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: Debug the PHP configuration file PHP SAPI ansible.builtin.debug: var: php_conf_file_sapi verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: Debug the PHP configuration file PHP version ansible.builtin.debug: var: php_conf_file_version verbosity: "{% if ansible_check_mode | bool %}1{% else %}2{% endif %}" - name: File backup present ansible.builtin.copy: src: "{{ php_conf_file }}" dest: "{{ php_conf_file_backup }}" remote_src: true mode: "0644" owner: root group: root changed_when: false - name: Include the tasks to edit the section-less PHP configuration file ansible.builtin.include_tasks: file_edited_present.yml when: - php_conf_file_proposed_vars is defined - php_conf_file_proposed_vars | length > 0 - php_conf_file_proposed_vars | ansible.builtin.type_debug == "dict" - not php_conf_file_existing_sections | bool - not php_conf_file_proposed_sections | bool - name: Include the tasks to remove config from the section-less PHP configuration file ansible.builtin.include_tasks: file_edited_absent.yml when: - php_conf_file_absent_vars is defined - php_conf_file_absent_vars | length > 0 - php_conf_file_absent_vars | ansible.builtin.type_debug == "dict" - not php_conf_file_existing_sections | bool - not php_conf_file_proposed_sections | bool - name: Include the PHP configuration file sections config edited tasks ansible.builtin.include_tasks: file_sections_edited.yml when: - php_conf_file_proposed_vars is defined - php_conf_file_proposed_vars | length > 0 - php_conf_file_proposed_vars | ansible.builtin.type_debug == "dict" - php_conf_file_existing_sections | bool - php_conf_file_proposed_sections | bool - name: Include the PHP configuration file sections config absent tasks ansible.builtin.include_tasks: file_sections_absent.yml when: - php_conf_file_absent_vars is defined - php_conf_file_absent_vars | length > 0 - php_conf_file_absent_vars | ansible.builtin.type_debug == "dict" - php_conf_file_existing_sections | bool - php_conf_file_proposed_sections | bool - name: Ansible managed comment present at the top of the file ansible.builtin.lineinfile: path: "{{ php_conf_file }}" line: "; {{ php_ansible_managed }}" state: present insertbefore: BOF mode: "0644" owner: root group: root - name: Vim syntaxhighlighting modeline present at the end of the file ansible.builtin.lineinfile: path: "{{ php_conf_file }}" line: "; vim: syntax=dosini" state: present insertafter: EOF mode: "0644" owner: root group: root - name: Test and reload PHP configuration file when file is in a FPM directory and the init system is systemd block: - name: Test PHP configuration block: - name: PHP FPM configtest ansible.builtin.command: "php-fpm{{ php_conf_file_version }} --test" check_mode: false changed_when: false register: php_fpm_test failed_when: >- ( php_fpm_test.rc != 0 ) or ( "Failed" in php_fpm_test.stderr ) rescue: - name: Copy broken PHP configuration file ansible.builtin.copy: src: "{{ php_conf_file }}" dest: "{{ php_conf_file_backup }}.broken" remote_src: true mode: "0644" owner: root group: root - name: Check if a backup files exists ansible.builtin.stat: path: "{{ php_conf_file_backup }}" register: php_conf_file_backup_path - name: Copy backup file over edited file as the configuration test failed ansible.builtin.copy: src: "{{ php_conf_file_backup }}" dest: "{{ php_conf_file }}" remote_src: true mode: "0644" owner: root group: root when: php_conf_file_backup_path.stat.exists | bool - name: Debug PHP configuration file test failure ansible.builtin.debug: var: php_fpm_test.stderr_lines - name: Fail as there was a problem with the updated configuration file ansible.builtin.fail: msg: "The original configuration has been restored and the broken file is available at {{ php_conf_file_backup }}.broken" - name: PHP FPM reloaded ansible.builtin.systemd_service: name: "php{{ php_conf_file_version }}-fpm" state: reloaded - name: Check that PHP FPM is running ansible.builtin.service_facts: register: php_service_facts until: php_service_facts | community.general.json_query(php_service_jpq) == "running" retries: 20 delay: 1 vars: php_service_jpq: 'ansible_facts.services.["php{{ php_conf_file_version }}-fpm.service"]|[0].state' when: - php_init is defined - php_init == "systemd" - php_conf_file_version in php_ver_installed - php_conf_file_changed | bool - php_conf_file_sapi is defined - php_conf_file_sapi == "fpm" - name: File backup absent when the PHP configuration file is unchanged ansible.builtin.file: path: "{{ php_conf_file_backup }}" state: absent changed_when: false when: not php_conf_file_changed | bool tags: - php - php_cfg - php_conf ...