diff --git a/nextcloud/check_stack_nextcloud.yml b/nextcloud/check_stack_nextcloud.yml index ddd3221..29b1c0a 100644 --- a/nextcloud/check_stack_nextcloud.yml +++ b/nextcloud/check_stack_nextcloud.yml @@ -8,7 +8,6 @@ become_method: sudo vars: - # Internal normalized variables vm_use_sudo: "{{ use_sudo | default(false) | bool }}" vm_commands: @@ -30,8 +29,8 @@ - >- set -o pipefail; - timeout 180s bash -x - /data/compose/nextcloud/stack-health.sh + timeout 180s + bash -x /data/compose/nextcloud/stack-health.sh 0 fail_msg: >- Missing vm_ip, vm_user or vm_pass. - Configure these variables in the attached Semaphore Variable Group. + Configure them in the attached Semaphore Variable Group. quiet: true no_log: true @@ -69,13 +68,17 @@ - UserKnownHostsFile=/dev/null - -o - ConnectTimeout=15 + - -o + - ServerAliveInterval=10 + - -o + - ServerAliveCountMax=3 - "{{ vm_user }}@{{ vm_ip }}" - >- {{ - ('sudo -n bash -lc ' + (item | quote)) + ('sudo -n bash -lc ' ~ (item | quote)) if vm_use_sudo else - ('bash -lc ' + (item | quote)) + ('bash -lc ' ~ (item | quote)) }} environment: SSHPASS: "{{ vm_pass }}" @@ -91,10 +94,12 @@ msg: | CMD: {{ item.item }} RC: {{ item.rc }} + STDOUT: - {{ (item.stdout | default('') | trim) }} + {{ item.stdout | default('') | trim }} + STDERR: - {{ (item.stderr | default('') | trim) }} + {{ item.stderr | default('') | trim }} loop: "{{ vm_cmds.results }}" loop_control: label: "{{ item.item }}" @@ -103,13 +108,13 @@ ansible.builtin.assert: that: - item.rc == 0 - fail_msg: >- + fail_msg: | Command failed on VM: {{ item.item }} - (rc={{ item.rc }}) + RC: {{ item.rc }} STDERR: {{ item.stderr | default('') | trim }} quiet: true loop: "{{ vm_cmds.results }}" loop_control: - label: "{{ item.item }}" + label: "{{ item.item }}" \ No newline at end of file diff --git a/nextcloud/update_nextcloud_v2.yml b/nextcloud/update_nextcloud_v2.yml index 16b3265..13e9629 100644 --- a/nextcloud/update_nextcloud_v2.yml +++ b/nextcloud/update_nextcloud_v2.yml @@ -1,4 +1,4 @@ -# nextcloud/update_nextcloud.yml +# nextcloud/update_nextcloud_v2.yml - name: Update Nextcloud on VM via Proxmox hosts: proxmox_nextcloud @@ -8,7 +8,7 @@ become_method: sudo vars: - # Internal normalized variables + # Normalized values from Semaphore Variable Group vm_use_sudo: "{{ use_sudo | default(false) | bool }}" debug_level: "{{ DEBUG | default(0) | int }}" retry_count: "{{ RETRIES | default(25) | int }}" @@ -21,51 +21,64 @@ nextcloud_container: "nextcloud" nextcloud_db_container: "nextcloud-db" - backup_dir: >- - /data/compose/nextcloud/backup-{{ - lookup('pipe', 'date +%F-%H%M%S') - }} - nextcloud_base_url: "https://cloud.martinfencl.eu" nextcloud_status_url: "{{ nextcloud_base_url }}/status.php" + backup_timestamp: "{{ lookup('pipe', 'date +%F-%H%M%S') }}" + backup_dir: "/data/compose/nextcloud/backup-{{ backup_timestamp }}" + docker_prefix: >- unalias docker 2>/dev/null || true; export DOCKER_CLI_HINTS=0; command docker - # Backup commands executed inside the Nextcloud VM + nextcloud_version_command: >- + docker exec -u www-data + {{ nextcloud_container }} + php occ -V + + nextcloud_maintenance_off_command: >- + docker exec -u www-data + {{ nextcloud_container }} + php occ maintenance:mode --off + + # Commands executed before the container update nextcloud_backup_commands: - >- mkdir -p {{ backup_dir | quote }} - >- {{ docker_prefix }} - exec -u www-data {{ nextcloud_container }} + exec -u www-data + {{ nextcloud_container }} php occ maintenance:mode --on - >- {{ docker_prefix }} - exec {{ nextcloud_container }} + exec + {{ nextcloud_container }} sh -c 'tar czf /tmp/nextcloud_conf.tgz - -C /var/www/html config custom_apps' + -C /var/www/html + config custom_apps' - >- {{ docker_prefix }} cp {{ nextcloud_container }}:/tmp/nextcloud_conf.tgz - {{ (backup_dir + '/nextcloud_conf.tgz') | quote }} + {{ (backup_dir ~ '/nextcloud_conf.tgz') | quote }} - >- {{ docker_prefix }} - exec {{ nextcloud_container }} + exec + {{ nextcloud_container }} rm -f /tmp/nextcloud_conf.tgz - >- set -o pipefail; {{ docker_prefix }} - exec {{ nextcloud_db_container }} + exec + {{ nextcloud_db_container }} sh -c 'if command -v mariadb-dump >/dev/null 2>&1; then exec mariadb-dump @@ -78,22 +91,25 @@ -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"; fi' - > {{ (backup_dir + '/db.sql') | quote }} + > {{ (backup_dir ~ '/db.sql') | quote }} - >- - test -s {{ (backup_dir + '/nextcloud_conf.tgz') | quote }} + test -s + {{ (backup_dir ~ '/nextcloud_conf.tgz') | quote }} - >- - test -s {{ (backup_dir + '/db.sql') | quote }} + test -s + {{ (backup_dir ~ '/db.sql') | quote }} - # Upgrade commands executed while maintenance mode remains enabled + # Commands executed while maintenance mode is enabled nextcloud_upgrade_commands: - >- {{ docker_prefix }} compose -p {{ nextcloud_project | quote }} -f {{ nextcloud_compose_file | quote }} - pull {{ nextcloud_service | quote }} + pull + {{ nextcloud_service | quote }} - >- {{ docker_prefix }} @@ -108,29 +124,44 @@ - >- timeout 180s bash -c 'until docker inspect - --format "{{ '{{' }}.State.Running{{ '}}' }}" + --format="{{ '{{' }}.State.Running{{ '}}' }}" {{ nextcloud_container | quote }} 2>/dev/null | grep -qx true; - do sleep 3; done' + do + sleep 3; + done' + + - >- + timeout 180s bash -c + 'until docker exec -u www-data + {{ nextcloud_container | quote }} + php occ status >/dev/null 2>&1; + do + sleep 3; + done' - >- {{ docker_prefix }} - exec -u www-data {{ nextcloud_container }} + exec -u www-data + {{ nextcloud_container }} php occ upgrade - >- {{ docker_prefix }} - exec -u www-data {{ nextcloud_container }} + exec -u www-data + {{ nextcloud_container }} php occ app:update --all - >- {{ docker_prefix }} - exec -u www-data {{ nextcloud_container }} + exec -u www-data + {{ nextcloud_container }} php occ maintenance:repair --include-expensive - >- {{ docker_prefix }} - exec -u www-data {{ nextcloud_container }} + exec -u www-data + {{ nextcloud_container }} php occ status pre_tasks: @@ -143,9 +174,10 @@ - vm_user | string | trim | length > 0 - vm_pass is defined - vm_pass | string | length > 0 + - retry_count | int > 0 fail_msg: >- - Missing vm_ip, vm_user or vm_pass. - Configure these variables in the attached Semaphore Variable Group. + Missing or invalid vm_ip, vm_user, vm_pass or RETRIES. + Configure them in the attached Semaphore Variable Group. quiet: true no_log: true @@ -171,22 +203,12 @@ - "{{ vm_user }}@{{ vm_ip }}" - >- {{ - ( - 'sudo -n bash -lc ' + - ( - 'docker exec -u www-data nextcloud php occ -V' - | quote - ) - ) + ('sudo -n bash -lc ' ~ + (nextcloud_version_command | quote)) if vm_use_sudo else - ( - 'bash -lc ' + - ( - 'docker exec -u www-data nextcloud php occ -V' - | quote - ) - ) + ('bash -lc ' ~ + (nextcloud_version_command | quote)) }} environment: SSHPASS: "{{ vm_pass }}" @@ -197,303 +219,243 @@ - name: Nextcloud | Print current version ansible.builtin.debug: - msg: "{{ nc_version_before.stdout | default('Version unavailable') }}" + msg: >- + {{ + nc_version_before.stdout + | default('Nextcloud version is unavailable') + | trim + }} when: debug_level == 1 - # ------------------------------------------------------------------------- - # Backup - # ------------------------------------------------------------------------- + - name: Nextcloud | Run backup and upgrade + block: + # --------------------------------------------------------------------- + # Backup + # --------------------------------------------------------------------- - - name: Nextcloud | Run backup commands on VM - ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - UserKnownHostsFile=/dev/null - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - >- - {{ - ('sudo -n bash -lc ' + (item | quote)) - if vm_use_sudo - else - ('bash -lc ' + (item | quote)) - }} - environment: - SSHPASS: "{{ vm_pass }}" - loop: "{{ nextcloud_backup_commands }}" - loop_control: - index_var: backup_index - label: "backup-cmd-{{ backup_index }}" - register: nc_backup_cmds - changed_when: false - failed_when: false + - name: Nextcloud | Run backup commands on VM + ansible.builtin.command: + argv: + - sshpass + - -e + - ssh + - -o + - StrictHostKeyChecking=no + - -o + - UserKnownHostsFile=/dev/null + - -o + - ConnectTimeout=15 + - -o + - ServerAliveInterval=10 + - -o + - ServerAliveCountMax=3 + - "{{ vm_user }}@{{ vm_ip }}" + - >- + {{ + ('sudo -n bash -lc ' ~ (item | quote)) + if vm_use_sudo + else + ('bash -lc ' ~ (item | quote)) + }} + environment: + SSHPASS: "{{ vm_pass }}" + loop: "{{ nextcloud_backup_commands }}" + loop_control: + index_var: backup_index + label: "backup-cmd-{{ backup_index }}" + register: nc_backup_cmds + changed_when: false + failed_when: false - - name: Nextcloud | Show backup command outputs - ansible.builtin.debug: - msg: | - CMD: {{ item.item }} - RC: {{ item.rc }} - STDOUT: - {{ item.stdout | default('') | trim }} - STDERR: - {{ item.stderr | default('') | trim }} - loop: "{{ nc_backup_cmds.results }}" - loop_control: - index_var: backup_debug_index - label: "backup-cmd-{{ backup_debug_index }}" - when: debug_level == 1 + - name: Nextcloud | Show backup command outputs + ansible.builtin.debug: + msg: | + CMD: {{ item.item }} + RC: {{ item.rc }} - - name: Nextcloud | Fail if any backup command failed - ansible.builtin.assert: - that: - - item.rc == 0 - fail_msg: >- - Nextcloud backup command failed: - {{ item.item }} - (rc={{ item.rc }}) + STDOUT: + {{ item.stdout | default('') | trim }} - STDERR: - {{ item.stderr | default('') | trim }} - quiet: true - loop: "{{ nc_backup_cmds.results }}" - loop_control: - index_var: backup_assert_index - label: "backup-cmd-{{ backup_assert_index }}" + STDERR: + {{ item.stderr | default('') | trim }} + loop: "{{ nc_backup_cmds.results }}" + loop_control: + index_var: backup_debug_index + label: "backup-cmd-{{ backup_debug_index }}" + when: debug_level == 1 - # ------------------------------------------------------------------------- - # Upgrade - # ------------------------------------------------------------------------- + - name: Nextcloud | Fail if any backup command failed + ansible.builtin.assert: + that: + - item.rc == 0 + fail_msg: | + Nextcloud backup command failed. + Command: {{ item.item }} + RC: {{ item.rc }} - - name: Nextcloud | Run upgrade commands on VM - ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - UserKnownHostsFile=/dev/null - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - >- - {{ - ('sudo -n bash -lc ' + (item | quote)) - if vm_use_sudo - else - ('bash -lc ' + (item | quote)) - }} - environment: - SSHPASS: "{{ vm_pass }}" - loop: "{{ nextcloud_upgrade_commands }}" - loop_control: - index_var: upgrade_index - label: "upgrade-cmd-{{ upgrade_index }}" - register: nc_upgrade_cmds - changed_when: false - failed_when: false + STDERR: + {{ item.stderr | default('') | trim }} + quiet: true + loop: "{{ nc_backup_cmds.results }}" + loop_control: + index_var: backup_assert_index + label: "backup-cmd-{{ backup_assert_index }}" - - name: Nextcloud | Show upgrade command outputs - ansible.builtin.debug: - msg: | - CMD: {{ item.item }} - RC: {{ item.rc }} - STDOUT: - {{ item.stdout | default('') | trim }} - STDERR: - {{ item.stderr | default('') | trim }} - loop: "{{ nc_upgrade_cmds.results }}" - loop_control: - index_var: upgrade_debug_index - label: "upgrade-cmd-{{ upgrade_debug_index }}" - when: debug_level == 1 + # --------------------------------------------------------------------- + # Upgrade + # --------------------------------------------------------------------- - - name: Nextcloud | Fail if any upgrade command failed - ansible.builtin.assert: - that: - - item.rc == 0 - fail_msg: >- - Nextcloud upgrade command failed: - {{ item.item }} - (rc={{ item.rc }}) + - name: Nextcloud | Run upgrade commands on VM + ansible.builtin.command: + argv: + - sshpass + - -e + - ssh + - -o + - StrictHostKeyChecking=no + - -o + - UserKnownHostsFile=/dev/null + - -o + - ConnectTimeout=15 + - -o + - ServerAliveInterval=10 + - -o + - ServerAliveCountMax=3 + - "{{ vm_user }}@{{ vm_ip }}" + - >- + {{ + ('sudo -n bash -lc ' ~ (item | quote)) + if vm_use_sudo + else + ('bash -lc ' ~ (item | quote)) + }} + environment: + SSHPASS: "{{ vm_pass }}" + loop: "{{ nextcloud_upgrade_commands }}" + loop_control: + index_var: upgrade_index + label: "upgrade-cmd-{{ upgrade_index }}" + register: nc_upgrade_cmds + changed_when: false + failed_when: false - STDERR: - {{ item.stderr | default('') | trim }} - quiet: true - loop: "{{ nc_upgrade_cmds.results }}" - loop_control: - index_var: upgrade_assert_index - label: "upgrade-cmd-{{ upgrade_assert_index }}" + - name: Nextcloud | Show upgrade command outputs + ansible.builtin.debug: + msg: | + CMD: {{ item.item }} + RC: {{ item.rc }} - # ------------------------------------------------------------------------- - # Disable maintenance mode - # ------------------------------------------------------------------------- + STDOUT: + {{ item.stdout | default('') | trim }} - - name: Nextcloud | Disable maintenance mode - ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - UserKnownHostsFile=/dev/null - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - >- - {{ - ( - 'sudo -n bash -lc ' + - ( - 'docker exec -u www-data nextcloud ' + - 'php occ maintenance:mode --off' - | quote - ) - ) - if vm_use_sudo - else - ( - 'bash -lc ' + - ( - 'docker exec -u www-data nextcloud ' + - 'php occ maintenance:mode --off' - | quote - ) - ) - }} - environment: - SSHPASS: "{{ vm_pass }}" - register: nc_maint_off - changed_when: false - failed_when: false + STDERR: + {{ item.stderr | default('') | trim }} + loop: "{{ nc_upgrade_cmds.results }}" + loop_control: + index_var: upgrade_debug_index + label: "upgrade-cmd-{{ upgrade_debug_index }}" + when: debug_level == 1 - - name: Nextcloud | Fail if maintenance mode could not be disabled - ansible.builtin.assert: - that: - - nc_maint_off.rc == 0 - fail_msg: >- - Failed to disable Nextcloud maintenance mode - (rc={{ nc_maint_off.rc }}). + - name: Nextcloud | Fail if any upgrade command failed + ansible.builtin.assert: + that: + - item.rc == 0 + fail_msg: | + Nextcloud upgrade command failed. + Command: {{ item.item }} + RC: {{ item.rc }} - STDERR: - {{ nc_maint_off.stderr | default('') | trim }} - quiet: true + STDERR: + {{ item.stderr | default('') | trim }} + quiet: true + loop: "{{ nc_upgrade_cmds.results }}" + loop_control: + index_var: upgrade_assert_index + label: "upgrade-cmd-{{ upgrade_assert_index }}" + + always: + - name: Nextcloud | Disable maintenance mode + ansible.builtin.command: + argv: + - sshpass + - -e + - ssh + - -o + - StrictHostKeyChecking=no + - -o + - UserKnownHostsFile=/dev/null + - -o + - ConnectTimeout=15 + - "{{ 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_pass }}" + register: nc_maint_off + changed_when: false + failed_when: false + + - name: Nextcloud | Show maintenance mode result + ansible.builtin.debug: + msg: | + RC: {{ nc_maint_off.rc }} + + STDOUT: + {{ nc_maint_off.stdout | default('') | trim }} + + STDERR: + {{ nc_maint_off.stderr | default('') | trim }} + when: debug_level == 1 + + - name: Nextcloud | Fail if maintenance mode could not be disabled + ansible.builtin.assert: + that: + - nc_maint_off.rc == 0 + fail_msg: | + Failed to disable Nextcloud maintenance mode. + RC: {{ nc_maint_off.rc }} + + STDERR: + {{ nc_maint_off.stderr | default('') | trim }} + quiet: true # ------------------------------------------------------------------------- # Readiness check # ------------------------------------------------------------------------- - - name: Nextcloud | Wait for status.php from controller + - name: Nextcloud | Wait for status.php ansible.builtin.uri: url: "{{ nextcloud_status_url }}" method: GET return_content: true validate_certs: true status_code: 200 - register: nc_status_controller + register: nc_status delegate_to: localhost run_once: true retries: "{{ retry_count }}" delay: 4 until: - - nc_status_controller.status | default(0) == 200 - - nc_status_controller.json is defined - failed_when: false + - nc_status.status | default(0) == 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 - - name: Nextcloud | Fetch status.php from VM - ansible.builtin.command: - argv: - - sshpass - - -e - - ssh - - -o - - StrictHostKeyChecking=no - - -o - - UserKnownHostsFile=/dev/null - - -o - - ConnectTimeout=15 - - "{{ vm_user }}@{{ vm_ip }}" - - >- - {{ - ( - 'sudo -n bash -lc ' + - ( - "python3 -c " + - ( - "import urllib.request; " + - "print(urllib.request.urlopen(" + - nextcloud_status_url | quote + - ", timeout=15).read().decode())" - ) | quote - ) - ) - if vm_use_sudo - else - ( - 'bash -lc ' + - ( - "python3 -c " + - ( - "import urllib.request; " + - "print(urllib.request.urlopen(" + - nextcloud_status_url | quote + - ", timeout=15).read().decode())" - ) | quote - ) - ) - }} - environment: - SSHPASS: "{{ vm_pass }}" - register: nc_status_vm - changed_when: false - failed_when: false - when: - - nc_status_controller.status | default(0) != 200 - or nc_status_controller.json is not defined - - - name: Nextcloud | Use controller status JSON - ansible.builtin.set_fact: - nextcloud_status_json: "{{ nc_status_controller.json }}" - when: - - nc_status_controller.status | default(0) == 200 - - nc_status_controller.json is defined - - - name: Nextcloud | Use VM status JSON - ansible.builtin.set_fact: - nextcloud_status_json: "{{ nc_status_vm.stdout | trim | from_json }}" - when: - - nextcloud_status_json is not defined - - nc_status_vm is defined - - nc_status_vm.stdout | default('') | trim | length > 0 - - - name: Nextcloud | Validate final status - ansible.builtin.assert: - that: - - nextcloud_status_json is defined - - nextcloud_status_json.installed | default(false) | bool - - not (nextcloud_status_json.maintenance | default(true) | bool) - - not (nextcloud_status_json.needsDbUpgrade | default(true) | bool) - fail_msg: >- - Nextcloud is not ready after upgrade. - Status: {{ nextcloud_status_json | default('unavailable') }} - quiet: true - - name: Nextcloud | Print final status ansible.builtin.debug: msg: >- - Nextcloud {{ nextcloud_status_json.version | default('?') }} - (installed={{ nextcloud_status_json.installed | default('?') }}, - maintenance={{ nextcloud_status_json.maintenance | default('?') }}, + Nextcloud {{ nc_status.json.version | default('?') }} + (installed={{ nc_status.json.installed | default('?') }}, + maintenance={{ nc_status.json.maintenance | default('?') }}, needsDbUpgrade={{ - nextcloud_status_json.needsDbUpgrade | default('?') - }}) + nc_status.json.needsDbUpgrade | default('?') + }}) \ No newline at end of file