diff --git a/host_vars/portainer.yml b/host_vars/portainer.yml new file mode 100644 index 0000000..8b01fa1 --- /dev/null +++ b/host_vars/portainer.yml @@ -0,0 +1,3 @@ +ansible_user: howard +ansible_password: "{{ portainer_pass }}" +ansible_ssh_common_args: "-o ProxyJump=root@192.168.69.2" \ No newline at end of file diff --git a/inv_linuxes_portainer b/inv_linuxes_portainer index 0b0a274..5a42354 100644 --- a/inv_linuxes_portainer +++ b/inv_linuxes_portainer @@ -1,5 +1,5 @@ [linux_servers] proxmox ansible_host=192.168.69.2 -[nextcloud_stack] -vm_portainer ansible_host=192.168.69.123 ansible_user=root ansible_python_interpreter=/usr/bin/python3 \ No newline at end of file +[portainer] +portainer ansible_host=192.168.69.253 \ No newline at end of file diff --git a/nextcloud/check_stack_nextcloud.yml b/nextcloud/check_stack_nextcloud.yml index c69fae7..ae0e29d 100644 --- a/nextcloud/check_stack_nextcloud.yml +++ b/nextcloud/check_stack_nextcloud.yml @@ -1,183 +1,104 @@ -# Comments in English in code: --- -- name: Upload and run stack health checks - hosts: nextcloud_stack # <-- dříve 'proxmox' +# Play: Run Nextcloud housekeeping commands inside the container +- name: Nextcloud maintenance (cron, app updates, repair, status, health check) + hosts: portainer gather_facts: false - become: false vars: - health_script_path: /data/compose/nextcloud/stack-health.sh + nextcloud_container: nextcloud tasks: - - name: Ensure target directory exists - ansible.builtin.file: - path: "{{ health_script_path | dirname }}" - state: directory - mode: '0755' + - name: Ensure docker CLI is available + ansible.builtin.command: + argv: ["/usr/bin/env", "bash", "-lc", "command -v docker"] + changed_when: false + failed_when: result.rc != 0 + register: result + # English: Hard fail if docker is not present. - - name: Install stack-health.sh (inline content, Jinja disabled via raw) - ansible.builtin.copy: - dest: "{{ health_script_path }}" - mode: '0755' - content: |- - {% raw %} - #!/usr/bin/env bash - set -euo pipefail + - name: Verify Nextcloud container is running + ansible.builtin.command: + argv: ["docker", "ps", "--format", "{{.Names}}"] + changed_when: false + register: docker_ps + # English: List running containers by name. - # --- config --- - NC_CONTAINER="${NC_CONTAINER:-nextcloud}" - DB_CONTAINER="${DB_CONTAINER:-nextcloud-db}" - REDIS_CONTAINER="${REDIS_CONTAINER:-redis}" - COLLA_CONTAINER="${COLLA_CONTAINER:-collabora}" - NC_HTTP="${NC_HTTP:-http://127.0.0.1:8080/status.php}" - COLLA_DISCOVERY="${COLLA_DISCOVERY:-http://127.0.0.1:9980/hosting/discovery}" - NC_PUBLIC_URL="${NC_PUBLIC_URL:-}" - - pass(){ echo -e "✅ $*"; } - fail(){ echo -e "❌ $*"; EXIT=1; } - EXIT=0 - TMP_DIR="$(mktemp -d)"; trap 'rm -rf "$TMP_DIR"' EXIT - - # 0) Docker reachable? - docker ps >/dev/null 2>&1 || { fail "Docker not accessible"; echo "----------------------------------------"; echo "ONE OR MORE CHECKS FAILED ❌"; exit "$EXIT"; } - - # 1) Containers running? - for c in "$NC_CONTAINER" "$DB_CONTAINER" "$REDIS_CONTAINER" "$COLLA_CONTAINER"; do - if docker ps --format '{{.Names}}' | grep -qx "$c"; then - pass "container '$c' is running" - else - fail "container '$c' NOT running" - fi - done - - # 1b) NC & DB share at least one network? - get_nets(){ docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{printf "%s " $k}}{{end}}' "$1" 2>/dev/null; } - NC_NETS="$(get_nets "$NC_CONTAINER" | xargs || true)" - DB_NETS="$(get_nets "$DB_CONTAINER" | xargs || true)" - COMMON_NET="$(printf "%s\n" $NC_NETS $DB_NETS | tr ' ' '\n' | grep -v '^$' | sort | uniq -d || true)" - if [ -n "$COMMON_NET" ]; then - pass "Nextcloud & DB share network: ${COMMON_NET}" - else - fail "Nextcloud & DB do NOT share a network (NC: [$NC_NETS], DB: [$DB_NETS])" - fi - - # 2) OCC status - if docker exec -u www-data "$NC_CONTAINER" php occ status >"$TMP_DIR/occ_status.txt" 2>"$TMP_DIR/occ_status.err"; then - cat "$TMP_DIR/occ_status.txt" - grep -q "installed: true" "$TMP_DIR/occ_status.txt" && pass "Nextcloud installed: true" || fail "Nextcloud installed: false" - grep -q "maintenance: false" "$TMP_DIR/occ_status.txt" && pass "Nextcloud maintenance: false" || fail "Nextcloud maintenance is ON" - else - fail "occ status failed: $(cat "$TMP_DIR/occ_status.err" || true)" - fi - - # 3) NC -> DB connectivity via PDO from Nextcloud config.php - DB_TEST_PHP=" - require \"/var/www/html/lib/base.php\"; - \$c=\\OC::\$server->getConfig(); - \$h=\$c->getSystemValue(\"dbhost\"); - \$d=\$c->getSystemValue(\"dbname\"); - \$u=\$c->getSystemValue(\"dbuser\"); - \$p=\$c->getSystemValue(\"dbpassword\"); - if(!is_string(\$h) || \$h===''){ fwrite(STDERR, \"Invalid dbhost\\n\"); exit(2); } - \$dsn=''; - if (\$h[0]==='/' || strpos(\$h,'localhost:/')===0 || strpos(\$h,'127.0.0.1:/')===0) { - if (strpos(\$h,':/')!==false) { \$parts=explode(':/', \$h, 2); \$sock='/'.\$parts[1]; } else { \$sock=\$h; } - \$dsn='mysql:unix_socket='.\$sock.';dbname='.\$d.';charset=utf8mb4'; - } else { - \$host=\$h; \$port=null; - if (strpos(\$h,':')!==false) { list(\$host,\$pp)=explode(':',\$h,2); if (ctype_digit(\$pp)) { \$port=(int)\$pp; } } - \$dsn='mysql:host='.\$host.';'; if (\$port) { \$dsn.='port='.\$port.';'; } - \$dsn.='dbname='.\$d.';charset=utf8mb4'; - } - try { - \$pdo=new PDO(\$dsn,\$u,\$p,[PDO::ATTR_TIMEOUT=>2,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]); - \$pdo->query('SELECT 1'); - echo \"DB connect OK\\n\"; - } catch (Throwable \$e) { - fwrite(STDERR, 'PDO error: '.\$e->getMessage().\"\\n\"); exit(2); - } - " - if docker exec -u www-data "$NC_CONTAINER" php -r "$DB_TEST_PHP" >"$TMP_DIR/db_ping.out" 2>"$TMP_DIR/db_ping.err"; then - pass "Nextcloud → DB connection: OK ($(tr -d "\r" <"$TMP_DIR/db_ping.out"))" - else - fail "Nextcloud → DB connection FAILED: $(tr -d "\r" <"$TMP_DIR/db_ping.err")" - fi - - # 4) DB readiness inside container - if docker exec "$DB_CONTAINER" sh -c 'mariadb-admin ping -h 127.0.0.1 --silent 2>/dev/null || mysqladmin ping -h 127.0.0.1 --silent 2>/dev/null'; then - pass "MariaDB is ready for connections" - else - fail "MariaDB readiness check FAILED" - fi - - # 5) NC -> Redis - REDIS_TEST_PHP=' - require "/var/www/html/lib/base.php"; - $c=\OC::$server->getConfig(); - $rc=$c->getSystemValue("redis", []); - $host = is_array($rc) && isset($rc["host"]) ? $rc["host"] : (getenv("REDIS_HOST") ?: "redis"); - $port = is_array($rc) && isset($rc["port"]) ? (int)$rc["port"] : 6379; - if (class_exists("Redis")) { - $r=new Redis(); - try { $r->connect($host,$port,1.0); echo $r->ping()."\n"; exit(0); } - catch (Throwable $e) { fwrite(STDERR, "Redis ext error: ".$e->getMessage()."\n"); exit(2); } - } else { - $e=$s=0; $fp=@fsockopen($host,$port,$e,$s,1.0); - if($fp){ echo "PONG (tcp ok)\n"; fclose($fp); exit(0); } - fwrite(STDERR,"No php-redis ext and TCP connect failed\n"); exit(2); - } - ' - if docker exec -u www-data "$NC_CONTAINER" php -r "$REDIS_TEST_PHP" >"$TMP_DIR/redis_ping.out" 2>"$TMP_DIR/redis_ping.err"; then - pass "Nextcloud → Redis: OK ($(tr -d "\r" <"$TMP_DIR/redis_ping.out"))" - else - fail "Nextcloud → Redis FAILED: $(tr -d "\r" <"$TMP_DIR/redis_ping.err")" - fi - - # 6) Redis local ping - if docker exec "$REDIS_CONTAINER" sh -c 'redis-cli -h 127.0.0.1 ping' >"$TMP_DIR/redis_cli.out" 2>"$TMP_DIR/redis_cli.err"; then - if grep -q "PONG" "$TMP_DIR/redis_cli.out"; then pass "Redis container responds to PING"; else fail "Redis cli ping did not return PONG"; fi - else - fail "redis-cli ping failed: $(cat "$TMP_DIR/redis_cli.err" || true)" - fi - - # 7) HTTP status.php - if curl -fsS "$NC_HTTP" -o "$TMP_DIR/nc_http.json"; then - pass "HTTP $NC_HTTP reachable" - head -c 300 "$TMP_DIR/nc_http.json"; echo - else - fail "HTTP $NC_HTTP unreachable" - fi - - # 8) Optional public URL - if [ -n "$NC_PUBLIC_URL" ]; then - if curl -fsSIL "$NC_PUBLIC_URL" >/dev/null; then pass "Public URL reachable: $NC_PUBLIC_URL"; else fail "Public URL unreachable: $NC_PUBLIC_URL"; fi - fi - - # 9) Collabora discovery - if curl -fsS "$COLLA_DISCOVERY" -o "$TMP_DIR/colla_disc.xml"; then - pass "Collabora discovery reachable ($COLLA_DISCOVERY)" - grep -m1 -o "]*>" "$TMP_DIR/colla_disc.xml" || true - else - fail "Collabora discovery unreachable: $COLLA_DISCOVERY" - fi - - # summary - if [ "$EXIT" -eq 0 ]; then - echo "----------------------------------------" - echo "ALL CHECKS PASSED ✅" - else - echo "----------------------------------------" - - - - name: Run stack-health.sh - ansible.builtin.shell: "{{ health_script_path }}" - register: health - args: - executable: /bin/bash - - - name: Show health output - ansible.builtin.debug: - msg: "{{ health.stdout | default('no stdout') }}" - - - name: Fail if checks failed (rc != 0) + - name: Fail if '{{ nextcloud_container }}' is not running ansible.builtin.fail: - msg: "Health checks failed" - when: health.rc != 0 \ No newline at end of file + msg: "Container '{{ nextcloud_container }}' is not running on target host." + when: nextcloud_container not in docker_ps.stdout_lines + # English: Avoid obscure 'docker exec' errors later. + + - name: Run Nextcloud maintenance pipeline + block: + - name: 1) Run cron.php + ansible.builtin.command: + argv: + - docker + - exec + - -u + - www-data + - "{{ nextcloud_container }}" + - php + - -f + - /var/www/html/cron.php + register: cron_run + + - name: 2) Update all apps + ansible.builtin.command: + argv: + - docker + - exec + - -u + - www-data + - "{{ nextcloud_container }}" + - php + - occ + - app:update + - --all + register: apps_update + + - name: 3) Run maintenance:repair (include expensive) + ansible.builtin.command: + argv: + - docker + - exec + - -u + - www-data + - "{{ nextcloud_container }}" + - php + - occ + - maintenance:repair + - --include-expensive + register: repair_run + + - name: 4) Show occ status + ansible.builtin.command: + argv: + - docker + - exec + - -u + - www-data + - "{{ nextcloud_container }}" + - php + - occ + - status + register: occ_status + changed_when: false + + - name: 5) Run stack health script + ansible.builtin.command: + argv: ["/data/compose/nextcloud/stack-health.sh"] + register: health + # English: If your script returns non-zero, the play will fail (desired in CI). + + always: + - name: Print outputs from maintenance steps + ansible.builtin.debug: + msg: + - "cron.php stdout: {{ cron_run.stdout | default('') }}" + - "cron.php stderr: {{ cron_run.stderr | default('') }}" + - "app:update stdout: {{ apps_update.stdout | default('') }}" + - "app:update stderr: {{ apps_update.stderr | default('') }}" + - "repair stdout: {{ repair_run.stdout | default('') }}" + - "repair stderr: {{ repair_run.stderr | default('') }}" + - "occ status:\n{{ occ_status.stdout | default('') }}" + - "health stdout:\n{{ health.stdout | default('') }}"