Overlay work#738
Open
yktyagi wants to merge 13 commits into
Open
Conversation
…mode LoadAndMergeTemplate merges the create-mode default OS template into every user template, and mergeSystemConfig unions the default package list with the user's. For an overlay template that requested only "tree", this re-seeded the entire from-scratch base package set (ubuntu-minimal, systemd-boot, dracut-core, systemd, cryptsetup-bin, efibootmgr, ...) into the additive overlay set. Overlay mode layers packages onto an already-complete baseline image, so those defaults are already present. Re-seeding them dragged in bootloader packages (systemd-boot) whose strict version pin (libsystemd-shared (= 255.4-1ubuntu8.16)) the frozen baseline (255.4-1ubuntu8.12) cannot satisfy under additive-only install, blocking the build. Restrict the merged package set to exactly the user-declared packages when the template is in overlay mode. Create mode is unchanged. Adds a regression test covering both modes. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR expands “overlay mode” capabilities and diagnostics across the build pipeline: it adds overlay-specific stage timing reporting, introduces an additive-and-upgrade overlay policy with stronger preflight gating (conflicts, unsatisfied pinned deps, rpm obsoletes, kernel immutability), improves SBOM correctness/merge behavior, and enhances SPDX compare/inspection to work directly from embedded SBOMs in images (including large ext4 roots).
Changes:
- Add overlay stage timings and render them via a shared timing-table helper.
- Implement additive-and-upgrade overlay policy with bounded upgrade scope + enhanced preflight checks (conflicts, version pins, rpm obsoletes, kernel-image immutability) and install-mode switching (
rpm -U,dpkg --auto-deconfigure). - Improve SBOM handling: merge overlay contributions into baseline SBOM, preserve versions from cached
.debfilenames, and enhance inspection/compare to classify upgrades and to extract SBOMs from images efficiently.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/utils/display/display.go | Introduces reusable timing table rendering and exported TimingRow. |
| internal/provider/ubuntu/ubuntu.go | Prints overlay build timing table on successful overlay builds; avoids timing-table misuse. |
| internal/ospackage/debutils/download.go | Preserves package version when reconstructing SBOM inputs from cached .deb files. |
| internal/ospackage/debutils/download_test.go | Adds regression test ensuring cached .deb filename parsing recovers versions. |
| internal/image/overlay/session.go | Adds per-stage overlay timing recording and SBOM merge embedding into baseline. |
| internal/image/overlay/session_test.go | Updates seams + tests overlay stage ordering and failure cutoffs for timings. |
| internal/image/overlay/sbom_test.go | Adds tests for baseline SBOM discovery/picking behavior. |
| internal/image/overlay/resolve.go | Adds upgrade-capable overlay resolution path with bounded upgrade scope and provenance logging. |
| internal/image/overlay/resolve_test.go | Adds coverage for upgrade mode behavior and upgrade-scope boundaries. |
| internal/image/overlay/preflight.go | Extends overlay preflight with upgrade gating, unsatisfied pinned deps, kernel-image immutability, conflicts/obsoletes metadata checks. |
| internal/image/overlay/preflight_test.go | Updates/extends preflight tests for new policy gates and kernel/obsoletes behaviors. |
| internal/image/overlay/install.go | Propagates “upgrade” intent to installer; adds dpkg --auto-deconfigure; switches rpm to -U when upgrades exist. |
| internal/image/overlay/install_test.go | Adds tests for upgrade propagation and --auto-deconfigure usage; updates command expectations. |
| internal/image/overlay/depcheck.go | Adds metadata readers/parsers for deb/rpm depends/conflicts/obsoletes and shared constraint parsing. |
| internal/image/overlay/depcheck_test.go | Adds unit tests for parsing + end-to-end preflight conflict/dep-pin regressions. |
| internal/image/overlay/bootupdate.go | Skips initramfs regeneration for purely userspace overlays via artifact file-list inspection; improves error surfacing. |
| internal/image/overlay/bootupdate_test.go | Adds tests for boot-relevance gate, parsing, and fail-safe behavior. |
| internal/image/imageinspect/sbom.go | Adds “upgraded” classification to SPDX diffs; adds clearer failure notes during SBOM filesystem inspection. |
| internal/image/imageinspect/sbom_test.go | Updates compare expectations and adds direct diff classification coverage. |
| internal/image/imageinspect/sbom_loopdev.go | Adds loop-device + debugfs SBOM extraction strategy for large ext roots (root-only, read-only). |
| internal/image/imageinspect/sbom_loopdev_test.go | Adds tests for loop-device SBOM reader and partition node selection. |
| internal/image/imageinspect/sbom_fsread_test.go | Adds test that in-place go-diskfs extraction works without partition copying. |
| internal/image/imageinspect/renderer_text.go | Renders upgraded packages section in SPDX compare text output. |
| internal/image/imageinspect/renderer_text_test.go | Updates renderer tests to include upgraded package output. |
| internal/image/imageinspect/imageinspect.go | Reorders SBOM extraction strategies (filesystem → loopdev → raw copy) and threads raw path for loop attach. |
| internal/config/schema/os-image-template.schema.json | Extends overlay policy schema to allow additive-and-upgrade. |
| internal/config/merge.go | Prevents overlay mode from inheriting create-mode default package set. |
| internal/config/merge_test.go | Adds regression tests for overlay-vs-create package merge behavior. |
| internal/config/manifest/manifest.go | Factors SPDX package conversion and adds baseline+overlay SBOM merge writer. |
| internal/config/manifest/manifest_test.go | Adds tests for SBOM merge preserving baseline header/metadata and handling malformed baselines. |
| internal/config/config.go | Adds overlay package-operation constant and derives AllowUpgrade gate from validation. |
| internal/config/baseline_test.go | Extends baseline validation tests to cover additive-and-upgrade and derived AllowUpgrade behavior. |
| cmd/image-composer-tool/compare_cmd.go | Allows SPDX compare inputs to be images or JSON; extracts embedded SBOMs from images before comparing. |
| cmd/image-composer-tool/compare_cmd_test.go | Adds tests for image-based SPDX compare extraction and JSON detection. |
| cmd/image-composer-tool/build.go | Skips create-mode timing table for overlay builds (overlay prints its own timings). |
71fe45e to
2ced215
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 36 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
internal/image/overlay/session.go:1
- Mutating the package-level
manifest.DefaultSPDXFileintroduces global shared state that can leak across builds/tests in the same process and can cause data races if multiple builds run concurrently. Consider avoiding global mutation by passing the intended SBOM filename explicitly to the embed/sidecar-copy helpers (or restoring the prior value viadeferafter the overlay postprocess completes).
…light An additive-only overlay cannot upgrade a baseline package, so a to-install artifact that pins an exact version of a package already present at a different version (e.g. systemd-boot requiring libsystemd-shared (= 255.4-1ubuntu8.16) against a baseline's 255.4-1ubuntu8.12) fails mid-dpkg with an opaque error. Read each plan.ToInstall artifact's declared dependencies from its on-disk control metadata (dpkg -f for deb, rpm -qpR for rpm) and classify edges whose version constraint cannot be satisfied by the post-install package set. Only the present-but-wrong-version case is flagged; absent (possibly virtual/Provides) dependencies and edges rescued by a satisfied alternative or co-install are not, avoiding false positives. The reader is a best-effort seam: an unreadable artifact logs a warning and does not fail the preflight. This converts a mid-install crash into a clear up-front block naming the offending dependency and versions. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
Introduce an opt-in "additive-and-upgrade" packageOperation that permits upgrading a package already installed in the baseline to a newer version, while downgrades and removals remain blocked. The new mode is derived into the internal OverlayPolicy.AllowUpgrade gate during config validation and exposed through the JSON schema enum. Resolver: in upgrade mode, requested-and-present packages are re-resolved so their repository candidate can be compared against the baseline. A bounded upgrade set (upgradeEligibleNames) is grown to a fixpoint covering requested-and-present packages and required transitive dependencies whose baseline copy fails a versioned pin; every other present package keeps its baseline version. Preflight: the AllowUpgrade gate now permits classified upgrades; the unsatisfied-dependency check evaluates against the post-install set so a pin satisfied by an upgraded package is not flagged. Bootable kernel images are made immutable (kernel-immutable rule) since boot regeneration refreshes only the initramfs, not the bootloader menu entries. rpm Obsoletes: declarations are classified as removals so rpm -U cannot silently erase a baseline package past the AllowRemoval gate. Install: the rpm backend switches from `rpm -i` to `rpm -U` when the approved plan contains upgrades; the deb backend is unchanged (dpkg -i upgrades in place). The overlay SBOM records contributed packages (added or upgraded). Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
The overlay boot-regen stage previously ran the initramfs generator unconditionally after any package install. On a systemd-boot/BLS baseline that is not just wasted work for a pure-userspace overlay: update-initramfs chains a loader hook that copies the kernel into the ESP, which overlay mode mounts read-only, so the build fails even though nothing about the boot-time initramfs changed. Gate regeneration on whether the overlay actually added boot-relevant content — a kernel module, firmware, or an initramfs-generator hook — by reading each installed artifact's file manifest on the host (dpkg -c / rpm -qlp). Skip regeneration only when every artifact was read and none carried such content; fail safe (regenerate) on a nil plan, a missing download dir, an unlocatable artifact, or an unreadable manifest, so an initramfs that genuinely needs rebuilding is never left stale. Also surface the generator's captured output in the boot-regen error, mirroring the deb/rpm installer backends: on its own the wrapped error is only "exit status 1" and the actual failing-hook diagnostic was otherwise lost to debug logging. RegenerateBoot now takes the ResolutionPlan (for the artifact download dir the gate reads from); the call site and tests are updated. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
The overlay pipeline does not run through the create-mode maker/chroot stages that populate the template's build timers, so the build-timing table printed on completion was all zeros for an overlay build. Instrument the overlay Builder to record each stage's wall-clock duration in execution order (acquire/mount, inspect, resolve, preflight, install, boot regen, resize, SBOM, emit) via a timeStage helper with a clock seam, and render them as a dedicated "Overlay Build Timings" table on a clean build. Extract the table-drawing logic into a generic display.PrintTimingTable that both the create-mode and overlay paths share. Suppress the all-zero create-mode table for overlay builds so only the accurate table is shown. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
compare --mode=spdx failed on large ext4 Ubuntu RAW images because the diskfs-based filesystem reader could not extract the embedded SBOM. Add a loop-device + debugfs reader that mounts the root partition read-only and pulls /usr/share/sbom/* in place, with no full-partition copy, so large images no longer exhaust temp space. Factor CompareSPDXData out of CompareSPDXFiles so the compare command can diff SBOM bytes read straight from each image, and surface per-partition diagnostic notes when SBOM discovery skips a candidate. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
An overlay image is built on a copy of the baseline and inherits its full embedded SBOM, but overlay mode wrote a delta-only spdx_manifest.json listing just the added packages. It also used a different filename than the baseline's timestamped manifest, so both coexisted at /usr/share/sbom and the delta shadowed the real inventory. A compare of that image then reported the entire baseline as removed (1710 -> 30). Read the inherited baseline SBOM from the mounted root during postprocess and merge the overlay's added/upgraded packages into it, writing under the baseline's own filename so the merged document replaces (not shadows) the baseline SBOM. Packages are keyed by name: a matching name is upgraded in place, a new name is appended; the baseline document header is preserved. Falls back to a delta write (with a warning) when no baseline SBOM is present or it cannot be parsed, so the build never fails on SBOM generation. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
When every required .deb is already cached, DownloadPackagesComplete returned package infos built from filenames via a helper that kept only the name and discarded the version. That empty version flowed into the overlay SBOM's versionInfo, so an upgraded package (e.g. linux-libc-dev) appeared in a name|version|url compare diff as a removed-and-re-added pair instead of an upgrade. Build the cached PackageInfo with parseDebFileName, which recovers both name and version from the name_version_arch.deb filename, and drop the now-unused name-only helper. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
The overlay install set is the transitive closure of the requested packages minus what the baseline already satisfies, so it is routinely larger than the template list. Without a breakdown the extra dependency packages look unexplained when they later surface in the image SBOM or a compare diff. After resolution, log each to-be-installed package tagged [requested] or [dependency] with its version, followed by a summary count of each. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
Reconcile a same-name add+remove pair in the SPDX package diff into a
single upgrade line ("name: fromVer -> toVer") instead of reporting an
unrelated deletion plus addition. A package whose full identity key
(name|version|downloadLocation) changes while its name stays the same is
now surfaced under a dedicated "Upgraded packages" section in both the
text and JSON renderers. Empty baseline versions render as "unknown".
Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
The simulateOverlayInstall seam was a no-op, so a to-install package declaring Conflicts:/Breaks: against a present baseline package was not caught by the preflight gate: conflictPolicy=fail had no conflict source to act on, and the clash only surfaced as an opaque dpkg -i / rpm -i abort at unpack time. Implement the seam as a metadata-based simulation: read each to-install artifact's declared conflicts on the host (dpkg -f Conflicts/Breaks, rpm -qp --conflicts) and classify each one that names a package present in the baseline (at a version the conflict's range covers) as an ActionConflict, gated by conflictPolicy. It runs without a chroot or a real package-manager invocation, so it is deterministic and always executes. Undeclared file-level collisions are not covered here and still fail loudly at install time; a future live simulator can augment this. Widen the seam signature to (info, baseline, plan) since a metadata simulation needs the baseline installed set to know what is present. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
…and preflight Two related overlay-install correctness fixes for lockstep-versioned package sets (e.g. vim-runtime/vim-tiny), where a package Breaks: an older version of another that the overlay upgrades in the same batch: - install: pass --auto-deconfigure to dpkg -i so a transient Breaks: satisfied later in the same batch is deconfigured and reconfigured rather than aborting at unpack time, mirroring apt's behavior. - preflight: evaluate a versioned Conflicts:/Breaks: against the POST-INSTALL version of the target, not the baseline version. When the overlay upgrades the target past the break range in the same batch there is no real conflict, so it must not be flagged. Extract a shared postInstallVersionIndex helper reused by classifyConflicts and classifyUnsatisfiedDeps. Adds regression coverage for both paths. Signed-off-by: Tyagi, Yogesh <yogesh.tyagi@intel.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge Checklist
All boxes should be checked before merging the PR
Description
Builds out overlay mode end-to-end — layering packages onto an existing baseline image instead of building from scratch — and hardens the supporting resolve/preflight/install/SBOM pipeline. Stacks on the merged foundation PRs (#714, #724, #728, #729, #731, #732, #733, #734).
Resolution & operation modes
additive-and-upgradepackage-operation mode: permits upgrading a baseline package to a newer repo version while downgrades and removals stay blocked. The upgrade set is bounded and grown to a fixpoint (requested-and-present packages + transitive deps whose baseline copy fails a version pin); all other present packages keep their baseline version.Obsoletes:are classified as removals sorpm -Ucannot silently erase a baseline package.[requested]vs[dependency]provenance so the transitive closure is explainable in the SBOM/compare.Preflight gating (fail up-front, avoid false positives)
Conflicts:/Breaks:against a present baseline package are caught before install rather than surfacing as an opaquedpkg/rpmabort. Undeclared file-level collisions remain a known gap (still fail loudly at install).vim-tiny/vim-runtime) are evaluated against the post-install version, so an in-batch upgrade past a break range is not falsely flagged; install passes--auto-deconfigure.Boot & SBOM
compareno longer reports the whole baseline as removed. Falls back to a delta write with a warning if the baseline SBOM is missing/unparseable.compare --mode=spdxclassifies same-name version bumps as upgrades, and reads the embedded SBOM from large ext4 RAW images via a read-only loop-device + debugfs reader (no full-partition copy).Misc
Any Newly Introduced Dependencies
None.
go.mod/go.sumare unchanged — no new third-party dependencies. New functionality relies on existing host tooling already used by the project (dpkg,rpm,debugfs, loop devices).How Has This Been Tested?
go test ./internal/... ./cmd/...): overlay resolve, preflight, install, boot update, depcheck, session, SBOM; config merge/baseline; SBOM filesystem + loop-device readers; compare renderers; debutils download. New/updated test files includeresolve_test.go,preflight_test.go,depcheck_test.go,bootupdate_test.go,sbom_loopdev_test.go,sbom_fsread_test.go,merge_test.go, and others.go build ./internal/... ./cmd/...thengo test ./internal/... ./cmd/....