From e47ab0e538e162cc4d4278118a972359bf9977ef Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 14 Nov 2025 13:09:01 +0000 Subject: [PATCH] Update playbooks/nextcloud-upgrade.yml --- playbooks/nextcloud-upgrade.yml | 219 +++++++++++++++++++------------- 1 file changed, 131 insertions(+), 88 deletions(-) diff --git a/playbooks/nextcloud-upgrade.yml b/playbooks/nextcloud-upgrade.yml index d75236d..bff50cd 100644 --- a/playbooks/nextcloud-upgrade.yml +++ b/playbooks/nextcloud-upgrade.yml @@ -1,21 +1,32 @@ -- name: Resilient Nextcloud upgrade on Rocky Linux +- name: Resilient & Environment-Aware Nextcloud Upgrade hosts: all become: yes - vars: - # --- Configuration Variables --- - nextcloud_dir: /var/www/nextcloud - web_user: apache - php_cli: /usr/bin/php - service_name: httpd - # --- Environment for occ commands --- - # This avoids setting TMPDIR on every single task + # ---------------------------------------------------------------------- + # ⚠️ REQUIRED USER VARIABLES + # ---------------------------------------------------------------------- + # YOU MUST EDIT THESE 3 VARIABLES TO MATCH YOUR SERVER + vars: + # 1. The full path to your Nextcloud installation + nextcloud_dir: /var/www/nextcloud + + # 2. The *exact* PHP CLI binary your Nextcloud uses + # (e.g., /usr/bin/php, /usr/bin/php8.1, /opt/remi/php81/root/bin/php) + php_cli_path: /usr/bin/php + + # 3. The service name for your webserver or PHP-FPM + # (e.g., httpd, apache2, nginx, php8.1-fpm) + web_service_name: httpd + + # ---------------------------------------------------------------------- + # INTERNAL PLAYBOOK VARIABLES + # ---------------------------------------------------------------------- + vars: + # Environment for all occ commands occ_env: TMPDIR: /tmp - - # --- Core Apps --- - # Add any app you consider "core" and do NOT want to disable. - # user_ldap is a good example if you use it for auth. + + # Core apps to NOT disable during upgrade core_apps: - activity - twofactor_nextcloud_notification @@ -34,91 +45,123 @@ tasks: - - name: Reminder - ensure backups exist - debug: - msg: "⚠️ CRITICAL: Ensure you have valid DB + data directory backups BEFORE PROCEEDING!" + # ---------------------------------------------------------------------- + # 🛡️ STAGE 1: PREFLIGHT CHECKS & FACT GATHERING + # ---------------------------------------------------------------------- + - name: 1.1 - Validate 'nextcloud_dir' + stat: + path: "{{ nextcloud_dir }}/config/config.php" + register: config_stat - - name: Pause playbook for 10 seconds to allow backup cancellation - pause: - seconds: 10 - prompt: "Press Ctrl+C and then 'A' to abort if you do not have backups." + - name: 1.2 - Fail if 'config.php' not found + fail: + msg: | + 'config.php' not found at '{{ nextcloud_dir }}/config/config.php'. + Please correct the 'nextcloud_dir' variable in the playbook. + when: not config_stat.stat.exists - # ---------------------------------------------------------------------- - # Step 1: Check current version - # ---------------------------------------------------------------------- - - name: Get current Nextcloud version - command: "{{ php_cli }} occ status" + - name: 1.3 - 🧠 DISCOVER 'nc_web_user' from 'config.php' owner + set_fact: + nc_web_user: "{{ config_stat.stat.owner }}" + + - name: 1.4 - Display discovered file owner + debug: + msg: "Discovered Nextcloud web user: {{ nc_web_user }}" + + - name: 1.5 - Validate 'php_cli_path' + stat: + path: "{{ php_cli_path }}" + register: php_stat + + - name: 1.6 - Fail if 'php_cli_path' is not an executable file + fail: + msg: | + 'php_cli_path' ('{{ php_cli_path }}') does not exist or is not executable. + Please correct the 'php_cli_path' variable. + when: not php_stat.stat.exists or not php_stat.stat.isexe + + - name: 1.7 - Validate 'web_service_name' + service_facts: + + - name: 1.8 - Fail if 'web_service_name' is not a known service + fail: + msg: | + 'web_service_name' ('{{ web_service_name }}') is not a valid service on this host. + Please correct the 'web_service_name' variable. + when: "web_service_name not in ansible_facts.services" + + - name: 1.9 - Get current NC status (and check if installed) + command: "{{ php_cli_path }} occ status" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" register: occ_status changed_when: false failed_when: "'installed: true' not in occ_status.stdout" - - name: Parse and show current version + - name: 1.10 - Parse and show current version set_fact: current_version: "{{ occ_status.stdout | regex_search('versionstring: (.*)', '\\1') | first | default('unknown') }}" when: occ_status.stdout is defined - - name: Show current version + - name: 1.11 - Show current version debug: msg: "Current Nextcloud version: {{ current_version }}" # ---------------------------------------------------------------------- - # Step 2: Disable 3rd party apps + # 🔄 STAGE 2: PRE-UPGRADE PREPARATION # ---------------------------------------------------------------------- - - name: Get list of installed apps - command: "{{ php_cli }} occ app:list" + - name: 2.1 - Backup Reminder + debug: + msg: "⚠️ CRITICAL: Ensure you have valid DB + data directory backups BEFORE PROCEEDING!" + + - name: 2.2 - Pause playbook for 10 seconds to allow cancellation + pause: + seconds: 10 + prompt: "Press Ctrl+C and then 'A' to abort if you do not have backups." + + - name: 2.3 - Get list of installed apps + command: "{{ php_cli_path }} occ app:list" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" register: app_list changed_when: false - - name: Disable all non-core 3rd party apps - command: "{{ php_cli }} occ app:disable {{ item }}" + - name: 2.4 - Disable all non-core 3rd party apps + command: "{{ php_cli_path }} occ app:disable {{ item }}" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" loop: "{{ app_list.stdout_lines | select('match', '^[ ]*-[ ](.*):[ ]enabled$') | map('regex_replace', '^[ ]*-[ ](.*):[ ]enabled$', '\\1') | list }}" register: app_disable_result when: item not in core_apps ignore_errors: yes # Don't fail if an app can't be disabled - - name: Save list of successfully disabled apps for re-enabling + - name: 2.5 - Save list of successfully disabled apps set_fact: disabled_apps: "{{ app_disable_result.results | selectattr('changed', 'defined') | selectattr('changed') | map(attribute='item') | list }}" when: app_disable_result is defined and app_disable_result.results is defined # ---------------------------------------------------------------------- - # Step 3: Begin Upgrade Transaction Block + # 🚀 STAGE 3: TRANSACTIONAL UPGRADE BLOCK # ---------------------------------------------------------------------- - - name: Perform Nextcloud Upgrade (Transactional) + - name: 3.0 - Perform Nextcloud Upgrade (Transactional) block: - - name: Check if maintenance mode is already active - command: "{{ php_cli }} occ maintenance:mode" + - name: 3.1 - Enable maintenance mode + command: "{{ php_cli_path }} occ maintenance:mode --on" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" - register: maint_status - changed_when: false - failed_when: false # Don't fail, just check output - - - name: Enable maintenance mode - command: "{{ php_cli }} occ maintenance:mode --on" - args: - chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" - when: "'maintenance mode is not active' in maint_status.stdout" - - name: Run Nextcloud CLI updater (with retry logic) - command: "{{ php_cli }} updater/updater.phar --no-interaction" + - name: 3.2 - Run Nextcloud CLI updater (with retry logic) + command: "{{ php_cli_path }} updater/updater.phar --no-interaction" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" register: updater_result until: @@ -126,93 +169,93 @@ - "'Update successful' in updater_result.stdout or 'Nextcloud is already latest version' in updater_result.stdout" retries: 3 delay: 10 - # This is the final failure check for the updater itself failed_when: - updater_result.rc != 0 - "'Nextcloud is already latest version' not in updater_result.stdout" - - name: Run occ upgrade - command: "{{ php_cli }} occ upgrade" + - name: 3.3 - Run occ upgrade (This is the most critical step) + command: "{{ php_cli_path }} occ upgrade" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" - # CRITICAL: Do NOT ignore errors here. This is the main failure point. + # CRITICAL: Do NOT ignore errors here. If this fails, we must stop. - - name: Add missing indices (optional cleanup) - command: "{{ php_cli }} occ db:add-missing-indices" + - name: 3.4 - Run optional cleanup commands + command: "{{ item }}" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" - ignore_errors: yes + loop: + - "{{ php_cli_path }} occ db:add-missing-indices" + - "{{ php_cli_path }} occ db:convert-filecache-bigint --no-interaction" + ignore_errors: yes # These are optional, so we ignore failures - - name: Convert filecache bigint fields (optional cleanup) - command: "{{ php_cli }} occ db:convert-filecache-bigint --no-interaction" + - name: 3.5 - Disable maintenance mode (SUCCESS) + command: "{{ php_cli_path }} occ maintenance:mode --off" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" - environment: "{{ occ_env }}" - ignore_errors: yes - - - name: Disable maintenance mode (SUCCESS) - command: "{{ php_cli }} occ maintenance:mode --off" - args: - chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" - - name: Re-enable previously disabled 3rd party apps - command: "{{ php_cli }} occ app:enable {{ item }}" + - name: 3.6 - Re-enable previously disabled 3rd party apps + command: "{{ php_cli_path }} occ app:enable {{ item }}" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" loop: "{{ disabled_apps | default([]) }}" when: disabled_apps is defined and disabled_apps | length > 0 ignore_errors: yes # Don't let a single app fail the whole restart - - name: Restart web service + - name: 3.7 - Restart web service service: - name: "{{ service_name }}" + name: "{{ web_service_name }}" state: restarted - - name: Check new Nextcloud version - command: "{{ php_cli }} occ status" + - name: 3.8 - Verify new version + command: "{{ php_cli_path }} occ status" args: chdir: "{{ nextcloud_dir }}" - become_user: "{{ web_user }}" + become_user: "{{ nc_web_user }}" environment: "{{ occ_env }}" register: occ_status_after + changed_when: false - - name: Display new version info + - name: 3.9 - Display new version info debug: var: occ_status_after.stdout - - name: ✅ Upgrade Complete + - name: 3.10 - ✅ Upgrade Complete debug: msg: "Nextcloud successfully upgraded!" # ---------------------------------------------------------------------- - # Step 4: Rescue Block (This ONLY runs if the 'block' above fails) + # 🆘 STAGE 4: RESCUE BLOCK (This ONLY runs if STAGE 3 fails) # ---------------------------------------------------------------------- rescue: - name: ❌ UPGRADE FAILED - REPORTING debug: msg: | - !!!!!!!!!! UPGRADE FAILED !!!!!!!!!! + !!!!!!!!!! CRITICAL UPGRADE FAILURE !!!!!!!!!! + + The playbook has failed during the upgrade block. The server is being LEFT IN MAINTENANCE MODE for manual inspection. Host: {{ inventory_hostname }} + Discovered Web User: {{ nc_web_user }} + Please SSH to the host and check logs to fix manually. Logs to check: - {{ nextcloud_dir }}/data/updater.log - {{ nextcloud_dir }}/data/nextcloud.log - Run '{{ php_cli }} occ maintenance:mode --off' manually when fixed. + Once fixed, run this command manually as the web user: + '{{ php_cli_path }} occ maintenance:mode --off' - - name: Fail the playbook run + - name: 4.1 - Fail the playbook run fail: msg: "Nextcloud upgrade failed. Host is in maintenance mode." \ No newline at end of file