Skip to content

Add support for OCI image manifests and indexes#584

Open
aman-coder03 wants to merge 2 commits intouber:masterfrom
aman-coder03:oci-manifest
Open

Add support for OCI image manifests and indexes#584
aman-coder03 wants to merge 2 commits intouber:masterfrom
aman-coder03:oci-manifest

Conversation

@aman-coder03
Copy link
Copy Markdown

What this PR does

Kraken currently only supports Docker v2 manifest formats. This PR adds support for OCI image manifests and image indexes so that images using OCI media types can be handled correctly

Changes made

  • added parsing support for:
    application/vnd.oci.image.manifest.v1+json
    application/vnd.oci.image.index.v1+json
  • extended the manifest parsing flow to fall back to OCI formats if Docker formats don’t match
  • updated the manifest media type regex in preheat.go to recognize OCI types
  • included OCI media types in GetSupportedManifestTypes so they are sent in Accept headers
  • promoted github.com/opencontainers/image-spec to a direct dependency for using official media type constants

Testing

  • added unit tests for both OCI manifest and OCI index parsing
  • verified that ParseManifest correctly handles OCI inputs end-to-end

Why this is needed

OCI image formats are widely used across modern container registries. Without this support, Kraken fails to process such images. This change ensures compatibility with OCI-compliant registries while keeping existing Docker support intact

fixes #574

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds OCI image manifest (application/vnd.oci.image.manifest.v1+json) and OCI image index (application/vnd.oci.image.index.v1+json) support so Kraken can parse and advertise OCI media types in registry interactions.

Changes:

  • Extend manifest parsing to fall back from Docker v2 manifest/list to OCI manifest/index formats.
  • Include OCI media types in preheat media-type matching and in Accept headers via GetSupportedManifestTypes().
  • Add unit tests for OCI manifest/index parsing and promote opencontainers/image-spec to a direct dependency.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
utils/dockerutil/dockerutil.go Adds OCI manifest/index parsing and expands supported manifest media types.
utils/dockerutil/dockerutil_test.go Adds unit tests for OCI manifest/index parsing paths.
proxy/proxyserver/preheat.go Updates manifest media type regex to recognize OCI manifest/index events.
go.mod Promotes github.com/opencontainers/image-spec to a direct dependency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +58 to +59
// Try OCI image index.
return ParseManifestOCIIndex(b)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

ParseManifest falls through multiple unmarshal attempts but, if all attempts fail, it returns only the final OCI-index error. This can produce misleading error messages (e.g., reporting an OCI-index parse failure for non-OCI inputs) and makes debugging harder. Consider returning an aggregated error that includes failures from each attempted format, or detect the manifest mediaType from JSON first and unmarshal only the matching type.

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +130
_, ok := manifestIndex.(*manifestlist.DeserializedManifestList)
if !ok {
return nil, core.Digest{}, errors.New("expected manifestlist.DeserializedManifestList for OCI index")
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

ParseManifestOCIIndex asserts the returned type but does not validate SchemaVersion (unlike ParseManifestV2List, which checks SchemaVersion==2 on the same DeserializedManifestList type). For consistency and to avoid accepting unsupported index versions, store the cast result and validate SchemaVersion before returning.

Suggested change
_, ok := manifestIndex.(*manifestlist.DeserializedManifestList)
if !ok {
return nil, core.Digest{}, errors.New("expected manifestlist.DeserializedManifestList for OCI index")
}
deserializedManifestIndex, ok := manifestIndex.(*manifestlist.DeserializedManifestList)
if !ok {
return nil, core.Digest{}, errors.New("expected manifestlist.DeserializedManifestList for OCI index")
}
version := deserializedManifestIndex.SchemaVersion
if version != 2 {
return nil, core.Digest{}, fmt.Errorf("unsupported OCI image index version: %d", version)
}

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +39
var _manifestRegexp = regexp.MustCompile(
`^application/vnd\.docker\.distribution\.manifest\.v\d\+(json|prettyjws)` +
`|^application/vnd\.oci\.image\.manifest\.v1\+json` +
`|^application/vnd\.oci\.image\.index\.v1\+json`,
)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

Adding OCI index media types to the preheat event filter means Kraken will start processing push events whose manifest.References() typically include only child manifests (not layer/config blobs). With the current process() implementation, this can result in preheating only nested manifests rather than the actual layers unless additional manifest push events are also received. Consider either keeping index/list types out of this filter, or recursively fetching referenced manifests and preheating their references to ensure layers are warmed.

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +132
manifest, d, err := dockerutil.ParseManifestOCI(testOCIManifestBytes)
require.NoError(err)
mediaType, _, err := manifest.Payload()
require.NoError(err)
require.Equal("application/vnd.oci.image.manifest.v1+json", mediaType)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

This test only asserts that the returned digest uses the sha256 algorithm, which would still pass if the digest value is incorrect. Consider asserting the full digest string matches the expected digest of testOCIManifestBytes (and adding a negative test case to ensure ParseManifestOCI rejects non-OCI media types).

Copilot uses AI. Check for mistakes.
Comment on lines +138 to +142
manifest, d, err := dockerutil.ParseManifestOCIIndex(testOCIIndexBytes)
require.NoError(err)
mediaType, _, err := manifest.Payload()
require.NoError(err)
require.Equal("application/vnd.oci.image.index.v1+json", mediaType)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

This test only checks the digest algorithm rather than the full digest value, and it doesn't exercise error paths (e.g., passing a manifest instead of an index). Consider asserting the exact digest string for testOCIIndexBytes and adding a failure-case test to confirm ParseManifestOCIIndex rejects non-index inputs.

Copilot uses AI. Check for mistakes.
}
_, ok := manifestIndex.(*manifestlist.DeserializedManifestList)
if !ok {
return nil, core.Digest{}, errors.New("expected manifestlist.DeserializedManifestList for OCI index")
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

The error message here exposes the internal Docker distribution type name rather than describing the OCI concept being validated. Consider rewording to something user-facing like "expected OCI image index" (and optionally including the actual type via %T) to make troubleshooting easier.

Suggested change
return nil, core.Digest{}, errors.New("expected manifestlist.DeserializedManifestList for OCI index")
return nil, core.Digest{}, fmt.Errorf("expected OCI image index, got %T", manifestIndex)

Copilot uses AI. Check for mistakes.
@aman-coder03
Copy link
Copy Markdown
Author

good point, the current process() implementation calls manifest.References() which for an OCI index returns the child manifests rather than layer blobs directly
however, including the OCI index media type in the filter is still useful, it ensures the index manifest itself is fetched and cached, and the child manifest push events(which are separate registry notifications) will each trigger their own preheat cycle that reaches the actual layers. Recursive index resolution would be a more complete solution but is out of scope for this PR
happy to track it as a follow-up issue if that would be useful...

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kraken does not support OCI manifest files

2 participants