Add OCI-based default UKI Configuration for HTTPBoot#266
Add OCI-based default UKI Configuration for HTTPBoot#266
Conversation
WalkthroughCentralizes UKI URL construction in a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant BootServer
participant UKIHelper as internal/uki
participant OCIRegistry as OCI Registry / Image Server
Client->>BootServer: HTTPBoot request (no explicit UKI)
BootServer->>UKIHelper: ConstructUKIURLFromOCI(image, imageServerURL, arch)
UKIHelper->>OCIRegistry: Resolve image reference / fetch manifest(s)
OCIRegistry-->>UKIHelper: Manifest / index JSON
UKIHelper->>OCIRegistry: Fetch nested manifest/layer blob
OCIRegistry-->>UKIHelper: Layer blob (UKI) with digest
UKIHelper-->>BootServer: UKI URL (imageServerURL/image:sha256:digest)
BootServer-->>Client: HTTPBoot response with constructed UKI URL
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
b4d08c3 to
ac3a292
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@internal/uki/oci.go`:
- Around line 57-66: The loop is shadowing the outer manifest variable and may
leave targetManifestDesc set to the initial desc when no architecture match is
found; update the code in the block that iterates indexManifest.Manifests so you
do not shadow the outer manifest (rename the loop variable, e.g., to m or
entry), ensure targetManifestDesc is initialized to a zero/empty descriptor (or
use a found flag) before the loop, set targetManifestDesc only when m.Platform
!= nil && m.Platform.Architecture == architecture, and after the loop check that
targetManifestDesc is actually populated (or the found flag is true) before
using its Digest; reference: indexManifest, targetManifestDesc, manifest
(outer), architecture, desc.
- Around line 20-24: The image parsing in ConstructUKIURLFromOCI incorrectly
splits on all ':' which breaks registry host:port cases (e.g.,
myregistry:5000/repo/image:v1.0); update the function to parse the OCI reference
properly by using a robust parser like github.com/distribution/reference or
github.com/google/go-containerregistry/pkg/name to extract the repository/name
and tag/digest, or at minimum use strings.LastIndex to separate the tag (and
handle image@digest with '@') instead of strings.Split, then build the UKI URL
from the parsed reference parts (retain imageServerURL and architecture logic).
In `@server/bootserver.go`:
- Around line 409-429: The code may return an empty UKI URL when both
defaultOCIImage and defaultUKIURL are empty, causing clients to receive
"UKIURL": ""; update the logic where ukiURL is set (the block handling
defaultOCIImage/defaultUKIURL that populates httpBootResponseData) to detect the
case both defaultOCIImage == "" and defaultUKIURL == "" and either log a
warning/error via log.Warn/ log.Error and return an HTTP error (http.Error) (or
set a clear fallback value) instead of silently returning an empty UKIURL;
ensure the change references ukiURL, defaultOCIImage, defaultUKIURL,
httpBootResponseData and preserves the existing OCI construction path using
uki.ConstructUKIURLFromOCI.
🧹 Nitpick comments (4)
internal/uki/oci.go (1)
102-106: Avoidfmt.Printffor error logging in library code.Using
fmt.Printffor logging is not suitable for production code. The calling code may have structured logging configured, and this bypasses it. Consider returning the close error or accepting a logger parameter.♻️ Proposed fix to silently ignore close error or propagate it
defer func() { - if cerr := reader.Close(); cerr != nil { - fmt.Printf("failed to close reader: %v\n", cerr) - } + _ = reader.Close() }()internal/controller/serverbootconfiguration_http_controller.go (1)
182-187: Consider validatingArchitecturefield is non-empty.The method validates
ImageServerURLbut notArchitecture. IfArchitectureis empty, it will be passed toConstructUKIURLFromOCI, which may fail to find a matching manifest or produce unexpected behavior.♻️ Proposed fix to validate Architecture
func (r *ServerBootConfigurationHTTPReconciler) constructUKIURL(ctx context.Context, image string) (string, error) { if strings.TrimSpace(r.ImageServerURL) == "" { return "", fmt.Errorf("image server URL is empty") } + if strings.TrimSpace(r.Architecture) == "" { + return "", fmt.Errorf("architecture is empty") + } return uki.ConstructUKIURLFromOCI(ctx, image, r.ImageServerURL, r.Architecture) }server/bootserver.go (1)
52-52: Consider using a configuration struct for cleaner function signatures.
RunBootServernow has 8 parameters, which impacts readability and maintainability. A configuration struct would make the API cleaner and easier to extend.♻️ Example configuration struct approach
type BootServerConfig struct { Addr string IPXEServiceURL string K8sClient client.Client Log logr.Logger DefaultOCIImage string DefaultUKIURL string ImageServerURL string Architecture string } func RunBootServer(cfg BootServerConfig) { // ... }cmd/main.go (1)
316-316: Both deprecated and new flag values are passed despite "ignoring" warning.At line 132-133, when both flags are provided, a warning states
--default-httpboot-uki-urlis being ignored. However, both values are still passed toRunBootServer. While the server logic handles precedence correctly, consider clearingdefaultHTTPBootUKIURLwhen it's being ignored for consistency with the warning message.♻️ Proposed fix to clear ignored value
if defaultHTTPBootOCIImage != "" && defaultHTTPBootUKIURL != "" { setupLog.Info("Ignoring --default-httpboot-uki-url because --default-httpboot-oci-image is set") + defaultHTTPBootUKIURL = "" }
| func ConstructUKIURLFromOCI(ctx context.Context, image string, imageServerURL string, architecture string) (string, error) { | ||
| imageDetails := strings.Split(image, ":") | ||
| if len(imageDetails) != 2 { | ||
| return "", fmt.Errorf("invalid image format") | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the actual implementation
find . -name "oci.go" -path "*/uki/*" | head -5Repository: ironcore-dev/boot-operator
Length of output: 91
🏁 Script executed:
# Check the structure of the file and the specific function
git ls-files | grep -E "internal/uki"Repository: ironcore-dev/boot-operator
Length of output: 89
🏁 Script executed:
# Let's look at the actual code around lines 20-24
if [ -f internal/uki/oci.go ]; then
echo "=== File exists, reading content ==="
wc -l internal/uki/oci.go
echo ""
echo "=== Lines 1-50 ==="
head -50 internal/uki/oci.go
fiRepository: ironcore-dev/boot-operator
Length of output: 1773
🌐 Web query:
OCI image reference format specification
💡 Result:
In OCI, the registry API (OCI Distribution Spec) treats a “reference” as either a tag or a digest in routes like /v2/<name>/manifests/<reference>—not a full “image ref string”. The Distribution Spec also standardizes the allowed repository name and tag formats via regexes. [2]
Separately, the OCI Image Spec explicitly points to the Distribution (a.k.a. “distribution/distribution”) reference format for “image reference” strings (e.g., annotations like org.opencontainers.image.base.name). [3] In practice, most tooling follows the Distribution Project’s reference grammar:
Common OCI/Docker image reference string grammar (Distribution Project)
reference := name [ ":" tag ] [ "@" digest ]
name := [domain '/'] remote-name
domain := host [':' port-number]
tag := /[\w][\w.-]{0,127}/
digest := digest-algorithm ":" digest-hex
[1]
OCI Distribution Spec regex constraints (registry API)
- Repository
<name>must match:[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)* - Tag
<reference>(when it’s a tag) must be ≤ 128 chars and match:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}
[2]
Examples
registry.example.com/myorg/myimage:1.2.3registry.example.com/myorg/myimage@sha256:<hex>myorg/myimage:latest(domain omitted; defaults are tool-specific) [1][2]
🌐 Web query:
Go library for parsing OCI image references RFC standards
💡 Result:
For Go, the two most common (and well-tested) libraries for parsing OCI/Docker image references are:
-
github.com/distribution/reference— the “authoritative” reference parser used across the registry ecosystem. Its grammar explicitly aligns thehostportion with RFC 3986 (Appendix A) (IPv4/IPv6-in-brackets, domain, optional port), and it provides helpers likeParse,ParseNamed,ParseNormalizedNamed, andParseDockerRef. [1] -
github.com/google/go-containerregistry/pkg/name— higher-level structured parsing with options for strict vs weak validation and Docker Hub defaulting behavior; entry point isname.ParseReference(...). [2]
If your priority is “OCI image reference parsing + RFC-style host validation,” start with github.com/distribution/reference. [1]
Sources: [1] [2]
Image format validation is fragile for registry URLs with ports.
The current split-by-: approach will fail for image references that include a registry port, such as myregistry:5000/repo/image:v1.0. This would produce more than 2 segments and incorrectly reject the reference as invalid.
Consider using a proper OCI reference parser. Go provides two well-maintained options: github.com/distribution/reference (the authoritative implementation) or github.com/google/go-containerregistry/pkg/name. Alternatively, if a simple fix is preferred, use strings.LastIndex to separate the tag from the image name, though this approach won't handle the full OCI reference syntax including digest anchors (e.g., image@sha256:<hash>).
🐛 Proposed minimal fix using last colon for tag separation
func ConstructUKIURLFromOCI(ctx context.Context, image string, imageServerURL string, architecture string) (string, error) {
- imageDetails := strings.Split(image, ":")
- if len(imageDetails) != 2 {
+ lastColon := strings.LastIndex(image, ":")
+ if lastColon == -1 || lastColon == len(image)-1 {
return "", fmt.Errorf("invalid image format")
}
+ imageName := image[:lastColon]
+ imageVersion := image[lastColon+1:]
- ukiDigest, err := getUKIDigestFromNestedManifest(ctx, imageDetails[0], imageDetails[1], architecture)
+ ukiDigest, err := getUKIDigestFromNestedManifest(ctx, imageName, imageVersion, architecture)
if err != nil {
return "", fmt.Errorf("failed to fetch UKI layer digest: %w", err)
}
ukiDigest = strings.TrimPrefix(ukiDigest, "sha256:")
- ukiURL := fmt.Sprintf("%s/%s/sha256-%s.efi", imageServerURL, imageDetails[0], ukiDigest)
+ ukiURL := fmt.Sprintf("%s/%s/sha256-%s.efi", imageServerURL, imageName, ukiDigest)
return ukiURL, nil
}🤖 Prompt for AI Agents
In `@internal/uki/oci.go` around lines 20 - 24, The image parsing in
ConstructUKIURLFromOCI incorrectly splits on all ':' which breaks registry
host:port cases (e.g., myregistry:5000/repo/image:v1.0); update the function to
parse the OCI reference properly by using a robust parser like
github.com/distribution/reference or
github.com/google/go-containerregistry/pkg/name to extract the repository/name
and tag/digest, or at minimum use strings.LastIndex to separate the tag (and
handle image@digest with '@') instead of strings.Split, then build the UKI URL
from the parsed reference parts (retain imageServerURL and architecture logic).
| for _, manifest := range indexManifest.Manifests { | ||
| platform := manifest.Platform | ||
| if manifest.Platform != nil && platform.Architecture == architecture { | ||
| targetManifestDesc = manifest | ||
| break | ||
| } | ||
| } | ||
| if targetManifestDesc.Digest == "" { | ||
| return "", fmt.Errorf("failed to find target manifest with architecture %s", architecture) | ||
| } |
There was a problem hiding this comment.
Variable shadowing and potential logic error in architecture matching.
The loop variable manifest shadows the outer manifest variable declared at line 50. Additionally, if no manifest matches the requested architecture, targetManifestDesc retains its initial value from desc, and the digest check on line 64 may incorrectly pass since desc already has a valid digest from the initial resolution.
🐛 Proposed fix to avoid shadowing and properly detect missing architecture
+ var found bool
- for _, manifest := range indexManifest.Manifests {
- platform := manifest.Platform
- if manifest.Platform != nil && platform.Architecture == architecture {
- targetManifestDesc = manifest
+ for _, m := range indexManifest.Manifests {
+ if m.Platform != nil && m.Platform.Architecture == architecture {
+ targetManifestDesc = m
+ found = true
break
}
}
- if targetManifestDesc.Digest == "" {
+ if !found {
return "", fmt.Errorf("failed to find target manifest with architecture %s", architecture)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for _, manifest := range indexManifest.Manifests { | |
| platform := manifest.Platform | |
| if manifest.Platform != nil && platform.Architecture == architecture { | |
| targetManifestDesc = manifest | |
| break | |
| } | |
| } | |
| if targetManifestDesc.Digest == "" { | |
| return "", fmt.Errorf("failed to find target manifest with architecture %s", architecture) | |
| } | |
| var found bool | |
| for _, m := range indexManifest.Manifests { | |
| if m.Platform != nil && m.Platform.Architecture == architecture { | |
| targetManifestDesc = m | |
| found = true | |
| break | |
| } | |
| } | |
| if !found { | |
| return "", fmt.Errorf("failed to find target manifest with architecture %s", architecture) | |
| } |
🤖 Prompt for AI Agents
In `@internal/uki/oci.go` around lines 57 - 66, The loop is shadowing the outer
manifest variable and may leave targetManifestDesc set to the initial desc when
no architecture match is found; update the code in the block that iterates
indexManifest.Manifests so you do not shadow the outer manifest (rename the loop
variable, e.g., to m or entry), ensure targetManifestDesc is initialized to a
zero/empty descriptor (or use a found flag) before the loop, set
targetManifestDesc only when m.Platform != nil && m.Platform.Architecture ==
architecture, and after the loop check that targetManifestDesc is actually
populated (or the found flag is true) before using its Digest; reference:
indexManifest, targetManifestDesc, manifest (outer), architecture, desc.
| if len(httpBootConfigs.Items) == 0 { | ||
| log.Info("No HTTPBootConfig found for client IP, delivering default httpboot data", "clientIPs", clientIPs) | ||
| ukiURL := defaultUKIURL | ||
| if defaultOCIImage != "" { | ||
| if imageServerURL == "" { | ||
| log.Error(fmt.Errorf("image server URL is empty"), "Default OCI image provided but image server URL is not set") | ||
| http.Error(w, "Internal Server Error", http.StatusInternalServerError) | ||
| return | ||
| } | ||
| constructedURL, err := uki.ConstructUKIURLFromOCI(ctx, defaultOCIImage, imageServerURL, architecture) | ||
| if err != nil { | ||
| log.Error(err, "Failed to construct default UKI URL from OCI image", "image", defaultOCIImage) | ||
| http.Error(w, "Internal Server Error", http.StatusInternalServerError) | ||
| return | ||
| } | ||
| ukiURL = constructedURL | ||
| } | ||
| httpBootResponseData = map[string]string{ | ||
| "ClientIPs": strings.Join(clientIPs, ","), | ||
| "UKIURL": defaultUKIURL, | ||
| "UKIURL": ukiURL, | ||
| } |
There was a problem hiding this comment.
Potential silent failure when neither OCI image nor UKI URL is configured.
If both defaultOCIImage and defaultUKIURL are empty, ukiURL will be an empty string, and the response will contain "UKIURL": "". This may cause issues for clients expecting a valid URL. Consider logging a warning or returning an error.
🛡️ Proposed fix to warn when no default UKI configuration is available
if len(httpBootConfigs.Items) == 0 {
log.Info("No HTTPBootConfig found for client IP, delivering default httpboot data", "clientIPs", clientIPs)
ukiURL := defaultUKIURL
if defaultOCIImage != "" {
if imageServerURL == "" {
log.Error(fmt.Errorf("image server URL is empty"), "Default OCI image provided but image server URL is not set")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
constructedURL, err := uki.ConstructUKIURLFromOCI(ctx, defaultOCIImage, imageServerURL, architecture)
if err != nil {
log.Error(err, "Failed to construct default UKI URL from OCI image", "image", defaultOCIImage)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
ukiURL = constructedURL
}
+ if ukiURL == "" {
+ log.Info("Warning: No default UKI URL configured, response will have empty UKIURL")
+ }
httpBootResponseData = map[string]string{🤖 Prompt for AI Agents
In `@server/bootserver.go` around lines 409 - 429, The code may return an empty
UKI URL when both defaultOCIImage and defaultUKIURL are empty, causing clients
to receive "UKIURL": ""; update the logic where ukiURL is set (the block
handling defaultOCIImage/defaultUKIURL that populates httpBootResponseData) to
detect the case both defaultOCIImage == "" and defaultUKIURL == "" and either
log a warning/error via log.Warn/ log.Error and return an HTTP error
(http.Error) (or set a clear fallback value) instead of silently returning an
empty UKIURL; ensure the change references ukiURL, defaultOCIImage,
defaultUKIURL, httpBootResponseData and preserves the existing OCI construction
path using uki.ConstructUKIURLFromOCI.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Dockerfile (1)
2-2:⚠️ Potential issue | 🟡 MinorUsing a release candidate Go version in production builds is risky.
golang:1.26rc2is a release candidate, not a stable release. RC versions may have bugs or behavior changes before the final release. Consider using a stable Go version (e.g.,golang:1.24orgolang:1.25) for production Docker images.
🤖 Fix all issues with AI agents
In `@internal/uki/oci.go`:
- Around line 36-42: getUKIDigestFromNestedManifest currently constructs an
unauthenticated resolver (docker.NewResolver(docker.ResolverOptions{})), which
fails for private OCI registries; update this function to build ResolverOptions
with Hosts populated and an authorizer created via docker.NewDockerAuthorizer so
the resolver can authenticate. Specifically, modify
getUKIDigestFromNestedManifest to load credentials (either static from
environment/secret or from the Docker config file ~/.docker/config.json), create
an authorizer with docker.NewDockerAuthorizer(...) using those credentials, set
ResolverOptions.Hosts to include registry host info, then pass that options
struct into docker.NewResolver so Resolve() can access private registries.
Ensure the changes reference the getUKIDigestFromNestedManifest function and the
ResolverOptions/ docker.NewDockerAuthorizer usage.
🧹 Nitpick comments (2)
internal/uki/oci.go (1)
102-106: Use structured logging instead offmt.Printf.The error from
reader.Close()is logged usingfmt.Printf, which bypasses the structured logging used elsewhere in the codebase. Consider accepting a logger parameter or returning the error.♻️ Proposed fix
-func fetchContent(ctx context.Context, resolver remotes.Resolver, ref string, desc ocispec.Descriptor) ([]byte, error) { +func fetchContent(ctx context.Context, resolver remotes.Resolver, ref string, desc ocispec.Descriptor) (data []byte, err error) { fetcher, err := resolver.Fetcher(ctx, ref) if err != nil { return nil, fmt.Errorf("failed to get fetcher: %w", err) } reader, err := fetcher.Fetch(ctx, desc) if err != nil { return nil, fmt.Errorf("failed to fetch content: %w", err) } - defer func() { - if cerr := reader.Close(); cerr != nil { - fmt.Printf("failed to close reader: %v\n", cerr) - } - }() + defer func() { + if cerr := reader.Close(); cerr != nil && err == nil { + err = fmt.Errorf("failed to close reader: %w", cerr) + } + }() - data, err := io.ReadAll(reader) + data, err = io.ReadAll(reader)cmd/main.go (1)
128-133: Consider clearingdefaultHTTPBootUKIURLwhen both flags are provided.The log message states the UKI URL is being ignored, but both values are still passed to
RunBootServer. While the boot server handles precedence correctly, explicitly clearing the deprecated value here would make the behavior more obvious and prevent confusion.♻️ Proposed fix
if defaultHTTPBootOCIImage != "" && defaultHTTPBootUKIURL != "" { setupLog.Info("Ignoring --default-httpboot-uki-url because --default-httpboot-oci-image is set") + defaultHTTPBootUKIURL = "" }
| func getUKIDigestFromNestedManifest(ctx context.Context, imageName, imageVersion, architecture string) (string, error) { | ||
| resolver := docker.NewResolver(docker.ResolverOptions{}) | ||
| imageRef := fmt.Sprintf("%s:%s", imageName, imageVersion) | ||
| name, desc, err := resolver.Resolve(ctx, imageRef) | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to resolve image reference: %w", err) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
containerd docker resolver authentication options Go
💡 Result:
In github.com/containerd/containerd/remotes/docker, the “Docker resolver” auth knobs are primarily:
- Per-host auth (recommended): set
ResolverOptions.Hosts(aRegistryHostsfunc) that returns one or moreRegistryHostentries, each of which can carry anAuthorizer(RegistryHost.Authorizer). TheAuthorizeris typically created withdocker.NewDockerAuthorizer(...). [1] - Authorizer configuration options:
docker.NewDockerAuthorizer(opts...)supports:docker.WithAuthCreds(func(host string) (user, secret string, err error))docker.WithAuthHeader(http.Header)(extra headers)docker.WithAuthClient(*http.Client)(custom HTTP client)- (also available)
docker.WithFetchRefreshToken(...)[1]
- Deprecated (older) resolver-wide auth fields:
ResolverOptions.Authorizer/ResolverOptions.Credentials/ResolverOptions.Hostexist but are deprecated in favor ofHosts. [1]
Minimal pattern (static creds) looks like:
import "github.com/containerd/containerd/remotes/docker"
auth := docker.NewDockerAuthorizer(
docker.WithAuthCreds(func(host string) (string, string, error) {
return "USERNAME", "PASSWORD_OR_TOKEN", nil
}),
)
resolver := docker.NewResolver(docker.ResolverOptions{
Hosts: docker.ConfigureDefaultRegistries(
docker.WithAuthorizer(auth),
),
})If you want Docker-CLI-style auth resolution (reads $DOCKER_CONFIG/config.json, default ~/.docker/config.json), nerdctl provides a ready-made helper resolver: github.com/containerd/nerdctl/v2/pkg/imgutil/dockerconfigresolver.New(...), plus an AuthCreds helper compatible with docker.WithAuthCreds. [2]
Sources: containerd remotes/docker API docs (resolver + authorizer + options) [1]; nerdctl dockerconfigresolver helper (Docker config-based creds/resolver) [2].
🏁 Script executed:
# Search for the function and surrounding context in the file
find . -name "oci.go" -path "*/uki/*" | head -5Repository: ironcore-dev/boot-operator
Length of output: 91
🏁 Script executed:
# Read the oci.go file to understand the full context
cat -n ./internal/uki/oci.goRepository: ironcore-dev/boot-operator
Length of output: 4490
🏁 Script executed:
# Check how getUKIDigestFromNestedManifest is called and if there's any auth setup elsewhere
rg "getUKIDigestFromNestedManifest|ConstructUKIURLFromOCI" -A 3 -B 3
# Check if there's any auth configuration in the codebase
rg "NewDockerAuthorizer|Authorizer|WithAuthCreds" --type goRepository: ironcore-dev/boot-operator
Length of output: 2619
🏁 Script executed:
# Check if there's a default OCI image configured and how it's used
rg "defaultOCIImage|ConstructUKIURLFromOCI" -B 5 -A 5
# Look for any configuration files that might specify registry settings
fd "config|config\.yaml|config\.yml|.env" --type f | head -20
# Check the main.go or initialization to understand how the service is configured
fd "main\.go" --type fRepository: ironcore-dev/boot-operator
Length of output: 5872
🏁 Script executed:
# Check the main.go to understand how RunBootServer is called
cat -n cmd/main.go | head -150
# Also check bootserver.go to see the RunBootServer function signature and how it initializes the resolver
cat -n server/bootserver.go | head -100Repository: ironcore-dev/boot-operator
Length of output: 11261
🏁 Script executed:
# Check if there are any tests that might clarify the intended usage
rg "getUKIDigestFromNestedManifest|ConstructUKIURLFromOCI" --type go -l
# Look for any TODO or FIXME comments related to authentication
rg "TODO|FIXME|todo|fixme" internal/uki/oci.goRepository: ironcore-dev/boot-operator
Length of output: 173
Add authentication support for private OCI registries.
The docker.NewResolver(docker.ResolverOptions{}) call creates a resolver without authentication credentials, which will fail when pulling UKI images from private registries. To support private registries, configure the resolver with authentication via ResolverOptions.Hosts and docker.NewDockerAuthorizer(...), either with static credentials or by reading from Docker config (~/.docker/config.json).
🤖 Prompt for AI Agents
In `@internal/uki/oci.go` around lines 36 - 42, getUKIDigestFromNestedManifest
currently constructs an unauthenticated resolver
(docker.NewResolver(docker.ResolverOptions{})), which fails for private OCI
registries; update this function to build ResolverOptions with Hosts populated
and an authorizer created via docker.NewDockerAuthorizer so the resolver can
authenticate. Specifically, modify getUKIDigestFromNestedManifest to load
credentials (either static from environment/secret or from the Docker config
file ~/.docker/config.json), create an authorizer with
docker.NewDockerAuthorizer(...) using those credentials, set
ResolverOptions.Hosts to include registry host info, then pass that options
struct into docker.NewResolver so Resolve() can access private registries.
Ensure the changes reference the getUKIDigestFromNestedManifest function and the
ResolverOptions/ docker.NewDockerAuthorizer usage.
Proposed Changes
Fixes #
Summary by CodeRabbit
New Features
Deprecations
Bug Fixes / Reliability