fix(spur-net): honor requested image architecture - #438
Conversation
|
@shiv-tyagi Thanks for the review! All checks are green -- could I please get some help merging? |
|
Solid change. The staged-replacement design is careful — same-filesystem rename, the existing artifact is left intact on a failed pull, and sidecar-less artifacts are safely rebuilt once — and the new pure functions are well-tested and network-free. No blocking issues; notes inline. Two items are pre-existing (not regressions from this PR), flagged just for context: the layer-extraction gate |
| // Clean up temp dir | ||
| let finalize_result = (|| -> anyhow::Result<()> { | ||
| std::fs::write(&staged_arch_path, oci_architecture(arch))?; | ||
| std::fs::rename(&staged_sqsh_path, &sqsh_path) |
There was a problem hiding this comment.
The install is two separate renames (.sqsh then .sqsh.arch). A crash between them leaves the new payload with a stale-or-missing arch sidecar. It is self-healing (the next pull rebuilds), but since the sidecar is the source of truth for the "rebuild on arch change" decision, writing the metadata first and renaming the payload in last (payload as the commit point) would make "sqsh present implies arch recorded" hold.
There was a problem hiding this comment.
Good catch. First-time installs now publish the sidecar before exposing the payload. Replacements invalidate the old sidecar before publishing the new payload, then publish the new sidecar last, so an interruption forces a cache miss rather than pairing a payload with stale metadata. Both paths have filesystem-level regression coverage.
|
|
||
| // Clean up temp dir | ||
| let finalize_result = (|| -> anyhow::Result<()> { | ||
| std::fs::write(&staged_arch_path, oci_architecture(arch))?; |
There was a problem hiding this comment.
The sidecar is written as the requested arch unconditionally. On the single-manifest path (a registry returning an image manifest directly rather than an index), no platform verification runs, so the recorded arch could be wrong and defeat the rebuild-on-arch-change guard. Verifying the fetched manifest's platform (or documenting the limitation) would close this.
There was a problem hiding this comment.
Fixed by validating direct manifests through their OCI config blob before layer extraction. The config os and normalized architecture must match the requested Linux platform before the sidecar can be recorded, with regression coverage for matching and mismatched platforms.
| pub async fn import_image(uri: &str) -> anyhow::Result<PathBuf> { | ||
| let dir = image_dir(); | ||
| spur_net::pull_image(uri, &dir).await | ||
| spur_net::pull_image(uri, &dir, std::env::consts::ARCH).await |
There was a problem hiding this comment.
Requested arch is honored on the pull path, but import_image here hardcodes host arch and resolve_image matches purely by filename (never reading the .sqsh.arch sidecar) — so a cross-arch artifact could be launched on the wrong host unchecked. Likely out of scope for this PR's title, but worth a follow-up; the sidecar added here is the enabling primitive for that check.
There was a problem hiding this comment.
This is a separate execution-time validation boundary. This PR keeps daemon-side imports host-native and stays focused on honoring the requested architecture during import; changing resolve_image and launch behavior would expand beyond issue #343 acceptance criteria. I am leaving that follow-up out of this patch.
|
|
||
| // Create temp directory for rootfs assembly | ||
| let tmp_dir = output_dir.join(format!(".pulling_{}", sanitized)); | ||
| if tmp_dir.exists() { |
There was a problem hiding this comment.
Minor: the .pulling_<name> temp dir is deterministic per image, so two concurrent pulls of the same image race — and this unconditional remove_dir_all can wipe an in-flight pull's working tree. A PID/uuid suffix would make it safe. Low likelihood for interactive imports.
There was a problem hiding this comment.
The shared staging directory is gone. Each pull now uses a UUID-suffixed directory, and same-image pulls are serialized through the cache recheck and final publication, so concurrent imports cannot delete or interleave one another's artifacts.
| "x86_64" => "amd64", | ||
| "aarch64" => "arm64", | ||
| "x86" => "386", | ||
| arch => arch, |
There was a problem hiding this comment.
Minor: the normalization fallthrough passes unknown arches through unchanged; combined with Platform not deserializing variant, 32-bit arm (v6/v7) cannot be disambiguated and the first arm entry wins. Fine for the amd64/arm64 common case — flagging for completeness.
There was a problem hiding this comment.
That limitation remains intentional here. The issue contract is architecture selection, while arm v6/v7 support needs a variant input and normalization contract rather than silently choosing the first arm entry. I am keeping variant handling out of this patch.
| .args([ | ||
| rootfs_dir.to_str().unwrap(), | ||
| sqsh_path.to_str().unwrap(), | ||
| staged_sqsh_path.to_str().unwrap(), |
There was a problem hiding this comment.
Nit: to_str().unwrap() in a library crate trips the no-unwrap-in-lib guideline (AGENTS.md). Pre-existing, but since this line is in the diff, a .context("non-UTF-8 image path")? would be a cheap tidy-up.
There was a problem hiding this comment.
Cleaned this up by passing the filesystem paths directly to Command, removing both UTF-8 conversions and the library unwrap() calls.
|
@hnotshe Can you please address Yan's comments? |
# Conflicts: # crates/spur-cli/src/image.rs # crates/spur-net/src/oci.rs
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #438 +/- ##
==========================================
+ Coverage 76.81% 76.82% +0.01%
==========================================
Files 169 169
Lines 68824 69115 +291
==========================================
+ Hits 52866 53096 +230
- Misses 15958 16019 +61 🚀 New features to boost your workflow:
|
Closes #343.
Thread the requested image architecture through the native OCI pull path and use it when resolving multi-architecture manifest lists. Host architecture names are normalized to their OCI equivalents, and missing variants now report the requested architecture.
Imported squashfs artifacts record their architecture so an existing image for another architecture is rebuilt instead of silently reused. Replacement is staged so a failed pull does not destroy the existing artifact.
Tested with workspace Clippy and the full locked test suite on Linux.