The catalog isn't a second pool independent of workspaces - a workspace is just a preset selection from catalog-eligible Merkmale. Clarifies that the present/absent default for a catalog-eligible Merkmal with no explicit device_merkmale row must follow workspace membership, not hardcode to absent; an explicit row always overrides that default in either direction. The first implementation got this wrong (hardcoded absent default), which would have silently removed any already workspace-composed Merkmal the moment it got flagged catalog-eligible. Fixed in provisioning-server/app.py in the same pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
44 lines
6.4 KiB
Markdown
44 lines
6.4 KiB
Markdown
# ADR-0010: Auftragskatalog als geräteweise Zuweisung bestehender Merkmale
|
|
|
|
**Status:** Beschlossen
|
|
**Datum:** 03.08.2026
|
|
|
|
## Kontext
|
|
|
|
Nach der Kernschleife des Provisioning Agent (Check-in, Ansible-Pull, siehe `13-live-provisioning-boot.md` und `06-backend-api.md`) sollte als nächster Ausbauschritt ein „Auftragskatalog" entstehen: eine Sammlung einzeln zuweisbarer Aufgaben, die ein Gerät auch nach der Erstinstallation zusätzlich zu seiner Bereitstellungsvorlage bekommen kann, gruppiert in Kategorien.
|
|
|
|
Zwei Anforderungen an diesen Katalog waren zu Beginn der Diskussion offen:
|
|
|
|
1. Ein Auftrag muss nach der Zuweisung auch wieder **abwählbar** sein, und das Abwählen muss real etwas zurückbauen, nicht nur zukünftige Anwendung verhindern.
|
|
2. Pro Gerät muss **transparent nachvollziehbar** sein, was wann wie durchgeführt wurde.
|
|
|
|
Die naheliegende erste Frage war, ob dafür ein neues, eigenständiges „Auftrag"-Modell nötig ist, oder ob sich das bestehende Merkmal/Blueprint-Modell (ADR-0002, `12-feature-blueprint-model.md`) wiederverwenden lässt. Ein Auftrag im hier gemeinten Sinn ist fachlich nicht von einem Merkmal zu unterscheiden: eine einzelne, distributionsunabhängig benannte Eigenschaft, die pro Backend als Ansible-Rolle umgesetzt und über den bestehenden Ansible-Pull-Mechanismus angewendet wird. Der einzige Unterschied ist der Zuweisungsweg: ein Merkmal wird bisher ausschließlich über die Workspace-Zugehörigkeit einer Bereitstellungsvorlage zugewiesen (zum Zeitpunkt der Installation, für die Lebensdauer des Geräts fix). Ein Auftrag soll zusätzlich direkt einem einzelnen, bereits laufenden Gerät zugewiesen und wieder entzogen werden können.
|
|
|
|
## Entscheidung
|
|
|
|
Ein Auftrag ist technisch ein Merkmal. Es wird kein separates Auftrag/Auftrag-Blueprint-Modell parallel zu Merkmal/Blueprint eingeführt.
|
|
|
|
Merkmale, die für die geräteweise Ad-hoc-Zuweisung freigegeben sind, werden über ein neues Attribut `im_auftragskatalog` markiert und optional einer `Kategorie` zugeordnet (neues, eigenständiges Kernobjekt, siehe `09-data-model-v1.md`).
|
|
|
|
Die Ad-hoc-Zuweisung eines Merkmals zu einem einzelnen Gerät ist eine neue, von der Workspace-Zugehörigkeit (`workspace_merkmale`) unabhängige Beziehung (`device_merkmale`), mit einem Aktiv-Flag statt einer reinen Zeile-existiert-oder-nicht-Semantik. Das Deselektieren eines Auftrags setzt dieses Flag auf inaktiv, statt die Zeile zu löschen.
|
|
|
|
Für jedes Gerät wird beim Check-in der vollständige Soll-Zustand aller katalogfähigen Merkmale (nicht nur die neu hinzugekommenen) an den Agenten übermittelt, je Merkmal mit explizitem Zustand `present` oder `absent`. Das ist rein aus dem aktuellen Auswahlstand ableitbar und erfordert keine serverseitige Historie. Damit ein Deselektieren real etwas zurückbaut, muss die zugehörige Ansible-Rolle einen `absent`-Zweig besitzen; das ist eine Erweiterung der Rollenkonvention aus `12-feature-blueprint-model.md`, keine Änderung an diesem Dokument selbst.
|
|
|
|
**Default ohne explizite Auftragszuweisung (nachträglich präzisiert, 04.08.2026):** Der Auftragskatalog ist kein von Workspaces unabhängiger zweiter Pool — ein Workspace ist fachlich nichts anderes als eine Vorauswahl aus katalogfähigen Merkmalen. Fehlt für ein Device eine explizite `device_merkmale`-Zeile zu einem katalogfähigen Merkmal, ist der Default deshalb dessen Workspace-Zugehörigkeit (`present`, wenn das Merkmal Teil des zugewiesenen Workspace ist, sonst `absent`), nicht pauschal `absent`. Eine vorhandene `device_merkmale`-Zeile überschreibt diesen Default immer, in beide Richtungen — auch ein workspace-komponiertes Merkmal lässt sich damit geräteweise abwählen, was hier bewusst kein Konflikt, sondern der Kern der ursprünglichen Abwählbarkeits-Anforderung ist. (In der ersten Umsetzung war der Default fälschlich hart auf `absent` gesetzt, unabhängig von der Workspace-Zugehörigkeit — dadurch hätte das bloße Markieren eines bereits workspace-komponierten Merkmals als katalogfähig es auf jedem betroffenen Gerät beim nächsten Check-in entfernt, ohne dass irgendjemand es abgewählt hätte. In `provisioning-server/app.py` korrigiert.)
|
|
|
|
Für die Nachvollziehbarkeit pro Gerät wird ein einziges, generisches, ausschließlich anhängendes Ereignisprotokoll (`device_merkmal_events`) eingeführt, statt mehrerer spezialisierter Log-Tabellen. Ein `event_type`-Feld unterscheidet sowohl Auswahländerungen (`selected`, `deselected`) als auch vom Agenten gemeldete Ausführungsergebnisse (`applied`, `apply_failed`, `removed`, `remove_failed`). Damit der Agent Ausführungsergebnisse überhaupt melden kann, braucht die Check-in-API einen neuen, additiven Report-Kanal; dessen genaue Form wird nicht in diesem ADR, sondern bei der Umsetzung in `08-provisioning-api.md` spezifiziert.
|
|
|
|
Workspace-komponierte Merkmale (nicht im Auftragskatalog) bleiben von dieser Änderung unberührt: sie werden weiterhin ausschließlich additiv über `--tags` angewendet, ohne `absent`-Zustand. Die Frage, ob sich eine Workspace-Zusammensetzung nachträglich für ein bereits provisioniertes Gerät ändern und zurückbauen lassen soll, ist ein separates, hier bewusst nicht adressiertes Problem.
|
|
|
|
## Konsequenzen
|
|
|
|
Neue Merkmale, die in den Auftragskatalog aufgenommen werden sollen, brauchen zusätzlich zum bestehenden Blueprint pro Backend eine `absent`-Umsetzung in der jeweiligen Ansible-Rolle. Merkmale, die ausschließlich über Workspaces zugewiesen werden, sind davon nicht betroffen.
|
|
|
|
Der Check-in-Response wird um einen expliziten `state`-Wert je Katalogeintrag erweitert; das ist rückwärtskompatibel additiv, kein Bruch der bestehenden, bereits gegen die QEMU-Testumgebung verifizierten Check-in-Antwort.
|
|
|
|
Ein neuer Report-Endpoint wird nötig, damit Ausführungsergebnisse serverseitig sichtbar werden. Bis dieser existiert, bleibt die einzige Ausführungsevidenz lokal auf dem Gerät (`/run/tuxflotte/agent/applied/`), wie bereits heute.
|
|
|
|
Das Datenmodell bleibt für künftige Auftragsarten erweiterbar, ohne neue Tabellen: neue Ereignisarten sind neue `event_type`-Werte, keine Schema-Änderung. Sollten künftig Aufträge nötig werden, die sich nicht als idempotenter Zielzustand beschreiben lassen (echte einmalige Aktionen ohne Present/Absent-Semantik), ist das ein eigenständiges, hier nicht getroffenes Entwurfsproblem.
|
|
|
|
Details zum Datenmodell: `09-data-model-v1.md`. Details zum Merkmal/Blueprint-Grundmodell: `12-feature-blueprint-model.md`, ADR-0002.
|