diff --git a/CHANGELOG.md b/CHANGELOG.md index 84ba109..810a107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # CHANGELOG — Set-OPS +## 2026-10-08 (118) — Les clones rattrapaient un passage d'apt hérité du gabarit + +**94 preuves.** La reconstruction de Technolibre sur `c7378ed` s'est arrêtée dès l'amorçage +du socle, sur `infra-pki-01` : `'apt-get autoremove' failed: E: Could not get lock +/var/cache/apt/archives/lock. It is held by process 13982 (apt-get)`. + +### Ce qui s'est passé, à la seconde + +- 22:54:30 : naissance de la VM ; `apt-daily.timer` et `apt-daily-upgrade.timer`, armés dans le + gabarit, démarrent. +- 22:58:30 : `common_packages` les réarme (décision du 2026-09-11 : les correctifs de sécurité + s'appliquent seuls, « armer un minuteur ne déclenche pas sa cible »). +- 22:59:20 : **`apt-daily-upgrade` part**, en pleine série d'opérations apt du rôle. Son + `apt-get` tient le verrou du **cache des archives**, que ni `lock_timeout` ni + `DPkg::Lock::Timeout` ne couvrent : `apt-get autoremove` échoue au lieu d'attendre. + +**Pourquoi il est parti** : les deux minuteurs sont **persistants** (`Persistent=true`) et +gardent la date de leur dernier passage. La trace d'`apt-daily` sur ce clone datait du +**2026-06-21**, jour de fabrication du gabarit. Chaque VM clonée croit donc avoir manqué un +passage et le rattrape dans l'heure (délai aléatoire, `RandomizedDelaySec=60m`). Avec 13 VM +par reconstruction, c'est un tirage au sort depuis le 2026-09-11 ; les reconstructions +précédentes l'ont gagné. La prémisse « armer ne déclenche pas » est vraie pour un minuteur +neuf, fausse pour un clone. + +### Fait + +`common_packages/tasks/minuteurs.yml`, inclus avant toute opération apt (quand les mises à jour +restent armées) : une trace plus ancienne que la naissance de la machine (clé d'hôte SSH) est +remise à l'heure, et son minuteur relancé, ce qui recalcule le prochain passage sans +rattrapage ; puis on attend la fin d'un passage déjà en cours. La décision du 2026-09-11 tient : +les correctifs de sécurité restent automatiques. + +### Éprouvé en vrai, sur `infra-pki-01` (clone neuf de Technolibre) + +Premier passage : la trace d'`apt-daily` (2026-06-21) est remise à l'heure et le minuteur +relancé ; celle d'`apt-daily-upgrade` (22:59:20, postérieure à la naissance) n'est **pas** +touchée (témoin). Second passage : aucun changement. Prochains passages : demain 06:23 et +17:26 ; aucun passage automatique parti depuis la relance. Lint propre ; `make verifier` +conforme, **94/94**. + ## 2026-10-08 (117) — Les applications de Nextcloud ne viennent plus de l'app store : le site les tient **94 preuves.** La reconstruction de Technolibre sur `7a4c55b` (règle stricte, entrée (116)) s'est diff --git a/roles/common_packages/tasks/main.yml b/roles/common_packages/tasks/main.yml index 2d5dc9c..ad5b3d1 100644 --- a/roles/common_packages/tasks/main.yml +++ b/roles/common_packages/tasks/main.yml @@ -110,6 +110,11 @@ when: not (common_packages_desarmer_maj_auto | bool) failed_when: false # une unite absente sur cette image n'est pas une faute +# AVANT TOUTE OPERATION APT : pas de rattrapage herite du gabarit (voir minuteurs.yml). +- name: Empecher le rattrapage des minuteurs apt herite du gabarit + ansible.builtin.include_tasks: minuteurs.yml + when: not (common_packages_desarmer_maj_auto | bool) + - name: Mettre à jour le cache APT ansible.builtin.apt: update_cache: true diff --git a/roles/common_packages/tasks/minuteurs.yml b/roles/common_packages/tasks/minuteurs.yml new file mode 100644 index 0000000..e136288 --- /dev/null +++ b/roles/common_packages/tasks/minuteurs.yml @@ -0,0 +1,65 @@ +--- +# LE RATTRAPAGE HERITE DU GABARIT (2026-10-08). +# +# `apt-daily.timer` et `apt-daily-upgrade.timer` sont PERSISTANTS : systemd garde la date +# de leur dernier passage (`/var/lib/systemd/timers/stamp-*`) et rattrape un passage +# manque. Une VM clonee herite de ces traces — celle d'`apt-daily` datait du 2026-06-21, +# jour de fabrication du gabarit. Elle « rattrape » donc des son demarrage, avec un delai +# aleatoire jusqu'a 60 min : a la reconstruction de Technolibre, `apt-daily-upgrade` est +# parti cinq minutes apres la naissance d'`infra-pki-01`, en pleine serie d'operations apt +# du role. Il tenait le verrou du CACHE DES ARCHIVES, qu'`apt-get` n'attend jamais +# (`DPkg::Lock::Timeout` ne couvre que dpkg) : +# +# 'apt-get autoremove' failed: E: Could not get lock /var/cache/apt/archives/lock. +# +# Une trace plus ancienne que la naissance de la machine n'est pas la sienne : on la remet a +# l'heure, et on relance le minuteur, qui recalcule son prochain passage (le matin suivant) +# sans rattraper. Les mises a jour automatiques restent armees (decision du 2026-09-11) ; +# seul disparait un passage herite. Sur une machine en service, la trace est recente : +# rien ne change. +- name: Naissance de la machine (cle d'hote SSH, posee au premier demarrage) + ansible.builtin.stat: + path: /etc/ssh/ssh_host_ed25519_key.pub + register: common_packages_naissance + +- name: Traces des minuteurs apt persistants + ansible.builtin.stat: + path: "/var/lib/systemd/timers/stamp-{{ item }}" + loop: "{{ common_packages_unites_maj_auto | select('match', '.*[.]timer$') | list }}" + register: common_packages_traces + +- name: Remettre a l'heure une trace heritee du gabarit + ansible.builtin.file: + path: "{{ item.stat.path }}" + state: touch + mode: "0644" + loop: >- + {{ common_packages_traces.results | selectattr('stat.exists') + | selectattr('stat.mtime', 'lt', common_packages_naissance.stat.mtime | default(0)) + | list }} + loop_control: + label: "{{ item.item }}" + register: common_packages_traces_remises + when: common_packages_naissance.stat.exists + +# Relancer recalcule le prochain passage d'apres la trace neuve. Relancer un minuteur ne +# declenche pas sa cible. +- name: Relancer les minuteurs dont la trace a ete remise + ansible.builtin.systemd_service: + name: "{{ item.item.item }}" + state: restarted + loop: "{{ common_packages_traces_remises.results | default([]) | selectattr('changed') | list }}" + loop_control: + label: "{{ item.item.item }}" + +# UN PASSAGE DEJA PARTI se termine avant nos operations apt, plutot que de se les disputer. +# `is-active` rend 0 si l'une au moins des unites tourne. +- name: Attendre la fin d'un passage apt automatique deja en cours + ansible.builtin.command: + argv: ["systemctl", "is-active", "--quiet", "apt-daily.service", "apt-daily-upgrade.service"] + register: common_packages_passage + changed_when: false + failed_when: false + until: common_packages_passage.rc != 0 + retries: 30 + delay: 10