Repository navigation
RFC: slotted extension storage - #838
Paco Huelsz (frhuelsz) wants to merge 27 commits into
Conversation
Adds a 'slotted' scope for sysexts/confexts, stored per A/B volume in a configurable store (default /var/trident/sysexts) and activated via an /etc/extensions symlink written during servicing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The A/B rollback path invokes no subsystems: automatic rollback is firmware-driven and validate_boot only reconciles Host Status, while manual_rollback::stage_rollback touches only the pcrlock policy. A symlink written during servicing would therefore survive a rollback pointing at the wrong slot. Activation now resolves at boot from the booted volume, via a single <store>/current flip ordered before systemd-sysext.service. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
The Rust types are an implementation detail; the public surface is the Host Configuration document. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The original premise was wrong. Trident does not place extensions on non-slotted storage: validate_extension_images_locations already rejects any extension directory not on an A/B volume. The real cost is that satisfying it requires a dedicated A/B volume pair, which the reference config pays for with four partitions and 2.4 GB, and which a layout whose only A/B volume is a verity-sealed /usr cannot pay at all. The proposal is now to relax that check: slot-scoping as a property of the extension rather than of the partition table, satisfied by an A/B volume as today or by a Trident-owned per-slot store. Activation no longer needs a boot-time unit. The extensions subsystem gains ManualRollbackAb in runs_on, and automatic rollback is corrected at commit via systemd-sysext refresh; version matching keeps the intervening window absent rather than wrong. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the previous mechanism. Symlinks are no longer written by the extensions subsystem into /var/lib/extensions during servicing; selection is recomputed every boot into /run/extensions from the slot that actually booted. That removes the ManualRollbackAb runs_on change and the commit-time repair, since nothing persists a decision that a rollback could invalidate. Corrections and additions from review: - Before=systemd-sysext.service is the ordering edge. ensure-sysext.service carries BindsTo= and After=systemd-sysext.service, so it runs after the merge and is the wrong anchor. - WantedBy= plus Before= does not fail closed; the merge unit needs a Requires= drop-in. - Per-file symlinking cannot commit a set atomically. - A store manifest replaces globbing, so absence is distinguishable from loss. - Two-phase rollout, since a rollback slot may predate the loader. - Documents why content matching alone is insufficient, and scopes slotted extensions to /usr and /opt because reordering the merge ahead of an /etc overlay would exhaust the kernel filesystem stacking budget. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both halves resolve the booted slot and expose that slot's content before a consumer reads it, and both must act before the /etc overlay is mounted in the initrd. /run is bind-moved across switch-root, so the sysext symlinks written there remain valid for the later merge in the real root. Resolving the slot once means the two halves cannot disagree about which slot they came from, and it removes the unit-ordering, condition and fail-closed problems a real-root systemd unit carries. /etc payloads are confext-format images mounted as additional lowerdirs on the existing overlay, so /etc stays mutable on systemd 255. Keeping the confext format rather than inventing a tarball means the artefact is already what native systemd-confext consumes on 256 or later; Azure Linux 4.0 ships 258.4. Payloads must declare ID=_any, because native mounting enforces extension-release matching that the custom mount bypasses, and a concrete ID would silently stop merging at the transition. Adds a change inventory across the new component, Trident and ACL with T-shirt estimates for design and implementation, plus suggested sequencing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0789 was rewritten and now names this RFC in three places: the optional path discussion, destination validation, and an open question asking how a bundled extension requests slotted placement. This RFC now answers that question rather than sitting beside it. The common case is a COSI-bundled extension that is slot-scoped, since an extension shipped with the OS image is already tied to it. An independently hosted extension placed per slot is the unusual combination. Removes what 0789 delivers: the invented manifest.json is replaced by 0789's Deployed Extension Inventory, the store is keyed by (kind, id) instead of filenames, and the effective set, resolved-placement validation, atomic staging and SELinux check relocation are referenced rather than restated. Adds scope: shared|slotted on both the COSI ExtensionImage and the Host Configuration Extension, spelled identically so one concept has one name, and mutually exclusive with path so placement never changes implicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
'commit' already names the post-boot servicing operation in Trident, so using it for the loader publishing its symlink set is a confusing collision. The loader has no relationship to that operation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Early-boot recovery and unresolved rollback and /etc reconciliation contracts require human architectural review.
2 open issues
What changed in this PR
Proposes slot-specific extension storage so ACL can update and roll back OS extensions together while preserving user /etc changes.
Changes:
- Defines managed storage, staging, activation and recovery contracts.
- Adds a versioned manifest schema and architecture diagram.
- Outlines compatibility requirements and validation gates.
| File | Description |
|---|---|
| docs/Development/RFCs/0838-slotted-sysexts/0838-slot-loader-manifest.schema.json | Defines the loader manifest format. |
| docs/Development/RFCs/0838-slotted-sysexts/0838-managed-extensions-overview.svg | Illustrates delivery and boot-time activation. |
| docs/Development/RFCs/0838-slotted-sysexts.md | Specifies the proposed lifecycle and integration design. |
Review effort: Balanced
| The loader unit uses `Restart=no` and gains | ||
| `OnFailure=trident-slot-loader-recovery.service`. This new initrd-only recovery | ||
| unit uses `ConditionKernelCommandLine=trident.update_boot=1` and | ||
| `ConditionPathExists=/etc/initrd-release`, with `DefaultDependencies=no`. | ||
| It has no dependency on `/etc` assembly or Trident's datastore and runs with | ||
| a bounded timeout. Nonzero exits, crashes and timeouts take this path, not | ||
| just errors returned by the loader's main function. |
| "minLength": 1, | ||
| "not": { | ||
| "anyOf": [ | ||
| {"enum": [".", ".."]}, |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| Mode and managed-set changes initially use A/B servicing; alias spelling alone | ||
| does not trigger one. Preserve retained slots until the transition completes. |
| `slot-loader.pending` marker, then synchronize its directory before clearing | ||
| the Trident-owned extension directory, including interrupted-staging | ||
| leftovers. | ||
| 3. Reserve space to stage the new extension files. Write temporary `.raw` files |
There was a problem hiding this comment.
copilot is always pestering me about atomic operations ... i wonder if we don't want to create a tmp folder and rename it instead of doing this file-by-file?
| ##### Selecting extensions at boot | ||
|
|
||
| 1. **Existing:** ACL mounts ROOT in the initrd. | ||
| 2. **New:** `trident-slot-loader.service` runs after that mount. The loader |
There was a problem hiding this comment.
"runs after that mount" ... do we also need to be sure to run before anything? or make that configurable?
| \* Future developments, not part of the initial implementation. | ||
|
|
||
| The mode applies to both sysexts and confexts. In `managed`/`acl`, explicit Host | ||
| Configuration URL/OCI images join the COSI-provided extension set and use |
There was a problem hiding this comment.
do we really want to support both url/oci images and COSI-provided for ACL? seems simpler to support either/or than combined.
| On subsequent updates, staging matches incoming metadata against these records | ||
| before downloading or extracting a payload. A match reuses the local DDI across | ||
| slots, sources or different compression without inspecting its inner files or | ||
| separately downloading the payload. Without a verified match, fetch and validate |


Proposes updating ACL's OS, sysexts and confext defaults together through A/B updates, with matching content on rollback and preserved user changes in
/etc. Builds on #789 for COSI extension delivery; Image Customizer's authoring API remains a separate proposal.>> Read formatted markdown here. <<