Skip to content

METAL-1720: Add kernel file support for multi-architecture PXE boot#170

Merged
openshift-merge-bot[bot] merged 2 commits intoopenshift:mainfrom
honza:multi-arch
Mar 27, 2026
Merged

METAL-1720: Add kernel file support for multi-architecture PXE boot#170
openshift-merge-bot[bot] merged 2 commits intoopenshift:mainfrom
honza:multi-arch

Conversation

@honza
Copy link
Copy Markdown
Member

@honza honza commented Mar 13, 2026

This is the image-customization-controller counterpart to the BMO commit "Add multi-architecture PXE boot support" (a7fd6912f). BMO now selects a per-architecture kernel URL from GeneratedImage.KernelURL when available, falling back to the static DEPLOY_KERNEL_URL only when the image provider doesn't supply one. This commit makes the image-customization-controller populate that field.

Previously, the controller never set KernelURL in GeneratedImage. For PXE/InitRD boot, Ironic would fall back to BMO's single static DEPLOY_KERNEL_URL environment variable -- a single URL for all architectures. This breaks multi-arch PXE boot because different architectures need different kernel binaries.

Changes:

  • Add optional DEPLOY_KERNEL environment variable to EnvInputs. Not required since kernel files are only needed for InitRD/PXE boot.

  • Add baseKernel type (basefile.go) that serves kernel files unmodified via os.Open -- no ignition injection is needed for kernel binaries.

  • Extend osImage from a boolean iso flag to a kind enum (ISO, initramfs, kernel) to cleanly distinguish all three image types.

  • Add kernel file discovery to imagehandler using the same _ naming convention as ISO and initramfs files (e.g. ipa_aarch64.kernel).

  • Add ServeKernel(arch) method to ImageHandler interface. Returns a serving URL for the kernel file, or empty string if no kernel is available for the architecture.

  • Update BuildImage in rhcos.go to call ServeKernel for InitRD format requests and populate GeneratedImage.KernelURL.

  • Add tests for kernel pattern matching, ServeKernel URL generation, idempotency, architecture fallback, and verification that kernel presence doesn't affect HasImagesForArchitecture.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Mar 13, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Mar 13, 2026

@honza: This pull request references METAL-1720 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This is the image-customization-controller counterpart to the BMO commit "Add multi-architecture PXE boot support" (a7fd6912f). BMO now selects a per-architecture kernel URL from GeneratedImage.KernelURL when available, falling back to the static DEPLOY_KERNEL_URL only when the image provider doesn't supply one. This commit makes the image-customization-controller populate that field.

Previously, the controller never set KernelURL in GeneratedImage. For PXE/InitRD boot, Ironic would fall back to BMO's single static DEPLOY_KERNEL_URL environment variable -- a single URL for all architectures. This breaks multi-arch PXE boot because different architectures need different kernel binaries.

Changes:

  • Add optional DEPLOY_KERNEL environment variable to EnvInputs. Not required since kernel files are only needed for InitRD/PXE boot.

  • Add baseKernel type (basefile.go) that serves kernel files unmodified via os.Open -- no ignition injection is needed for kernel binaries.

  • Extend osImage from a boolean iso flag to a kind enum (ISO, initramfs, kernel) to cleanly distinguish all three image types.

  • Add kernel file discovery to imagehandler using the same _ naming convention as ISO and initramfs files (e.g. ipa_aarch64.kernel).

  • Add ServeKernel(arch) method to ImageHandler interface. Returns a serving URL for the kernel file, or empty string if no kernel is available for the architecture.

  • Update BuildImage in rhcos.go to call ServeKernel for InitRD format requests and populate GeneratedImage.KernelURL.

  • Add tests for kernel pattern matching, ServeKernel URL generation, idempotency, architecture fallback, and verification that kernel presence doesn't affect HasImagesForArchitecture.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

Walkthrough

