From 7ea0bb86f2794afb8e1da898535d4dbea95deeeb Mon Sep 17 00:00:00 2001 From: "martin.fencl" Date: Wed, 24 Dec 2025 00:50:45 +0100 Subject: [PATCH] test 2 --- nextcloud/update_nextcloud_v2.yml | 335 +++++++++++++++++++----------- 1 file changed, 218 insertions(+), 117 deletions(-) diff --git a/nextcloud/update_nextcloud_v2.yml b/nextcloud/update_nextcloud_v2.yml index e39143f..3a7cf73 100644 --- a/nextcloud/update_nextcloud_v2.yml +++ b/nextcloud/update_nextcloud_v2.yml @@ -1,7 +1,7 @@ # nextcloud/update_nextcloud.yml - name: Update Nextcloud on VM via Proxmox - hosts: proxmox_nextcloud # linux_servers + hosts: proxmox_nextcloud gather_facts: false become: true become_user: root @@ -31,7 +31,20 @@ # Docker command prefix (consistent behavior and quiet hints) docker_prefix: "unalias docker 2>/dev/null || true; DOCKER_CLI_HINTS=0; command docker" - + + # SSH argv base (always run remote command via bash -lc) + ssh_argv_base: + - sshpass + - -e + - ssh + - -o + - StrictHostKeyChecking=no + - -o + - ConnectTimeout=15 + - "{{ vm_user }}@{{ vm_ip }}" + - bash + - -lc + # --- Backup phase commands (run on VM) --- nextcloud_backup_commands: - >- @@ -47,23 +60,6 @@ - >- {{ docker_prefix }} exec nextcloud-db sh -c 'command -v mariadb-dump >/dev/null && mariadb-dump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" || mysqldump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' > "{{ backup_dir }}/db.sql" - # --- Upgrade phase commands (run on VM) --- - nextcloud_upgrade_commands: - - >- - {{ docker_prefix }} compose -p {{ nextcloud_project }} -f {{ nextcloud_compose_file }} pull {{ nextcloud_service }} - - >- - {{ docker_prefix }} compose -p {{ nextcloud_project }} -f {{ nextcloud_compose_file }} up -d --no-deps --force-recreate {{ nextcloud_service }} - - >- - {{ docker_prefix }} exec -u www-data nextcloud php occ maintenance:mode --off - - >- - {{ docker_prefix }} exec -u www-data nextcloud php occ upgrade - - >- - {{ docker_prefix }} exec -u www-data nextcloud php occ app:update --all || true - - >- - {{ docker_prefix }} exec -u www-data nextcloud php occ maintenance:repair --include-expensive || true - - >- - {{ docker_prefix }} exec -u www-data nextcloud php occ maintenance:mode --on - tasks: - name: Ensure sshpass is installed (for password-based SSH) ansible.builtin.apt: @@ -73,40 +69,29 @@ - name: Nextcloud | Show current version before upgrade (DEBUG) ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - bash - - -lc - - 'docker exec -u www-data nextcloud php occ -V || true' + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ -V || true' ] + }} environment: SSHPASS: "{{ vm_pass }}" register: nc_version_before changed_when: false failed_when: false when: DEBUG == 1 + no_log: "{{ DEBUG == 0 }}" # ------------------------- # Backup phase # ------------------------- - - name: Nextcloud | Run backup commands on VM (via SSH) # run plain commands via SSH + - name: Nextcloud | Run backup commands on VM (via SSH) ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - "{{ ('sudo ' if use_sudo else '') + item }}" + argv: >- + {{ + ssh_argv_base + + [ ((use_sudo | bool) | ternary('sudo -n ' ~ item, item)) ] + }} environment: SSHPASS: "{{ vm_pass }}" loop: "{{ nextcloud_backup_commands }}" @@ -115,6 +100,7 @@ label: "backup-cmd-{{ idx }}" register: nc_backup_cmds changed_when: false + failed_when: false no_log: "{{ DEBUG == 0 }}" - name: Nextcloud | Show outputs of backup commands (DEBUG) @@ -140,78 +126,208 @@ label: "backup-cmd-{{ idx }}" # ------------------------- - # Upgrade phase + # Upgrade phase (robust) # ------------------------- - - - name: Nextcloud | Run upgrade commands on VM (via SSH) + - name: Nextcloud | Pull image ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - bash - - -lc - - "{{ ('sudo ' if use_sudo else '') + item }}" + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' compose -p ' ~ nextcloud_project ~ ' -f ' ~ nextcloud_compose_file ~ ' pull ' ~ nextcloud_service ] + }} environment: SSHPASS: "{{ vm_pass }}" - loop: "{{ nextcloud_upgrade_commands }}" - loop_control: - index_var: idx - label: "upgrade-cmd-{{ idx }}" - register: nc_upgrade_cmds + register: nc_pull changed_when: false failed_when: false no_log: "{{ DEBUG == 0 }}" - - name: Nextcloud | Show outputs of upgrade commands (DEBUG) - ansible.builtin.debug: - msg: | - CMD: {{ item.item }} - RC: {{ item.rc }} - STDOUT: - {{ (item.stdout | default('')).strip() }} - STDERR: - {{ (item.stderr | default('')).strip() }} - loop: "{{ nc_upgrade_cmds.results }}" - when: DEBUG == 1 - - - name: Nextcloud | Fail play if any upgrade command failed - ansible.builtin.assert: - that: "item.rc == 0" - fail_msg: "Nextcloud upgrade step failed on VM: {{ item.item }} (rc={{ item.rc }})" - success_msg: "All Nextcloud upgrade commands succeeded." - loop: "{{ nc_upgrade_cmds.results }}" - loop_control: - index_var: idx - label: "upgrade-cmd-{{ idx }}" - - - name: Nextcloud | Disable maintenance mode (only after successful upgrade) + - name: Nextcloud | Recreate service ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - "{{ ('sudo ' if use_sudo else '') }}docker exec -u www-data nextcloud php occ maintenance:mode --off" + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' compose -p ' ~ nextcloud_project ~ ' -f ' ~ nextcloud_compose_file ~ ' up -d --no-deps --force-recreate ' ~ nextcloud_service ] + }} environment: SSHPASS: "{{ vm_pass }}" - register: nc_maint_off + register: nc_up changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Ensure maintenance mode is OFF before occ upgrade + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ maintenance:mode --off || true' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_maint_off_before_upgrade + changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | occ upgrade (attempt 1) + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ upgrade' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_upgrade_1 + changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Remediation if occ upgrade complains about maintenance/upgrade-in-progress + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ item ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + loop: + # Force-disable maintenance flag in config.php (works even if maintenance command is blocked) + - >- + {{ docker_prefix }} exec -u www-data nextcloud php occ config:system:set maintenance --type=boolean --value=false || true + # Repair can move stale updater step files and clear related state + - >- + {{ docker_prefix }} exec -u www-data nextcloud php occ maintenance:repair || true + # Ensure maintenance is off before retry + - >- + {{ docker_prefix }} exec -u www-data nextcloud php occ maintenance:mode --off || true + register: nc_remediate + changed_when: false + failed_when: false + when: >- + nc_upgrade_1.rc != 0 and ( + ('Maybe an upgrade is already in process' in (nc_upgrade_1.stdout | default(''))) or + ('Maybe an upgrade is already in process' in (nc_upgrade_1.stderr | default(''))) or + ('Nextcloud is in maintenance mode' in (nc_upgrade_1.stdout | default(''))) or + ('Nextcloud is in maintenance mode' in (nc_upgrade_1.stderr | default(''))) + ) + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | occ upgrade (attempt 2 after remediation) + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ upgrade' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_upgrade_2 + changed_when: false + failed_when: false + when: nc_upgrade_1.rc != 0 + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Decide final upgrade result + ansible.builtin.set_fact: + nc_upgrade_final: "{{ (nc_upgrade_2 if (nc_upgrade_2 is defined) else nc_upgrade_1) }}" + + - name: Nextcloud | Show occ upgrade output (DEBUG) + ansible.builtin.debug: + msg: | + RC: {{ nc_upgrade_final.rc }} + STDOUT: + {{ (nc_upgrade_final.stdout | default('')).strip() }} + STDERR: + {{ (nc_upgrade_final.stderr | default('')).strip() }} + when: DEBUG == 1 + + - name: Nextcloud | Fail if occ upgrade did not succeed + ansible.builtin.assert: + that: + - nc_upgrade_final.rc == 0 + fail_msg: >- + Nextcloud occ upgrade failed (rc={{ nc_upgrade_final.rc }}). + stdout={{ (nc_upgrade_final.stdout | default('') | trim) }} + stderr={{ (nc_upgrade_final.stderr | default('') | trim) }} + + # Keep the rest under maintenance mode to avoid user activity during app updates/repairs + - name: Nextcloud | Enable maintenance mode for post-upgrade steps + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ maintenance:mode --on' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_maint_on_post + changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Update apps (best-effort) + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ app:update --all || true' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_app_update + changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Run maintenance repair (include expensive) (best-effort) + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ maintenance:repair --include-expensive || true' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_repair + changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Disable maintenance mode (end) + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ maintenance:mode --off' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_maint_off_end + changed_when: false + failed_when: false + no_log: "{{ DEBUG == 0 }}" + + - name: Nextcloud | Show current version after upgrade (DEBUG) + ansible.builtin.command: + argv: >- + {{ + ssh_argv_base + + [ docker_prefix ~ ' exec -u www-data nextcloud php occ -V || true' ] + }} + environment: + SSHPASS: "{{ vm_pass }}" + register: nc_version_after + changed_when: false + failed_when: false + when: DEBUG == 1 no_log: "{{ DEBUG == 0 }}" # ------------------------- # Readiness check (status.php) # ------------------------- - - name: Nextcloud | Wait for status.php (controller first) ansible.builtin.uri: url: "{{ nextcloud_status_url }}" @@ -230,26 +346,11 @@ - name: Nextcloud | VM-side fetch status.php (JSON via Python) ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - bash - - -lc - - | - python3 - <<'PY' - import json, urllib.request, sys - try: - with urllib.request.urlopen("{{ nextcloud_status_url }}", timeout=15) as r: - sys.stdout.write(r.read().decode()) - except Exception: - pass - PY + argv: >- + {{ + ssh_argv_base + + [ "python3 - <<'PY'\nimport sys, urllib.request\ntry:\n with urllib.request.urlopen(\"" ~ nextcloud_status_url ~ "\", timeout=15) as r:\n sys.stdout.write(r.read().decode())\nexcept Exception:\n pass\nPY" ] + }} environment: SSHPASS: "{{ vm_pass }}" register: nc_status_vm