# nextcloud/update_nextcloud_v2.yml - name: Update Nextcloud on VM via Proxmox hosts: proxmox_nextcloud gather_facts: false become: true become_user: root become_method: sudo vars: # vm_pass is stored in Semaphore Secrets as an environment variable. vm_password: "{{ lookup('ansible.builtin.env', 'vm_pass') }}" # Values stored in Semaphore Extra variables. vm_use_sudo: "{{ use_sudo | default(false) | bool }}" debug_level: "{{ DEBUG | default(0) | int }}" retry_count: "{{ RETRIES | default(25) | int }}" # Docker Compose configuration. nextcloud_project: "nextcloud-collabora" nextcloud_service: "nextcloud" # Compose file stored in Git. nextcloud_compose_local_file: >- {{ playbook_dir }}/../docker-compose/docker-compose-nextcloud.yml # Compose file location on the Nextcloud VM. nextcloud_compose_file: >- /data/compose/nextcloud/docker-compose-nextcloud.yml # Temporary Compose file on the Proxmox host. nextcloud_compose_controller_staging_file: >- /tmp/docker-compose-nextcloud-{{ backup_timestamp }}.yml # Temporary Compose file on the Nextcloud VM. nextcloud_compose_vm_staging_file: >- /tmp/docker-compose-nextcloud-{{ backup_timestamp }}.yml # Container names. nextcloud_container: "nextcloud" nextcloud_db_container: "nextcloud-db" redis_container: "redis" # Public endpoints. nextcloud_base_url: "https://cloud.martinfencl.eu" nextcloud_status_url: "{{ nextcloud_base_url }}/status.php" collabora_url: "https://collabora.martinfencl.eu/" collabora_discovery_url: >- https://collabora.martinfencl.eu/hosting/discovery # Backup configuration. backup_root: "/data/compose/nextcloud" # Docker command without CLI hints. docker_cmd: "env DOCKER_CLI_HINTS=0 docker" nextcloud_version_command: >- {{ docker_cmd }} exec -u www-data {{ nextcloud_container | quote }} php occ -V nextcloud_maintenance_off_command: >- {{ docker_cmd }} exec -u www-data {{ nextcloud_container | quote }} php occ maintenance:mode --off # ------------------------------------------------------------------------- # Compose installation script # ------------------------------------------------------------------------- compose_install_script: | set -Eeuo pipefail current_step="Compose installation initialization" trap ' rc=$? echo >&2 echo "Compose installation failed" >&2 echo "Step: ${current_step}" >&2 echo "Line: ${LINENO}" >&2 echo "Command: ${BASH_COMMAND}" >&2 echo "Return code: ${rc}" >&2 exit "${rc}" ' ERR source_file={{ nextcloud_compose_vm_staging_file | quote }} destination_file={{ nextcloud_compose_file | quote }} destination_dir="$(dirname "${destination_file}")" cleanup() { rm -f "${source_file}" } trap cleanup EXIT current_step="checking staged Compose file" echo "Checking staged Docker Compose file" test -s "${source_file}" current_step="validating staged Compose file" echo "Validating staged Docker Compose file" {{ docker_cmd }} compose \ -p {{ nextcloud_project | quote }} \ -f "${source_file}" \ config --quiet current_step="creating Compose destination directory" echo "Ensuring Compose destination directory exists" mkdir -p "${destination_dir}" current_step="comparing Compose files" if [ -f "${destination_file}" ] && \ cmp -s "${source_file}" "${destination_file}" then echo "Compose file is already up to date" exit 0 fi current_step="installing Compose file" echo "Installing Compose file" install \ -m 0644 \ "${source_file}" \ "${destination_file}" current_step="validating installed Compose file" echo "Validating installed Docker Compose file" {{ docker_cmd }} compose \ -p {{ nextcloud_project | quote }} \ -f "${destination_file}" \ config --quiet echo "Compose file updated" # ------------------------------------------------------------------------- # Preflight script # ------------------------------------------------------------------------- preflight_script: | set -Eeuo pipefail current_step="preflight initialization" trap ' rc=$? echo >&2 echo "Preflight check failed" >&2 echo "Step: ${current_step}" >&2 echo "Line: ${LINENO}" >&2 echo "Command: ${BASH_COMMAND}" >&2 echo "Return code: ${rc}" >&2 exit "${rc}" ' ERR current_step="checking Docker executable" echo "Checking Docker executable" command -v docker >/dev/null current_step="checking Docker daemon" echo "Checking Docker daemon" docker version >/dev/null current_step="checking Docker Compose" echo "Checking Docker Compose" docker compose version >/dev/null current_step="checking Docker Compose file" echo "Checking Docker Compose file" test -f {{ nextcloud_compose_file | quote }} current_step="validating Docker Compose configuration" echo "Validating Docker Compose configuration" {{ docker_cmd }} compose \ -p {{ nextcloud_project | quote }} \ -f {{ nextcloud_compose_file | quote }} \ config --quiet current_step="checking required containers" echo "Checking required containers" for container in \ {{ nextcloud_container | quote }} \ {{ nextcloud_db_container | quote }} \ {{ redis_container | quote }} do state="$( docker inspect \ --format '{% raw %}{{.State.Running}}{% endraw %}' \ "${container}" \ 2>/dev/null || true )" if [ "${state}" != "true" ]; then echo "Container ${container} is not running" >&2 exit 1 fi done current_step="checking Nextcloud status" echo "Checking Nextcloud status" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ status current_step="checking MariaDB" echo "Checking MariaDB" {{ docker_cmd }} exec \ {{ nextcloud_db_container | quote }} \ sh -c ' if command -v mariadb-admin >/dev/null 2>&1; then exec mariadb-admin ping \ -h 127.0.0.1 \ --silent else exec mysqladmin ping \ -h 127.0.0.1 \ --silent fi ' current_step="checking Redis" echo "Checking Redis" {{ docker_cmd }} exec \ {{ redis_container | quote }} \ redis-cli -h 127.0.0.1 ping | grep -qx PONG echo "Preflight checks completed successfully" # ------------------------------------------------------------------------- # Backup script # ------------------------------------------------------------------------- backup_script: | set -Eeuo pipefail current_step="backup initialization" trap ' rc=$? echo >&2 echo "Nextcloud backup failed" >&2 echo "Step: ${current_step}" >&2 echo "Line: ${LINENO}" >&2 echo "Command: ${BASH_COMMAND}" >&2 echo "Return code: ${rc}" >&2 exit "${rc}" ' ERR current_step="creating backup directory" echo "Creating backup directory: {{ backup_dir }}" mkdir -p {{ backup_dir | quote }} current_step="enabling maintenance mode" echo "Enabling Nextcloud maintenance mode" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ maintenance:mode --on current_step="creating configuration archive" echo "Backing up Nextcloud configuration and custom applications" {{ docker_cmd }} exec \ {{ nextcloud_container | quote }} \ sh -c ' tar czf /tmp/nextcloud_conf.tgz \ -C /var/www/html \ config custom_apps ' current_step="copying configuration archive" echo "Copying configuration archive" {{ docker_cmd }} cp \ {{ nextcloud_container | quote }}:/tmp/nextcloud_conf.tgz \ {{ (backup_dir ~ '/nextcloud_conf.tgz') | quote }} current_step="removing temporary configuration archive" echo "Removing temporary configuration archive" {{ docker_cmd }} exec \ {{ nextcloud_container | quote }} \ rm -f /tmp/nextcloud_conf.tgz current_step="creating database dump" echo "Backing up Nextcloud database" {{ docker_cmd }} exec \ {{ nextcloud_db_container | quote }} \ sh -c ' if command -v mariadb-dump >/dev/null 2>&1; then exec mariadb-dump \ -u"$MYSQL_USER" \ -p"$MYSQL_PASSWORD" \ "$MYSQL_DATABASE" else exec mysqldump \ -u"$MYSQL_USER" \ -p"$MYSQL_PASSWORD" \ "$MYSQL_DATABASE" fi ' > {{ (backup_dir ~ '/db.sql') | quote }} current_step="validating configuration archive" echo "Validating configuration archive" test -s {{ (backup_dir ~ '/nextcloud_conf.tgz') | quote }} current_step="validating database dump" echo "Validating database dump" test -s {{ (backup_dir ~ '/db.sql') | quote }} current_step="disabling maintenance mode after backup" echo "Disabling Nextcloud maintenance mode after backup" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ maintenance:mode --off current_step="verifying maintenance mode after backup" echo "Verifying maintenance mode after backup" maintenance_state="$( {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ config:system:get maintenance \ 2>/dev/null || true )" if [ "${maintenance_state}" = "true" ] || \ [ "${maintenance_state}" = "1" ]; then echo "Maintenance mode is still enabled after backup" >&2 exit 1 fi echo "Backup completed successfully" echo "Backup directory: {{ backup_dir }}" # ------------------------------------------------------------------------- # Upgrade script # ------------------------------------------------------------------------- upgrade_script: | set -Eeuo pipefail current_step="upgrade initialization" trap ' rc=$? echo >&2 echo "Nextcloud upgrade failed" >&2 echo "Step: ${current_step}" >&2 echo "Line: ${LINENO}" >&2 echo "Command: ${BASH_COMMAND}" >&2 echo "Return code: ${rc}" >&2 exit "${rc}" ' ERR current_step="validating Docker Compose configuration" echo "Validating Docker Compose configuration" {{ docker_cmd }} compose \ -p {{ nextcloud_project | quote }} \ -f {{ nextcloud_compose_file | quote }} \ config --quiet current_step="pulling Nextcloud image" echo "Pulling the current Nextcloud image" {{ docker_cmd }} compose \ -p {{ nextcloud_project | quote }} \ -f {{ nextcloud_compose_file | quote }} \ pull {{ nextcloud_service | quote }} current_step="recreating Nextcloud container" echo "Recreating the Nextcloud container" {{ docker_cmd }} compose \ -p {{ nextcloud_project | quote }} \ -f {{ nextcloud_compose_file | quote }} \ up -d \ --no-deps \ --force-recreate \ {{ nextcloud_service | quote }} current_step="waiting for Nextcloud container" echo "Waiting for the Nextcloud container" container_deadline=$((SECONDS + 180)) while true do container_state="$( docker inspect \ --format '{% raw %}{{.State.Running}}{% endraw %}' \ {{ nextcloud_container | quote }} \ 2>/dev/null || true )" if [ "${container_state}" = "true" ]; then break fi if [ "${SECONDS}" -ge "${container_deadline}" ]; then echo "Timeout waiting for container {{ nextcloud_container }}" >&2 exit 124 fi sleep 3 done current_step="waiting for Nextcloud OCC" echo "Waiting for Nextcloud OCC" occ_deadline=$((SECONDS + 180)) while true do if {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ status >/dev/null 2>&1 then break fi if [ "${SECONDS}" -ge "${occ_deadline}" ]; then echo "Timeout waiting for Nextcloud OCC" >&2 exit 124 fi sleep 3 done current_step="disabling stale maintenance mode" echo "Ensuring maintenance mode is disabled before OCC upgrade" maintenance_state="$( {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ config:system:get maintenance \ 2>/dev/null || true )" if [ "${maintenance_state}" = "true" ] || \ [ "${maintenance_state}" = "1" ]; then echo "Maintenance mode is enabled; disabling it" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ maintenance:mode --off fi current_step="verifying maintenance mode before OCC upgrade" maintenance_state="$( {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ config:system:get maintenance \ 2>/dev/null || true )" if [ "${maintenance_state}" = "true" ] || \ [ "${maintenance_state}" = "1" ]; then echo "Maintenance mode could not be disabled" >&2 exit 1 fi current_step="running Nextcloud database upgrade" echo "Running the Nextcloud database upgrade" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ upgrade --no-interaction current_step="updating Nextcloud applications" echo "Updating Nextcloud applications" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ app:update --all current_step="running Nextcloud maintenance repair" echo "Running Nextcloud maintenance repair" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ maintenance:repair --include-expensive current_step="reading final Nextcloud status" echo "Reading Nextcloud status" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ status echo "Nextcloud upgrade commands completed successfully" # ------------------------------------------------------------------------- # Post-upgrade script # ------------------------------------------------------------------------- postcheck_script: | set -Eeuo pipefail current_step="post-upgrade initialization" trap ' rc=$? echo >&2 echo "Post-upgrade check failed" >&2 echo "Step: ${current_step}" >&2 echo "Line: ${LINENO}" >&2 echo "Command: ${BASH_COMMAND}" >&2 echo "Return code: ${rc}" >&2 exit "${rc}" ' ERR current_step="checking required containers" echo "Checking required containers after upgrade" for container in \ {{ nextcloud_container | quote }} \ {{ nextcloud_db_container | quote }} \ {{ redis_container | quote }} do state="$( docker inspect \ --format '{% raw %}{{.State.Running}}{% endraw %}' \ "${container}" \ 2>/dev/null || true )" if [ "${state}" != "true" ]; then echo "Container ${container} is not running" >&2 exit 1 fi done current_step="checking MariaDB" echo "Checking MariaDB after upgrade" {{ docker_cmd }} exec \ {{ nextcloud_db_container | quote }} \ sh -c ' if command -v mariadb-admin >/dev/null 2>&1; then exec mariadb-admin ping \ -h 127.0.0.1 \ --silent else exec mysqladmin ping \ -h 127.0.0.1 \ --silent fi ' current_step="checking Redis" echo "Checking Redis after upgrade" {{ docker_cmd }} exec \ {{ redis_container | quote }} \ redis-cli -h 127.0.0.1 ping | grep -qx PONG current_step="checking Nextcloud status" echo "Checking Nextcloud status after upgrade" {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ status current_step="checking Nextcloud maintenance state" maintenance_state="$( {{ docker_cmd }} exec -u www-data \ {{ nextcloud_container | quote }} \ php occ config:system:get maintenance \ 2>/dev/null || true )" if [ "${maintenance_state}" = "true" ] || \ [ "${maintenance_state}" = "1" ]; then echo "Nextcloud is still in maintenance mode" >&2 exit 1 fi echo "Post-upgrade checks completed successfully" pre_tasks: - name: Validate VM connection variables ansible.builtin.assert: that: - vm_ip is defined - vm_ip | string | trim | length > 0 - vm_user is defined - vm_user | string | trim | length > 0 - vm_password | string | length > 0 - retry_count | int > 0 fail_msg: >- Missing or invalid vm_ip, vm_user, vm_pass or RETRIES. Check the attached Semaphore Variable Group. quiet: true no_log: true - name: Generate backup timestamp ansible.builtin.set_fact: backup_timestamp: "{{ now(utc=true, fmt='%Y-%m-%d-%H%M%S') }}" changed_when: false - name: Set backup directory ansible.builtin.set_fact: backup_dir: >- {{ backup_root }}/backup-{{ backup_timestamp }} changed_when: false tasks: - name: Ensure sshpass is installed ansible.builtin.apt: name: sshpass state: present update_cache: true # ------------------------------------------------------------------------- # Compose synchronization # ------------------------------------------------------------------------- - name: Nextcloud | Check Compose file in repository ansible.builtin.stat: path: "{{ nextcloud_compose_local_file }}" register: nextcloud_compose_local_stat delegate_to: localhost run_once: true changed_when: false - name: Nextcloud | Fail when repository Compose file is missing ansible.builtin.assert: that: - nextcloud_compose_local_stat.stat.exists - nextcloud_compose_local_stat.stat.isreg - nextcloud_compose_local_stat.stat.size | int > 0 fail_msg: >- Nextcloud Compose file is missing or empty: {{ nextcloud_compose_local_file }} quiet: true run_once: true - name: Nextcloud | Synchronize Compose file to VM block: - name: Nextcloud | Stage Compose file on Proxmox host ansible.builtin.copy: src: "{{ nextcloud_compose_local_file }}" dest: "{{ nextcloud_compose_controller_staging_file }}" mode: "0600" register: nextcloud_compose_controller_copy - name: Nextcloud | Upload Compose file to VM staging path ansible.builtin.command: argv: - sshpass - -e - scp - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - "{{ nextcloud_compose_controller_staging_file }}" - >- {{ vm_user }}@{{ vm_ip }}: {{ nextcloud_compose_vm_staging_file }} environment: SSHPASS: "{{ vm_password }}" register: nextcloud_compose_upload changed_when: nextcloud_compose_upload.rc == 0 no_log: true - name: Nextcloud | Validate and install Compose file on VM ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (compose_install_script | quote)) if vm_use_sudo else ('bash -lc ' ~ (compose_install_script | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nextcloud_compose_install changed_when: >- 'Compose file updated' in (nextcloud_compose_install.stdout | default('')) failed_when: false no_log: true - name: Nextcloud | Show Compose installation result ansible.builtin.debug: msg: | Compose installation return code: {{ nextcloud_compose_install.rc }} STDOUT: {{ nextcloud_compose_install.stdout | default('') | trim }} STDERR: {{ nextcloud_compose_install.stderr | default('') | trim }} when: - debug_level == 1 or nextcloud_compose_install.rc != 0 - name: Nextcloud | Fail when Compose installation failed ansible.builtin.assert: that: - nextcloud_compose_install.rc == 0 fail_msg: | Failed to install Nextcloud Compose file. Source file: {{ nextcloud_compose_local_file }} Destination file: {{ nextcloud_compose_file }} Return code: {{ nextcloud_compose_install.rc }} STDOUT: {{ nextcloud_compose_install.stdout | default('') | trim }} STDERR: {{ nextcloud_compose_install.stderr | default('') | trim }} quiet: true always: - name: Nextcloud | Remove staged Compose file from Proxmox host ansible.builtin.file: path: "{{ nextcloud_compose_controller_staging_file }}" state: absent changed_when: false - name: Nextcloud | Remove staged Compose file from VM ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - "{{ vm_user }}@{{ vm_ip }}" - >- rm -f {{ nextcloud_compose_vm_staging_file | quote }} environment: SSHPASS: "{{ vm_password }}" changed_when: false failed_when: false no_log: true # ------------------------------------------------------------------------- # Preflight # ------------------------------------------------------------------------- - name: Nextcloud | Run preflight checks on VM ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (preflight_script | quote)) if vm_use_sudo else ('bash -lc ' ~ (preflight_script | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nc_preflight changed_when: false failed_when: false no_log: true - name: Nextcloud | Show preflight result ansible.builtin.debug: msg: | Preflight return code: {{ nc_preflight.rc }} STDOUT: {{ nc_preflight.stdout | default('') | trim }} STDERR: {{ nc_preflight.stderr | default('') | trim }} when: - debug_level == 1 or nc_preflight.rc != 0 - name: Nextcloud | Fail when preflight checks failed ansible.builtin.assert: that: - nc_preflight.rc == 0 fail_msg: | Nextcloud preflight checks failed. Return code: {{ nc_preflight.rc }} STDOUT: {{ nc_preflight.stdout | default('') | trim }} STDERR: {{ nc_preflight.stderr | default('') | trim }} quiet: true # ------------------------------------------------------------------------- # Current version # ------------------------------------------------------------------------- - name: Nextcloud | Read current version before upgrade ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (nextcloud_version_command | quote)) if vm_use_sudo else ('bash -lc ' ~ (nextcloud_version_command | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nc_version_before changed_when: false failed_when: false no_log: true - name: Nextcloud | Print current version ansible.builtin.debug: msg: >- Current version: {{ nc_version_before.stdout | default('Nextcloud version is unavailable') | trim }} # ------------------------------------------------------------------------- # Backup and upgrade # ------------------------------------------------------------------------- - name: Nextcloud | Run backup and upgrade block: - name: Nextcloud | Create configuration and database backup ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (backup_script | quote)) if vm_use_sudo else ('bash -lc ' ~ (backup_script | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nc_backup changed_when: nc_backup.rc == 0 failed_when: false no_log: true - name: Nextcloud | Show backup output ansible.builtin.debug: msg: | Backup directory: {{ backup_dir }} Backup return code: {{ nc_backup.rc }} STDOUT: {{ nc_backup.stdout | default('') | trim }} STDERR: {{ nc_backup.stderr | default('') | trim }} when: - debug_level == 1 or nc_backup.rc != 0 - name: Nextcloud | Fail when backup failed ansible.builtin.assert: that: - nc_backup.rc == 0 fail_msg: | Nextcloud backup failed. Backup directory: {{ backup_dir }} Return code: {{ nc_backup.rc }} STDOUT: {{ nc_backup.stdout | default('') | trim }} STDERR: {{ nc_backup.stderr | default('') | trim }} quiet: true - name: Nextcloud | Pull image and run upgrade ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (upgrade_script | quote)) if vm_use_sudo else ('bash -lc ' ~ (upgrade_script | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nc_upgrade changed_when: nc_upgrade.rc == 0 failed_when: false no_log: true - name: Nextcloud | Show upgrade output ansible.builtin.debug: msg: | Upgrade return code: {{ nc_upgrade.rc }} STDOUT: {{ nc_upgrade.stdout | default('') | trim }} STDERR: {{ nc_upgrade.stderr | default('') | trim }} - name: Nextcloud | Fail when upgrade command failed ansible.builtin.assert: that: - nc_upgrade.rc == 0 fail_msg: | Nextcloud upgrade failed. Return code: {{ nc_upgrade.rc }} STDOUT: {{ nc_upgrade.stdout | default('') | trim }} STDERR: {{ nc_upgrade.stderr | default('') | trim }} quiet: true always: - name: Nextcloud | Disable maintenance mode ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (nextcloud_maintenance_off_command | quote)) if vm_use_sudo else ('bash -lc ' ~ (nextcloud_maintenance_off_command | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nc_maint_off changed_when: false failed_when: false no_log: true - name: Nextcloud | Show maintenance mode result ansible.builtin.debug: msg: | Maintenance return code: {{ nc_maint_off.rc }} STDOUT: {{ nc_maint_off.stdout | default('') | trim }} STDERR: {{ nc_maint_off.stderr | default('') | trim }} when: - debug_level == 1 or nc_maint_off.rc != 0 - name: Nextcloud | Verify maintenance mode was disabled ansible.builtin.assert: that: - nc_maint_off.rc == 0 fail_msg: | Failed to disable Nextcloud maintenance mode. Return code: {{ nc_maint_off.rc }} STDOUT: {{ nc_maint_off.stdout | default('') | trim }} STDERR: {{ nc_maint_off.stderr | default('') | trim }} quiet: true # ------------------------------------------------------------------------- # Public Nextcloud readiness # ------------------------------------------------------------------------- - name: Nextcloud | Wait for public status.php ansible.builtin.uri: url: "{{ nextcloud_status_url }}" method: GET return_content: true validate_certs: true status_code: 200 timeout: 20 register: nc_status delegate_to: localhost run_once: true retries: "{{ retry_count }}" delay: 4 until: - nc_status.status | default(0) | int == 200 - nc_status.json is defined - nc_status.json.installed | default(false) | bool - not (nc_status.json.maintenance | default(true) | bool) - not (nc_status.json.needsDbUpgrade | default(true) | bool) changed_when: false # ------------------------------------------------------------------------- # Post-upgrade checks # ------------------------------------------------------------------------- - name: Nextcloud | Run post-upgrade checks on VM ansible.builtin.command: argv: - sshpass - -e - ssh - -o - StrictHostKeyChecking=no - -o - UserKnownHostsFile=/dev/null - -o - LogLevel=ERROR - -o - ConnectTimeout=15 - -o - ServerAliveInterval=10 - -o - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ ('sudo -n bash -lc ' ~ (postcheck_script | quote)) if vm_use_sudo else ('bash -lc ' ~ (postcheck_script | quote)) }} environment: SSHPASS: "{{ vm_password }}" register: nc_postcheck changed_when: false failed_when: false no_log: true - name: Nextcloud | Show post-upgrade checks ansible.builtin.debug: msg: | Post-upgrade return code: {{ nc_postcheck.rc }} STDOUT: {{ nc_postcheck.stdout | default('') | trim }} STDERR: {{ nc_postcheck.stderr | default('') | trim }} when: - debug_level == 1 or nc_postcheck.rc != 0 - name: Nextcloud | Fail when post-upgrade checks failed ansible.builtin.assert: that: - nc_postcheck.rc == 0 fail_msg: | Nextcloud post-upgrade checks failed. Return code: {{ nc_postcheck.rc }} STDOUT: {{ nc_postcheck.stdout | default('') | trim }} STDERR: {{ nc_postcheck.stderr | default('') | trim }} quiet: true # ------------------------------------------------------------------------- # External Collabora checks # ------------------------------------------------------------------------- - name: Nextcloud | Check external Collabora root endpoint ansible.builtin.uri: url: "{{ collabora_url }}" method: GET return_content: true validate_certs: true status_code: 200 timeout: 20 register: collabora_root delegate_to: localhost run_once: true changed_when: false failed_when: >- (collabora_root.status | default(0) | int != 200) or ('OK' not in (collabora_root.content | default(''))) - name: Nextcloud | Check external Collabora discovery endpoint ansible.builtin.uri: url: "{{ collabora_discovery_url }}" method: GET return_content: true validate_certs: true status_code: 200 timeout: 20 register: collabora_discovery delegate_to: localhost run_once: true changed_when: false failed_when: >- (collabora_discovery.status | default(0) | int != 200) or ('