Adds kernel image support: new EnvInputs (DeployKernel, IronicRootfsURL), an imageKind enum (ISO, Initramfs, Kernel), kernel discovery/storage/serving via ServeKernel, kernel-aware filesystem types, tests, and rhcos provider changes to populate KernelURL and optional arch-specific rootfs params.

Changes

Cohort / File(s) Summary
Environment Configuration
pkg/env/env.go
Added DeployKernel and IronicRootfsURL fields to EnvInputs with env tags DEPLOY_KERNEL and IRONIC_ROOTFS_URL.
Image handler core
pkg/imagehandler/imagehandler.go
Introduced imageKind (ISO,Initramfs,Kernel); replaced iso boolean with kind on osImage; added ServeKernel(arch string) (string, error) to the ImageHandler interface, kernel storage (kernelFiles), getKernel, and adjusted discovery/initialization to populate kernel entries and switch on imageKind.
Filesystem & kernel base types
pkg/imagehandler/basefile.go, pkg/imagehandler/imagefile.go, pkg/imagehandler/imagefilesystem.go
Added non-exported baseKernel type and newBaseKernel plus no-op InsertIgnition; added compile-time assertion var _ overlay.OverlayReader = (*os.File)(nil); added kernel bool to imageFile and updated imageFileSystem.Open to select kernel vs base image.
Image provider (rhcos)
pkg/imageprovider/rhcos.go
BuildImage now calls ServeKernel for InitRD images, sets generated.KernelURL, returns early on errors, and when IronicRootfsURL is set for non-host arch computes an arch-specific rootfs URL via new archSpecificURL and sets ExtraKernelParams. Also tightened error propagation after ServeImage.
Tests — image handler
pkg/imagehandler/imagehandler_test.go
Reworked tests to use imageKind expectations; added TestImagePatternWithKernel, TestHasImagesForArchitectureWithKernel, TestServeKernel, and TestServeKernelNoKernelConfigured; adjusted assertions and imports accordingly.
Tests — static server stub
cmd/static-server/main_test.go
Added ServeKernel(arch string) (string, error) stub implementation on fakeImageFileSystem to satisfy updated interface expected by tests.

Sequence Diagram

sequenceDiagram
    participant Client
    participant ImageProvider
    participant ImageHandler
    participant ImageFileSystem

    Client->>ImageProvider: BuildImage(format=InitRD, arch)
    ImageProvider->>ImageHandler: Discover images for arch
    ImageHandler->>ImageFileSystem: Open image (kind decision)
    ImageFileSystem->>ImageFileSystem: Select base or kernel file
    ImageFileSystem-->>ImageHandler: Return image stream / kernel entry
    ImageProvider->>ImageHandler: ServeKernel(arch)
    ImageHandler->>ImageFileSystem: Lookup kernelFiles[arch]
    ImageFileSystem-->>ImageHandler: Kernel path / URL
    ImageHandler-->>ImageProvider: KernelURL
    ImageProvider-->>Client: BuildImage response (KernelURL, ExtraKernelParams)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 6 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning Test suite contains critical flaw: TestHasImagesForArchitectureWithKernel hard-codes aarch64 as unsupported architecture, failing on arm64 hosts. TestServeKernel violates single responsibility by testing multiple unrelated behaviors in one test function. Apply proposed review fix to dynamically select non-host architecture for kernel test. Split TestServeKernel into separate test functions for each behavior (specific arch, host fallback, unsupported arch, idempotency).
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding kernel file support for multi-architecture PXE boot. It directly aligns with the PR's core objective of enabling per-architecture kernel selection.
Stable And Deterministic Test Names ✅ Passed Codebase uses standard Go testing package with static, descriptive test names rather than Ginkgo, ensuring all tests have stable and deterministic identifiers.
Microshift Test Compatibility ✅ Passed The pull request does not add any Ginkgo e2e tests; it only contains standard Go unit tests using the testing package, so the check is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The PR adds new unit tests using standard Go testing framework, not Ginkgo-style e2e tests, so SNO compatibility check is not applicable.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed This pull request adds standard Go unit tests to pkg/imagehandler/imagehandler_test.go, not Ginkgo e2e tests. The custom check for IPv6 and disconnected network compatibility applies only to Ginkgo e2e tests, which are not present in this change.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci bot requested review from elfosardo and zaneb March 13, 2026 14:26
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 13, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: honza

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 13, 2026
@honza
Copy link
Copy Markdown
Member Author

