Skip to content

feat: migrate zarf tools registry ls from crane to oras-go - #5199

Open
abhishekgit03 wants to merge 4 commits into
zarf-dev:mainfrom
abhishekgit03:issue-5003
Open

feat: migrate zarf tools registry ls from crane to oras-go#5199
abhishekgit03 wants to merge 4 commits into
zarf-dev:mainfrom
abhishekgit03:issue-5003

Conversation

@abhishekgit03

Copy link
Copy Markdown
Contributor

Related to #5003

First PR in the phased migration of zarf tools registry from Crane to ORAS, as discussed in the issue. This one covers list (ls), the smallest command, to establish the pattern the rest will follow (digest/manifest next, per the agreed ordering).

Why

zarf tools registry currently wraps Crane's own cobra commands directly, backed by go-containerregistry. Crane assumes every image layer is a tar file, which breaks on OCI images with non-tar layers. oras-go doesn't make that assumption.

What changed

  • New src/cmd/registry_list.go reimplements ls using oras-go instead of craneCmd.NewCmdList, preserving the exact same flags (--full-ref, -O/--omit-digest-tags), example text, and behavior.
  • src/cmd/crane.go: swapped the Crane-wrapped list registration for the new command. One line changed.
  • Reuses existing patterns rather than inventing new ones: images.NewAuthClientFromDocker for the Docker-credential fallback path, the same orasRemote.Repository construction shape already used in src/pkg/images/pull.go.

Zarf-managed registry support

When the target repo matches a Zarf-managed cluster's registry, this transparently tunnels to it and authenticates with Zarf's own registry credentials (mirroring what zarfCraneInternalWrapper does for the not-yet-migrated commands). Two things worth flagging for review:

  • The credential must be keyed to whichever host ORAS actually connects to (the tunnel's local endpoint, not the original registry address)
  • For mTLS-secured registries, scheme resolution uses RegistryInfo.ResolvePlainHTTP, which already knows the answer from state without needing to probe the network - a generic probe would need to present a client certificate it doesn't have.

Test plan

  • Unit tests in src/cmd/registry_list_test.go (default/--full-ref/--omit-digest-tags behavior, invalid-ref error path, a regression test proving the known-scheme value is trusted rather than silently re-probed)
  • make docs-and-schema - zero diff, confirming generated CLI docs are unchanged from the Crane-wrapped version
  • Tested live against a real cluster
  • go build ./..., golangci-lint run ./..., pre-commit run --all-files

Signed-off-by: Abhishek Dasgupta <abhishek20dgp@gmail.com>
@abhishekgit03
abhishekgit03 requested review from a team as code owners August 8, 2026 19:45
@netlify

netlify Bot commented Aug 8, 2026

Copy link
Copy Markdown

Deploy Preview for zarf-docs canceled.

Name Link
🔨 Latest commit 94374ec
🔍 Latest deploy log https://app.netlify.com/projects/zarf-docs/deploys/6a7cde12c2e8ca0008a41a3c

@a1994sc

a1994sc commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

This is a personal question, but do you mind expanding on this?

Crane assumes every image layer is a tar file, which breaks on OCI images with non-tar layers. oras-go doesn't make that assumption.

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 32.35294% with 92 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/cmd/registry_list.go 31.85% 89 Missing and 3 partials ⚠️
Files with missing lines Coverage Δ
src/cmd/crane.go 24.50% <100.00%> (ø)
src/cmd/registry_list.go 31.85% <31.85%> (ø)

... and 28 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@abhishekgit03

Copy link
Copy Markdown
Contributor Author

This is a personal question, but do you mind expanding on this?

Crane assumes every image layer is a tar file, which breaks on OCI images with non-tar layers. oras-go doesn't make that assumption.

image layers used to always be gzipped tar archives (a filesystem diff), so Crane's layer-handling code assumes that. The OCI spec has since moved on, a manifest's layers can now be arbitrary blobs (WASM binaries, SBOMs, signatures, anything), not just tar diffs. Crane never updated that assumption, so it breaks when it tries to read non-tar content as if it were a tar stream. oras-go treats layers as opaque blobs by media type from the start, so it doesn't hit this.

@a1994sc

a1994sc commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

This is a personal question, but do you mind expanding on this?

Crane assumes every image layer is a tar file, which breaks on OCI images with non-tar layers. oras-go doesn't make that assumption.

image layers used to always be gzipped tar archives (a filesystem diff), so Crane's layer-handling code assumes that. The OCI spec has since moved on, a manifest's layers can now be arbitrary blobs (WASM binaries, SBOMs, signatures, anything), not just tar diffs. Crane never updated that assumption, so it breaks when it tries to read non-tar content as if it were a tar stream. oras-go treats layers as opaque blobs by media type from the start, so it doesn't hit this.

Thank you very much for that description!

@abhishekgit03

Copy link
Copy Markdown
Contributor Author

Hi @AustinAbro321 @brandtkeller can you have a look at this when you get a chance? let me know if the approach is fine.

@AustinAbro321 AustinAbro321 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall design looks good, a few comments on keeping parity.

Comment thread src/cmd/registry_list.go Outdated
// Credential must be keyed to the host ORAS actually connects to: the tunnel when tunneling, otherwise the registry's own address.
credentialHost := s.RegistryInfo.Address
if tunnel != nil {
credentialHost = endpoint

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The credentialHost should be set from a function like below which will take the conn.Ref.

func registryHost(repoRef string) (string, error) {
	ref, err := registry.ParseReference(repoRef)
	if err != nil {
		return "", fmt.Errorf("parsing repo %q: %w", repoRef, err)
	}
	return ref.Host(), nil
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed by using registryHost() method.

Comment thread src/cmd/registry_list.go Outdated
client.Client.Transport = t
}

plainHTTP, err := s.RegistryInfo.ResolvePlainHTTP(ctx, credentialHost, false, ocischeme.ProbeOptions{InsecureSkipTLSVerify: insecure})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In crane --insecure means both --plain-http and --insecure-tls-skip-verify. I'd like to get rid of this design as it overloads a flag, but I don't want to have a breaking change without a deprecation process.

Deprecate the insecure flag (for this command specifically), and add the --plain-http and --insecure-skip-tls-verify flags to zarf tools registry ls. As we go through other commands, we'll follow the same process.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. added --plain-http and --insecure-skip-tls-verify

@github-project-automation github-project-automation Bot moved this to In progress in Zarf Aug 12, 2026
…tion and HTTP usage

Signed-off-by: Abhishek Dasgupta <abhishek20dgp@gmail.com>
Signed-off-by: Abhishek Dasgupta <abhishek20dgp@gmail.com>
Signed-off-by: Abhishek Dasgupta <abhishek20dgp@gmail.com>

@AustinAbro321 AustinAbro321 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed one more regression, overall looks good

Comment thread src/cmd/registry_list.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed one more regression where docker.io is not the default registry after this change. For instance, on main, zarf tools registry ls stefanprodan/podinfo:6.4.0 will work, but it will fail on this branch.

You should be able to use reference.ParseNormalizedNamed for this, we use similar functions in other spots in the repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

3 participants