platform-docs/adr/0010-auftragskatalog-geraeteweise-zuweisung.md
Thomas Stallinger 38c04cd83a docs: fix Auftragskatalog default semantics in ADR-0010
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>
2026-08-04 10:06:19 +02:00

6.4 KiB

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.