honza commented Mar 13, 2026

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 13, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Mar 13, 2026

@honza: This pull request references METAL-1720 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This is the image-customization-controller counterpart to the BMO commit "Add multi-architecture PXE boot support" (a7fd6912f). BMO now selects a per-architecture kernel URL from GeneratedImage.KernelURL when available, falling back to the static DEPLOY_KERNEL_URL only when the image provider doesn't supply one. This commit makes the image-customization-controller populate that field.

Previously, the controller never set KernelURL in GeneratedImage. For PXE/InitRD boot, Ironic would fall back to BMO's single static DEPLOY_KERNEL_URL environment variable -- a single URL for all architectures. This breaks multi-arch PXE boot because different architectures need different kernel binaries.

Changes:

  • Add optional DEPLOY_KERNEL environment variable to EnvInputs. Not required since kernel files are only needed for InitRD/PXE boot.

  • Add baseKernel type (basefile.go) that serves kernel files unmodified via os.Open -- no ignition injection is needed for kernel binaries.

  • Extend osImage from a boolean iso flag to a kind enum (ISO, initramfs, kernel) to cleanly distinguish all three image types.

  • Add kernel file discovery to imagehandler using the same _ naming convention as ISO and initramfs files (e.g. ipa_aarch64.kernel).

  • Add ServeKernel(arch) method to ImageHandler interface. Returns a serving URL for the kernel file, or empty string if no kernel is available for the architecture.

  • Update BuildImage in rhcos.go to call ServeKernel for InitRD format requests and populate GeneratedImage.KernelURL.

  • Add tests for kernel pattern matching, ServeKernel URL generation, idempotency, architecture fallback, and verification that kernel presence doesn't affect HasImagesForArchitecture.

Summary by CodeRabbit

  • New Features

  • Added kernel image serving support with architecture-specific handling

  • Extended image handler to classify and manage kernel images alongside ISO and initramfs formats

  • Introduced new DeployKernel configuration option for specifying kernel image location

  • Tests

  • Added comprehensive test coverage for kernel image discovery and serving scenarios

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/imagehandler/imagefilesystem.go (1)

60-70: ⚠️ Potential issue | 🟠 Major

Potential nil pointer dereference when base image is not found.

Both getKernel and getBaseImage can return nil if no matching image is found. Calling im.Init(baseImage) with a nil baseImage will cause a panic when InsertIgnition is invoked on the nil receiver.

🐛 Proposed fix to add nil check
 	var baseImage baseFile
 	if im.kernel {
 		baseImage = f.getKernel(im.arch)
 	} else {
 		baseImage = f.getBaseImage(im.arch, im.initramfs)
 	}
+	if baseImage == nil {
+		return nil, fs.ErrNotExist
+	}

 	if err := im.Init(baseImage); err != nil {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/imagehandler/imagefilesystem.go` around lines 60 - 70, The code calls
im.Init(baseImage) without verifying baseImage is non-nil, risking a panic
because f.getKernel or f.getBaseImage can return nil; update the logic around
f.getKernel(f.getBaseImage) to check if baseImage == nil before calling im.Init,
log an explanatory error via f.log.Error (include arch/initramfs/context), and
return nil, error (or a new descriptive error) instead of proceeding to
im.Init/InsertIgnition when no base image was found.
🧹 Nitpick comments (1)
pkg/imagehandler/imagehandler_test.go (1)

301-304: Consider using a descriptive string for imageKind in test errors.

The error message uses %d for the kind field, which outputs the integer value. While functional, it's less readable than a descriptive name.

♻️ Optional: Add String() method for imageKind

In imagehandler.go, you could add a String() method:

func (k imageKind) String() string {
	switch k {
	case imageKindISO:
		return "ISO"
	case imageKindInitramfs:
		return "Initramfs"
	case imageKindKernel:
		return "Kernel"
	default:
		return fmt.Sprintf("Unknown(%d)", k)
	}
}

Then update test assertions to use %s or %v:

-		t.Errorf("kind: expected %d but got %d", tc.kind, ii.kind)
+		t.Errorf("kind: expected %v but got %v", tc.kind, ii.kind)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/imagehandler/imagehandler_test.go` around lines 301 - 304, The test error
prints the numeric imageKind value (ii.kind / tc.kind) which is hard to read;
add a String() method on the imageKind type in imagehandler.go (handling
imageKindISO, imageKindInitramfs, imageKindKernel and a default) and then update
the test in imagehandler_test.go to format the values as strings (use %s or %v
with the String method) in the t.Errorf call so failures show descriptive names
instead of integers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@pkg/imagehandler/imagefilesystem.go`:
- Around line 60-70: The code calls im.Init(baseImage) without verifying
baseImage is non-nil, risking a panic because f.getKernel or f.getBaseImage can
return nil; update the logic around f.getKernel(f.getBaseImage) to check if
baseImage == nil before calling im.Init, log an explanatory error via
f.log.Error (include arch/initramfs/context), and return nil, error (or a new
descriptive error) instead of proceeding to im.Init/InsertIgnition when no base
image was found.

---

Nitpick comments:
In `@pkg/imagehandler/imagehandler_test.go`:
- Around line 301-304: The test error prints the numeric imageKind value
(ii.kind / tc.kind) which is hard to read; add a String() method on the
imageKind type in imagehandler.go (handling imageKindISO, imageKindInitramfs,
imageKindKernel and a default) and then update the test in imagehandler_test.go
to format the values as strings (use %s or %v with the String method) in the
t.Errorf call so failures show descriptive names instead of integers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47666b99-0d68-49e9-9f09-0ba23986daf9

📥 Commits

Reviewing files that changed from the base of the PR and between eacc65f and c9944a1.

📒 Files selected for processing (8)
  • cmd/static-server/main_test.go
  • pkg/env/env.go
  • pkg/imagehandler/basefile.go
  • pkg/imagehandler/imagefile.go
  • pkg/imagehandler/imagefilesystem.go
  • pkg/imagehandler/imagehandler.go
  • pkg/imagehandler/imagehandler_test.go
  • pkg/imageprovider/rhcos.go

Enable PXE booting non-host architectures (e.g. aarch64 workers on an
x86_64 cluster) by resolving two issues:

1. Add IRONIC_ROOTFS_URL to EnvInputs so the rootfs base URL is
   available to the image provider.

2. In BuildImage(), when building an initrd-format image for a non-host
   architecture, set ExtraKernelParams with an arch-specific rootfs URL.
   This overrides Ironic's global kernel_append_params which hardcodes
   the host architecture rootfs. BMO appends ExtraKernelParams after
   %default%, and since the last coreos.live.rootfs_url= on the kernel
   command line wins, the correct arch-specific rootfs is downloaded.

Also resolve the Dockerfile merge conflict for the 4.22 rebase, adding
the cli stage needed for the oc binary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Mar 13, 2026

@honza: This pull request references METAL-1720 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This is the image-customization-controller counterpart to the BMO commit "Add multi-architecture PXE boot support" (a7fd6912f). BMO now selects a per-architecture kernel URL from GeneratedImage.KernelURL when available, falling back to the static DEPLOY_KERNEL_URL only when the image provider doesn't supply one. This commit makes the image-customization-controller populate that field.

Previously, the controller never set KernelURL in GeneratedImage. For PXE/InitRD boot, Ironic would fall back to BMO's single static DEPLOY_KERNEL_URL environment variable -- a single URL for all architectures. This breaks multi-arch PXE boot because different architectures need different kernel binaries.

Changes:

  • Add optional DEPLOY_KERNEL environment variable to EnvInputs. Not required since kernel files are only needed for InitRD/PXE boot.

  • Add baseKernel type (basefile.go) that serves kernel files unmodified via os.Open -- no ignition injection is needed for kernel binaries.

  • Extend osImage from a boolean iso flag to a kind enum (ISO, initramfs, kernel) to cleanly distinguish all three image types.

  • Add kernel file discovery to imagehandler using the same _ naming convention as ISO and initramfs files (e.g. ipa_aarch64.kernel).

  • Add ServeKernel(arch) method to ImageHandler interface. Returns a serving URL for the kernel file, or empty string if no kernel is available for the architecture.

  • Update BuildImage in rhcos.go to call ServeKernel for InitRD format requests and populate GeneratedImage.KernelURL.

  • Add tests for kernel pattern matching, ServeKernel URL generation, idempotency, architecture fallback, and verification that kernel presence doesn't affect HasImagesForArchitecture.

Summary by CodeRabbit

  • New Features

  • Kernel image serving with architecture-aware handling and kernel-specific discovery

  • Image handler now classifies/manages kernels alongside ISO and initramfs

  • New DeployKernel and IronicRootfsURL environment options; arch-specific rootfs URLs and kernel URL injection

  • Tests

  • Expanded tests for kernel discovery, serving, fallbacks, and no-kernel scenarios

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/imagehandler/imagehandler_test.go`:
- Around line 686-706: The test hard-codes "aarch64" as the kernel-only
architecture which breaks on machines where env.HostArchitecture() == "aarch64";
instead pick an arch different from the host before creating the kernel-only
file and asserting HasImagesForArchitecture returns false (e.g., determine
kernelOnlyArch := if env.HostArchitecture() == "aarch64" then "x86_64" else
"aarch64"), create ipa_<kernelOnlyArch>.kernel, construct the ImageHandler via
NewImageHandler and call handler.HasImagesForArchitecture(kernelOnlyArch) to
assert false; update references to the hard-coded "aarch64" in this test to use
the computed kernelOnlyArch so the test behaves correctly regardless of host
architecture.

In `@pkg/imageprovider/rhcos.go`:
- Around line 115-128: For InitRD images (when data.Format ==
metal3.ImageFormatInitRD) treat an empty kernel URL from
ip.ImageHandler.ServeKernel(data.Architecture) as an error for non-host
architectures: call ServeKernel, if err != nil return it; if kernelURL == "" and
data.Architecture != env.HostArchitecture() return a descriptive error (do not
set generated.KernelURL); otherwise set generated.KernelURL = kernelURL and
continue with the existing arch-specific rootfs override logic. Ensure you
reference ServeKernel, ip.ImageHandler, data.Architecture,
env.HostArchitecture(), data.Format, metal3.ImageFormatInitRD, and
generated.KernelURL when implementing the check.
- Around line 134-140: The archSpecificURL function currently manipulates the
full URL string with filepath.Ext which can misplace the _arch suffix when
IRONIC_ROOTFS_URL contains queries/fragments; instead parse the URL with
net/url.Parse, operate only on u.Path using path.Ext and strings.TrimSuffix to
insert "_<arch>" before the path extension, assign the modified path back to
u.Path, and return u.String() so query and fragment components are preserved;
update references inside archSpecificURL to use url.Parse, u.Path, path.Ext, and
u.String().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f73f255-8e05-4fba-9524-87bec248cf38

📥 Commits

Reviewing files that changed from the base of the PR and between c9944a1 and a32f2f3.

📒 Files selected for processing (8)
  • cmd/static-server/main_test.go
  • pkg/env/env.go
  • pkg/imagehandler/basefile.go
  • pkg/imagehandler/imagefile.go
  • pkg/imagehandler/imagefilesystem.go
  • pkg/imagehandler/imagehandler.go
  • pkg/imagehandler/imagehandler_test.go
  • pkg/imageprovider/rhcos.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/imagehandler/imagefilesystem.go
  • pkg/env/env.go

@honza
Copy link
Copy Markdown
Member Author

honza commented Mar 20, 2026

/unhold

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Mar 20, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Mar 20, 2026

@honza: This pull request references METAL-1720 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This is the image-customization-controller counterpart to the BMO commit "Add multi-architecture PXE boot support" (a7fd6912f). BMO now selects a per-architecture kernel URL from GeneratedImage.KernelURL when available, falling back to the static DEPLOY_KERNEL_URL only when the image provider doesn't supply one. This commit makes the image-customization-controller populate that field.

Previously, the controller never set KernelURL in GeneratedImage. For PXE/InitRD boot, Ironic would fall back to BMO's single static DEPLOY_KERNEL_URL environment variable -- a single URL for all architectures. This breaks multi-arch PXE boot because different architectures need different kernel binaries.

Changes:

  • Add optional DEPLOY_KERNEL environment variable to EnvInputs. Not required since kernel files are only needed for InitRD/PXE boot.

  • Add baseKernel type (basefile.go) that serves kernel files unmodified via os.Open -- no ignition injection is needed for kernel binaries.

  • Extend osImage from a boolean iso flag to a kind enum (ISO, initramfs, kernel) to cleanly distinguish all three image types.

  • Add kernel file discovery to imagehandler using the same _ naming convention as ISO and initramfs files (e.g. ipa_aarch64.kernel).

  • Add ServeKernel(arch) method to ImageHandler interface. Returns a serving URL for the kernel file, or empty string if no kernel is available for the architecture.

  • Update BuildImage in rhcos.go to call ServeKernel for InitRD format requests and populate GeneratedImage.KernelURL.

  • Add tests for kernel pattern matching, ServeKernel URL generation, idempotency, architecture fallback, and verification that kernel presence doesn't affect HasImagesForArchitecture.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/imagehandler/imagehandler.go`:
- Around line 147-157: The code currently adds only the literal DeployKernel
path to filePaths when ImageSharedDir is set, which prevents finding
arch-suffixed kernels in the kernel's directory; update the block that handles
deployPaths so that for each p you: 1) if filepath.Dir(p) !=
envInputs.ImageSharedDir, append filepath.Dir(p) to searchDirs so that the
directory is scanned for arch-specific variants; and 2) only append the literal
p to filePaths if the file actually exists (use os.Stat or equivalent) to avoid
registering a missing base path as a host kernel; keep existing behavior for
paths inside envInputs.ImageSharedDir.
- Around line 301-326: ServeKernel is inserting "kernel-<arch>" entries into the
same f.images/f.keys maps used by ServeImage which allows collisions with
user-provided keys; fix by giving kernel artifacts their own namespace: add new
maps (e.g. f.kernelImages map[string]*imageFile and f.kernelKeys
map[string]string) and change the ServeKernel code that currently references
f.images and f.keys to use f.kernelImages and f.kernelKeys instead (preserve the
imageFile struct and kernel flag), and update any lookup/resolve logic that
serves kernel URLs to consult the kernel maps so user images keyed like
"kernel-aarch64" no longer collide.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b020add0-8f46-4bad-89dd-3b9ba0143ed8

