Skip to content

feat: push OCI/docker image archive tarballs (NCN-113655)#1038

Merged
jimmidyson merged 18 commits into
mainfrom
NCN-113655/push-image-archive
Apr 20, 2026
Merged

feat: push OCI/docker image archive tarballs (NCN-113655)#1038
jimmidyson merged 18 commits into
mainfrom
NCN-113655/push-image-archive

Conversation

@jimmidyson

Copy link
Copy Markdown
Contributor

Summary

  • Adds mindthegap push image-archive subcommand for pushing OCI image
    layout tarballs and docker-save tarballs directly to an OCI registry,
    analogous to crane push.
  • Extends mindthegap push bundle with content-type detection that emits
    a helpful error when pointed at an image archive rather than a
    mindthegap bundle.
  • Supports docker-save (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 with
    content-based auto-detection and no on-disk extraction.
  • Destination references are taken from embedded metadata (docker
    RepoTags[0] or the OCI org.opencontainers.image.ref.name
    annotation); --image-tag <repo:tag> overrides when exactly one
    archive with one image is supplied.
  • Mirrors push bundle TLS/auth flag surface (--to-registry-ca-cert-file,
    --to-registry-insecure-skip-tls-verify, --to-registry-username,
    --to-registry-password).

Test plan

  • Unit tests covering archive detection, docker reader, OCI reader
    (including multi-arch and empty archives), --image-tag validation,
    reference resolution, basic auth plumbing, and the tagless-archive
    error path.
  • End-to-end tests (Ginkgo) covering OCI-layout push, docker-save
    push, multi-image push, --image-tag override on a tagless OCI
    archive, push bundle rejection of an image archive, and a
    DescribeTable over TLS variants (loopback plain-HTTP, TLS with CA
    file, insecure TLS, explicit http scheme).
  • README documents the new subcommand with an example.
  • task test:unit passes (154 tests).
  • pre-commit run --all-files passes (21 hooks).
  • go test -tags=e2e ./test/e2e/imagearchive/... passes (9 specs).

Refs: https://jira.nutanix.com/browse/NCN-113655

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
@github-actions

github-actions Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Unit test results

155 tests   155 ✅  0s ⏱️
 30 suites    0 💤
  1 files      0 ❌

Results for commit c14ca1e.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

e2e test results

59 tests   59 ✅  3m 26s ⏱️
 3 suites   0 💤
 1 files     0 ❌

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
@jimmidyson jimmidyson enabled auto-merge (squash) April 20, 2026 14:48
@jimmidyson jimmidyson disabled auto-merge April 20, 2026 15:13
@jimmidyson jimmidyson merged commit 02e9b57 into main Apr 20, 2026
17 of 18 checks passed
@jimmidyson jimmidyson deleted the NCN-113655/push-image-archive branch April 20, 2026 15:13
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant