mirror of
https://github.com/games-on-whales/LXC2Docker.git
synced 2026-10-15 12:52:04 +00:00
[PR #148] [MERGED] fix(images/containers): real config-digest image IDs + StartedAt on every start path #148
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
games-on-whales/LXC2Docker#148
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/games-on-whales/LXC2Docker/pull/148
Author: @JBailes
Created: 7/25/2026
Status: ✅ Merged
Merged: 7/25/2026
Merged by: @JBailes
Base:
main← Head:worktree-image-id-parity📝 Commits (2)
f2785c3fix(images): content-address pulled images with the real config digest250c882fix(containers): stamp StartedAt on every start path, not just the API handler📊 Changes
5 files changed (+222 additions, -14 deletions)
View changed files
📝
internal/api/containers.go(+5 -8)📝
internal/lxc/manager.go(+30 -0)➕
internal/lxc/started_at_test.go(+82 -0)➕
internal/oci/config_digest_test.go(+93 -0)📝
internal/oci/pull.go(+12 -6)📄 Description
Two docker-parity gaps reported from a live deploy, both confirmed against the code and fixed here.
1. Pulled images weren't content-addressed
imageDisplayIDalready prefers a record'sConfigDigest(the sha256 of the OCI image config — stock Docker's image ID) and only falls back to the tag-derivedoci_<safe-ref>pseudo-ID. Build (build.go) and commit (portainer_ops.go) both compute one; pull never did. Sodocker imagesshowed IDs likesha256:oci_ghcr_io_org_app_testing, and two different images pushed to the same tag collapsed onto one ID.The value was already being read and thrown away:
parseImageConfigparsesmanifest.Config.Digestto locate the config blob, then dropped it. It's now surfaced onoci.ImageConfigand recorded inpullOCI— in both the store record and theoci-meta.jsonsidecar, so a restored record keeps it.This is image identity, orthogonal to refresh: a moved tag is detected by comparing the registry manifest digest (
RepoDigest), whichPullImagealready does.2.
StartedAtwas stamped only by the API start handlerEvery other start path calls
Manager.StartContainerdirectly — the restart-policy watcher, the startup reconcile, andPOST /restart— and none stamped it. A policy-started container reported0001-01-01T00:00:00Zfromdocker inspectwhile plainly running, anddocker restartnever refreshed it.The functional tail matters more than the cosmetic one: the watcher branches on
StartedAt == nilandcontinues before its exited check, so a container the watcher itself started stayed permanently in the "never started" branch and never got restart-policy handling on later crashes — analwayscontainer would come up once, then stay dead after its next exit.The stamp moves into
StartContainer, the choke point all start paths share — the same reasoning the existingensurePortForwardscomment there documents for an identical past gap.StoppedByUserdeliberately stays in the API handler: it records user intent, so a policy convergence must not silently un-stop a hand-stopped container.Not changed
A third report — that moved tags don't refresh on recreate — did not reproduce as a missing feature. Two layers already exist:
PullImagere-checks the registry manifest digest for mutable tags and drops the stale template, andreusableContainerrefuses a warm rootfs on digest drift (#88, #126, #128). Worth noting stock Docker doesn't re-resolve a locally-present tag oncreate/runeither — that's what--pull=alwaysis for — so "pull + pin before materialise" is correct practice on both. A stale blob served after an explicit pull would point at a deployed binary predating those commits.Testing
go buildandgo vetclean. New unit tests ininternal/oci/config_digest_test.go(digest captured; distinct content yields distinct IDs) andinternal/lxc/started_at_test.go(stamped on first start, refreshed on restart withFinishedAtcleared, no-op on a concurrently-removed record).Full suite green except
TestCheckCreateDiskPressure, which fails identically on unmodified main on hosts wheret.TempDir()lands on tmpfs —checkCreateDiskPressuredeliberately skips tmpfs sources. Environmental, pre-existing, unrelated to this PR; CI is unaffected.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.