Merge pull request 'Add initial project structure and documentation' (#6) from chore/documentation into main

Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
2026-06-02 06:16:33 +00:00
2 changed files with 88 additions and 0 deletions

55
roles/upgrade/README.md Normal file
View File

@@ -0,0 +1,55 @@
# Upgrade role
Upgrades all packages using the native package manager (`apk` or `apt`).
## Problem
Tailscale may be updated during the upgrade. When its service restarts, the SSH tunnel drops and Ansible loses connectivity, causing a fatal `UNREACHABLE`.
## Solution — async + poll
Instead of a blocking upgrade, we fire the job in the background and poll for completion, tolerating temporary unreachability.
```mermaid
sequenceDiagram
participant A as Ansible
participant T as Target
A->>T: apk upgrade (async, poll: 0)
T-->>A: job ID: 42
Note over T: 🔄 upgrade en cours...
A->>T: async_status jid=42
T-->>A: finished: false
Note over A: FAILED-RETRYING (normal)
A-->>A: sleep 10s
A->>T: async_status jid=42
T-->>A: finished: false
Note over A: FAILED-RETRYING (normal)
A-->>A: sleep 10s
A->>T: async_status jid=42
T-->>A: finished: true ✅
Note over A: ok
```
- **`async: 3600` + `poll: 0`** — fire and forget. Ansible gets a job ID and moves on.
- **`async_status` with `until: finished`** — polls every 10s for up to 60 retries (~10 min).
- **`ignore_unreachable: true`** — if Tailscale dropped SSH, Ansible doesn't crash. It retries until the host is back.
- The `FAILED - RETRYING` messages are normal — they just mean the `until` condition isn't met yet.
## Idempotency
A task with `async` + `poll: 0` always reports `changed: true` by default, even when nothing happened. To fix this:
```mermaid
graph LR
A["Fire: changed_when: false"] --> B["Package manager runs in background"]
B --> C["Wait: polls every 10s"]
C -->|finished| D["async_status reads real changed value"]
D -->|"packages updated"| E["changed ✅"]
D -->|"nothing to do"| F["ok"]
```
- The **fire** task has `changed_when: false` — it never claims change.
- The **wait** task reads `changed_when: async_result.changed` — it propagates the real status from the package manager once the job finishes.
Second run with nothing to upgrade → `changed=0` on all hosts.