📥 Commits

Reviewing files that changed from the base of the PR and between a32f2f3 and f47081d.

📒 Files selected for processing (3)
  • pkg/imagehandler/imagehandler.go
  • pkg/imagehandler/imagehandler_test.go
  • pkg/imageprovider/rhcos.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/imagehandler/imagehandler_test.go
  • pkg/imageprovider/rhcos.go

Comment on lines +147 to +157
deployPaths := []string{envInputs.DeployISO, envInputs.DeployInitrd}
if envInputs.DeployKernel != "" {
deployPaths = append(deployPaths, envInputs.DeployKernel)
}

if envInputs.ImageSharedDir != "" {
searchDirs = append(searchDirs, envInputs.ImageSharedDir)

if filepath.Dir(envInputs.DeployISO) != envInputs.ImageSharedDir {
filePaths = append(filePaths, envInputs.DeployISO)
}
if filepath.Dir(envInputs.DeployInitrd) != envInputs.ImageSharedDir {
filePaths = append(filePaths, envInputs.DeployInitrd)
for _, p := range deployPaths {
if filepath.Dir(p) != envInputs.ImageSharedDir {
filePaths = append(filePaths, p)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Arch-suffixed kernels outside the shared dir still won't be discovered.

If DEPLOY_KERNEL points outside ImageSharedDir, this branch only appends the literal base path. A setup like /var/lib/ipa.kernel plus /var/lib/ipa_aarch64.kernel never scans /var/lib, so the real arch-specific kernel is missed and the missing base path is registered as a host kernel instead. Please scan the external directory here, and only add the direct path when it actually exists.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/imagehandler/imagehandler.go` around lines 147 - 157, The code currently
adds only the literal DeployKernel path to filePaths when ImageSharedDir is set,
which prevents finding arch-suffixed kernels in the kernel's directory; update
the block that handles deployPaths so that for each p you: 1) if filepath.Dir(p)
!= envInputs.ImageSharedDir, append filepath.Dir(p) to searchDirs so that the
directory is scanned for arch-specific variants; and 2) only append the literal
p to filePaths if the file actually exists (use os.Stat or equivalent) to avoid
registering a missing base path as a host kernel; keep existing behavior for
paths inside envInputs.ImageSharedDir.

Comment on lines +301 to +326
key := fmt.Sprintf("kernel-%s", arch)

f.mu.Lock()
defer f.mu.Unlock()

if img, exists := f.images[key]; exists {
p, err := url.Parse(fmt.Sprintf("/%s", img.name))
if err != nil {
return "", err
}
return f.baseURL.ResolveReference(p).String(), nil
}

name := key
p, err := url.Parse(fmt.Sprintf("/%s", name))
if err != nil {
return "", err
}

f.keys[name] = key
f.images[key] = &imageFile{
name: name,
arch: arch,
size: size,
kernel: true,
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ServeKernel can collide with normal image keys.

These kernel-<arch> entries go into the same f.images/f.keys maps that ServeImage uses for caller-supplied keys. If a normal image is ever keyed as kernel-aarch64 (or similar), one entry aliases the other and the wrong artifact gets served. Kernel records need their own map or a truly separate namespace.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/imagehandler/imagehandler.go` around lines 301 - 326, ServeKernel is
inserting "kernel-<arch>" entries into the same f.images/f.keys maps used by
ServeImage which allows collisions with user-provided keys; fix by giving kernel
artifacts their own namespace: add new maps (e.g. f.kernelImages
map[string]*imageFile and f.kernelKeys map[string]string) and change the
ServeKernel code that currently references f.images and f.keys to use
f.kernelImages and f.kernelKeys instead (preserve the imageFile struct and
kernel flag), and update any lookup/resolve logic that serves kernel URLs to
consult the kernel maps so user images keyed like "kernel-aarch64" no longer
collide.

@hroyrh
Copy link
Copy Markdown

hroyrh commented Mar 21, 2026

/lgtm

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Mar 21, 2026
@honza
Copy link
Copy Markdown
Member Author

honza commented Mar 26, 2026

/test e2e-metal-ipi-ovn-ipv6

@sgoveas
Copy link
Copy Markdown

sgoveas commented Mar 26, 2026

/verified later @sgoveas

@openshift-ci-robot openshift-ci-robot added verified-later verified Signifies that the PR passed pre-merge verification criteria labels Mar 26, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@sgoveas: This PR has been marked to be verified later by @sgoveas.

Details

In response to this:

/verified later @sgoveas

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@honza
Copy link
Copy Markdown
Member Author

honza commented Mar 27, 2026

/retest-required

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 27, 2026

@honza: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@openshift-merge-bot openshift-merge-bot bot merged commit a43d9c9 into openshift:main Mar 27, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria verified-later

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants