feat: push OCI/docker image archive tarballs (NCN-113655)#1038
Merged
Conversation
Spec for enhancing mindthegap to push OCI image layout and docker-save tarballs directly, analogous to `crane push`, plus a detection hook in `push bundle` that emits a helpful error when pointed at an image archive rather than a mindthegap bundle. Refs: https://jira.nutanix.com/browse/NCN-113655
Step-by-step TDD plan for the push image-archive subcommand and the push bundle detection hook. Broken into 18 bite-sized tasks covering archive reader, command wiring, validation, push loop, documentation and e2e coverage with concrete code for every step. Refs: https://jira.nutanix.com/browse/NCN-113655
Introduce the images/archive package with the Format enum that will drive archive type detection for the upcoming push image-archive subcommand. Refs: NCN-113655
Detect scans tar headers once at the root level looking for oci-layout or manifest.json markers, returning FormatUnknown when neither is present. This primitive will be used by push bundle to short-circuit with a helpful error when the user points it at an image archive. Refs: NCN-113655
Introduce the Archive interface and Entry struct that will carry a v1.Image or v1.ImageIndex with an optional embedded reference. Open() detects the format and routes to docker or OCI readers; readers currently return 'not implemented' and are fleshed out in follow-up commits. Refs: NCN-113655
Parse manifest.json from a docker-save tarball and emit one Entry per RepoTags value. Untagged entries are also surfaced with a nil Ref so the caller can apply a destination tag override. Refs: NCN-113655
Read OCI image layout tarballs through archives.FileSystem with no disk extraction. Blobs are served on demand via an fs.FS-backed partial.CompressedImageCore; image indexes are returned as a small v1.ImageIndex implementation. The reference name (if any) is taken from the org.opencontainers.image.ref.name annotation on the top-level descriptor. Refs: NCN-113655
Adds unit coverage for multi-arch OCI indexes (v1.ImageIndex) and for both OCI and docker archives containing zero images, which the command layer treats as a no-op rather than an error. Refs: NCN-113655
Register the cobra command with its full flag set (image-archive, to-registry, TLS/auth, image-tag); the RunE body is a stub that returns nil and will be implemented alongside the push logic. Refs: NCN-113655
Open each --image-archive argument up front (expanding globs) and enforce the precondition that --image-tag is only valid with a single archive containing a single image. The override is also validated as a strict OCI reference. Push is still a no-op and is added in a follow-up commit. Refs: NCN-113655
Wire up the TLS/auth plumbing (mirroring push bundle), resolve each archive entry's destination reference using the embedded tag or --image-tag override, and push images via remote.Write / image indexes via remote.WriteIndex. End-to-end unit tests cover both docker-save and OCI layout archives via an in-process go-containerregistry registry. Refs: NCN-113655
Adds a regression test ensuring that pushing an archive which contains no embedded tag and no --image-tag override produces a clear error that mentions --image-tag so the user knows how to unblock themselves. Refs: NCN-113655
Before extracting bundle configs, inspect each --bundle path with archive.Detect and abort with a pointer to push image-archive when the file is actually an OCI or docker image archive. This replaces the confusing silent no-op (or unrelated extraction error) users previously saw. Refs: NCN-113655
Use containers/image reference.Parse (without Docker Hub normalization) for --image-tag so that a single-segment override like "override:v3" is pushed to the destination as "override:v3" rather than being normalized to "library/override:v3". Embedded tags continue to use ParseNormalizedNamed to strip any registry host and preserve legacy Docker Hub semantics. Refs: NCN-113655
Cover OCI layout push, docker-save push, push bundle detection against an image archive, multi-image docker-save push, the --image-tag override on a tagless OCI archive, and a DescribeTable over TLS variants (loopback plain-HTTP, TLS with CA file, insecure TLS, and explicit http scheme) mirroring the push bundle e2e coverage. Refs: NCN-113655
Wrap the in-process go-containerregistry registry with a basic-auth gate via httptest.Server and assert that the correct credentials succeed while mismatched credentials fail. Exercises the --to-registry-username / --to-registry-password plumbing without requiring an htpasswd-capable test registry. Refs: NCN-113655
Describe the new subcommand, its supported input archive formats, tag resolution behaviour, and the --image-tag override. Refs: NCN-113655
Contributor
Unit test results155 tests 155 ✅ 0s ⏱️ Results for commit c14ca1e. ♻️ This comment has been updated with latest results. |
Contributor
e2e test results59 tests 59 ✅ 3m 26s ⏱️ Results for commit c14ca1e. ♻️ This comment has been updated with latest results. |
rejectImageArchives previously wrapped any archive.Detect error and returned it as an "inspecting bundle" failure, which shadowed the existing "compressed tar archives (.tar.gz) are not supported" error path for gzipped bundles (detected when archive/tar rejects the gzip magic bytes with "invalid tar header"). Ignore Detect errors instead so unclassifiable files fall through to downstream handlers unchanged; only surface the helpful "use push image-archive" error when Detect unambiguously identifies an OCI layout or docker-save tarball. Also add a unit regression covering the .tar.gz path. Refs: NCN-113655
Merged
jimmidyson
pushed a commit
that referenced
this pull request
May 19, 2026
🤖 I have created a release *beep* *boop* --- ## 1.26.0 (2026-05-19) <!-- Release notes generated using configuration in .github/release.yaml at main --> ## What's Changed ### Exciting New Features 🎉 * feat: push OCI/docker image archive tarballs (NCN-113655) by @jimmidyson in #1038 ### Fixes 🔧 * fix: Allow same-host private/loopback registry bearer realms (NCN-114223) by @jimmidyson in #1046 ### Other Changes * build: bump Go toolchain to 1.26.3 by @jimmidyson in #1048 **Full Changelog**: v1.25.4...v1.26.0 --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: mesosphere-actions-pr-bot[bot] <157582460+mesosphere-actions-pr-bot[bot]@users.noreply.github.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.
Summary
mindthegap push image-archivesubcommand for pushing OCI imagelayout tarballs and docker-save tarballs directly to an OCI registry,
analogous to
crane push.mindthegap push bundlewith content-type detection that emitsa helpful error when pointed at an image archive rather than a
mindthegap bundle.
docker save,podman save) and OCI layout(
skopeo copy docker://... oci-archive:out.tar,crane push --format=oci,buildah push ... oci-archive:out.tar) tarballs withcontent-based auto-detection and no on-disk extraction.
RepoTags[0]or the OCIorg.opencontainers.image.ref.nameannotation);
--image-tag <repo:tag>overrides when exactly onearchive with one image is supplied.
push bundleTLS/auth flag surface (--to-registry-ca-cert-file,--to-registry-insecure-skip-tls-verify,--to-registry-username,--to-registry-password).Test plan
(including multi-arch and empty archives),
--image-tagvalidation,reference resolution, basic auth plumbing, and the tagless-archive
error path.
push, multi-image push,
--image-tagoverride on a tagless OCIarchive,
push bundlerejection of an image archive, and aDescribeTable over TLS variants (loopback plain-HTTP, TLS with CA
file, insecure TLS, explicit http scheme).
task test:unitpasses (154 tests).pre-commit run --all-filespasses (21 hooks).go test -tags=e2e ./test/e2e/imagearchive/...passes (9 specs).Refs: https://jira.nutanix.com/browse/NCN-113655