Skip to content

METAL-1730: Add stream symlinks for multi-stream support#83

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
honza:dual-stream
May 19, 2026
Merged

METAL-1730: Add stream symlinks for multi-stream support#83
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
honza:dual-stream

Conversation

@honza
Copy link
Copy Markdown
Member

@honza honza commented Apr 14, 2026

Create ironic-python-agent-{stream} symlinks (e.g., ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.iso) for all available CoreOS versions. These allow the image-customization-controller to discover and serve different CoreOS base images based on the coreos.openshift.io/stream label.

How it works

The coreos.openshift.io/stream label on a BareMetalHost determines
which kernel, initrd, and rootfs files are used during PXE boot. For
example, a BMH labeled coreos.openshift.io/stream: rhel-10 will PXE
boot with ironic-python-agent-rhel-10.kernel,
ironic-python-agent-rhel-10.initramfs, and
ironic-python-agent-rhel-10.rootfs instead of the unqualified defaults.

The per-node coreos.live.rootfs_url kernel parameter set via ICC's
ExtraKernelParams overrides the global one from ironic-image because
dracut's getarg returns the last value for duplicate parameters.

Changes across repos

installer (openshift/installer#10502)
pkg/asset/machines/baremetal/: Sets the coreos.openshift.io/stream
label on all generated BareMetalHost objects (control-plane, worker,
arbiter) based on the install-config's OSImageStream setting, defaulting
to rhel-9. Configures hostSelector.matchLabels on
BareMetalMachineProviderSpec so MachineSets only claim BMHs with the
matching stream label.

cluster-baremetal-operator (openshift/cluster-baremetal-operator#590)
provisioning/image_customization.go: Passes three new environment
variables to the ICC container: DEPLOY_KERNEL (path to the kernel file),
IMAGE_SHARED_DIR (path to the shared images directory where
stream-prefixed files are discovered), and IRONIC_ROOTFS_URL (base URL
for the rootfs served by httpd). These enable ICC to discover multi-stream
images and construct per-node kernel/rootfs URLs.

machine-os-images (openshift/machine-os-images#83)
scripts/copy-metal: Creates stream-prefixed IPA symlinks (e.g.
ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.kernel)
for all available CoreOS versions, allowing ICC to discover images by
stream. Also fixes missing initramfs symlinks in the fixed=true (PXE)
block — without these, a stream-specific kernel would be paired with the
default-stream initrd, causing a version mismatch that crashes immediately
on PXE boot.

image-customization-controller (openshift/image-customization-controller#175)
pkg/imagehandler/, pkg/imageprovider/rhcos.go: Adds OS stream
selection so ICC can serve different CoreOS base images based on the
coreos.openshift.io/stream label. Images are indexed by (stream,
architecture) and discovered from stream-prefixed filenames. Key changes:

  • streamArchSpecificURL() transforms the base rootfs URL into a stream-
    and arch-specific URL
  • BuildImage() passes the stream to ServeKernel() and uses the
    stream-specific rootfs URL in ExtraKernelParams
  • imageKey() includes the stream in the cache key so that changing a
    BMH's stream label invalidates the cached image

Summary by CodeRabbit

  • Chores
    • Enhanced build process to properly support multiple OS stream versions, ensuring artifacts are correctly available for RHEL 9 and RHEL 10 installations.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 55205e15-dd59-4623-978e-80fd758c3c7f

📥 Commits

Reviewing files that changed from the base of the PR and between 94c5b48 and f409f39.

📒 Files selected for processing (1)
  • scripts/copy-metal
✅ Files skipped from review due to trivial changes (1)
  • scripts/copy-metal

Walkthrough

The scripts/copy-metal script adds a post-processing loop over CoreOS image prefixes (coreos, coreos10) that detects stream definitions, maps them to stream labels, and generates stream-prefixed symlinks for ironic-python-agent artifacts and mode-specific boot files (kernel, initramfs, rootfs).

Changes

Stream-Prefixed Symlink Generation

Layer / File(s) Summary
Detection & Mapping
scripts/copy-metal (lines 125–135)
Iterates over coreos and coreos10 prefixes, skips those lacking /coreos/<prefix>-stream.json, and derives stream label (rhel-9 or rhel-10).
Custom Mode Symlinks
scripts/copy-metal (lines 136–151)
Creates stream-prefixed .iso and .initramfs symlinks; conditionally adds aarch64-suffixed variants when arch is x86_64 and files exist.
Fixed/PXE Mode Symlinks
scripts/copy-metal (lines 152–171)
Creates stream-prefixed .kernel, .initramfs, and .rootfs symlinks; conditionally adds aarch64-suffixed variants for x86_64 builds with present files.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 12
✅ Passed checks (12 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'METAL-1730: Add stream symlinks for multi-stream support' directly and specifically describes the main change: adding stream-prefixed symlinks to enable multi-stream CoreOS image support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed The custom check for Ginkgo test name stability is not applicable. This PR modifies shell scripts, documentation, and build configuration files only. No Ginkgo test files exist in this repository.
Test Structure And Quality ✅ Passed Custom check not applicable. PR modifies only shell scripts (scripts/copy-metal); repository contains no Go code or Ginkgo tests. Ginkgo test quality check cannot be applied to shell script changes.
Microshift Test Compatibility ✅ Passed This PR modifies only shell scripts (scripts/copy-metal) and contains no Ginkgo e2e tests or Go code. The MicroShift test compatibility check is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests were added in this PR. Changes are shell script modifications only (scripts/copy-metal, copy-iso, copy-pxe, and fetch_image.sh). The SNO compatibility check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR modifies only scripts/copy-metal, a shell script for extracting CoreOS images. Contains no Kubernetes manifests, operator code, controllers, or scheduling constraints. No topology checks needed.
Ote Binary Stdout Contract ✅ Passed This PR modifies only shell scripts, Dockerfile, and documentation. No Go test code or OTE binaries exist. The OTE Binary Stdout Contract check is not applicable.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests added in this PR. The PR modifies shell scripts (scripts/copy-metal) only. Check is not applicable to non-test code changes.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

@openshift-ci openshift-ci Bot requested review from iurygregory and zaneb April 14, 2026 17:28
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 14, 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 Apr 14, 2026
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: 1

🧹 Nitpick comments (1)
scripts/copy-metal (1)

131-134: Avoid hard-coded stream mapping in this loop.

This works for 9/10, but it will drift when new streams are added. Prefer deriving stream directly from Line 126’s JSON.

Refactor idea
-    case "${prefix}" in
-        coreos) stream="rhel-9" ;;
-        coreos10) stream="rhel-10" ;;
-    esac
+    stream="$(jq -r '.stream // empty' "${stream_json}")"
+    if [ -z "${stream}" ]; then
+        echo "Failed to read stream from ${stream_json}" >&2
+        continue
+    fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/copy-metal` around lines 131 - 134, The hard-coded case mapping for
the variable stream (inside the case on prefix) should be replaced by extracting
the stream value from the parsed JSON produced earlier (the variable holding the
JSON parsed at Line 126), so that stream is derived dynamically for the current
prefix instead of using fixed coreos/coreos10 branches; locate the case
statement that matches prefix and replace it with a lookup from that JSON (e.g.,
use jq or shell-parsed fields to get the stream for the current prefix) so
prefix -> stream comes directly from the parsed JSON variable rather than
hard-coded values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/copy-metal`:
- Around line 142-145: The current ln -f -s commands that create aarch64
symlinks (using variables prefix, stream, DEST_DIR and conditional on arch ==
"x86_64") can produce dangling symlinks when the source artifacts are missing;
change the logic to test for the existence of each source file (e.g., [ -f
"${prefix}-aarch64.iso" ] and [ -f "${prefix}-aarch64-initrd.img" ]) before
creating the corresponding symlink so links are only created when the real
artifact exists; apply the same existence checks to the other aarch64 symlink
block referenced (lines creating ironic-python-agent-..._aarch64.iso and
..._aarch64.initramfs) to avoid broken stream/arch discovery at runtime.

---

Nitpick comments:
In `@scripts/copy-metal`:
- Around line 131-134: The hard-coded case mapping for the variable stream
(inside the case on prefix) should be replaced by extracting the stream value
from the parsed JSON produced earlier (the variable holding the JSON parsed at
Line 126), so that stream is derived dynamically for the current prefix instead
of using fixed coreos/coreos10 branches; locate the case statement that matches
prefix and replace it with a lookup from that JSON (e.g., use jq or shell-parsed
fields to get the stream for the current prefix) so prefix -> stream comes
directly from the parsed JSON variable rather than hard-coded values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: b089df0b-e655-48c1-b845-deb286822f63

📥 Commits

Reviewing files that changed from the base of the PR and between df9a652 and 94c5b48.

📒 Files selected for processing (2)
  • Dockerfile
  • scripts/copy-metal

Comment thread scripts/copy-metal
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Apr 14, 2026

@honza: This pull request references METAL-1730 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 "5.0.0" version, but no target version was set.

Details

In response to this:

Create ironic-python-agent-{stream} symlinks (e.g., ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.iso) for all available CoreOS versions. These allow the image-customization-controller to discover and serve different CoreOS base images based on the coreos.openshift.io/stream label.

How it works

The coreos.openshift.io/stream label on a BareMetalHost determines
which kernel, initrd, and rootfs files are used during PXE boot. For
example, a BMH labeled coreos.openshift.io/stream: rhel-10 will PXE
boot with ironic-python-agent-rhel-10.kernel,
ironic-python-agent-rhel-10.initramfs, and
ironic-python-agent-rhel-10.rootfs instead of the unqualified defaults.

The per-node coreos.live.rootfs_url kernel parameter set via ICC's
ExtraKernelParams overrides the global one from ironic-image because
dracut's getarg returns the last value for duplicate parameters.

Changes across repos

installer (openshift/installer#10502)
pkg/asset/machines/baremetal/: Sets the coreos.openshift.io/stream
label on all generated BareMetalHost objects (control-plane, worker,
arbiter) based on the install-config's OSImageStream setting, defaulting
to rhel-9. Configures hostSelector.matchLabels on
BareMetalMachineProviderSpec so MachineSets only claim BMHs with the
matching stream label.

cluster-baremetal-operator (openshift/cluster-baremetal-operator#590)
provisioning/image_customization.go: Passes three new environment
variables to the ICC container: DEPLOY_KERNEL (path to the kernel file),
IMAGE_SHARED_DIR (path to the shared images directory where
stream-prefixed files are discovered), and IRONIC_ROOTFS_URL (base URL
for the rootfs served by httpd). These enable ICC to discover multi-stream
images and construct per-node kernel/rootfs URLs.

machine-os-images (openshift/machine-os-images#83)
scripts/copy-metal: Creates stream-prefixed IPA symlinks (e.g.
ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.kernel)
for all available CoreOS versions, allowing ICC to discover images by
stream. Also fixes missing initramfs symlinks in the fixed=true (PXE)
block — without these, a stream-specific kernel would be paired with the
default-stream initrd, causing a version mismatch that crashes immediately
on PXE boot.

image-customization-controller (openshift/image-customization-controller#175)
pkg/imagehandler/, pkg/imageprovider/rhcos.go: Adds OS stream
selection so ICC can serve different CoreOS base images based on the
coreos.openshift.io/stream label. Images are indexed by (stream,
architecture) and discovered from stream-prefixed filenames. Key changes:

  • streamArchSpecificURL() transforms the base rootfs URL into a stream-
    and arch-specific URL
  • BuildImage() passes the stream to ServeKernel() and uses the
    stream-specific rootfs URL in ExtraKernelParams
  • imageKey() includes the stream in the cache key so that changing a
    BMH's stream label invalidates the cached image

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 Apr 24, 2026

/retest-required

@dtantsur
Copy link
Copy Markdown
Member

/lgtm

The bot's suggestion is worth addressing but it doesn't seem critical to me.

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Apr 29, 2026
Create ironic-python-agent-{stream} symlinks (e.g.,
ironic-python-agent-rhel-9.iso, ironic-python-agent-rhel-10.iso)
for all available CoreOS versions. These allow the
image-customization-controller to discover and serve different
CoreOS base images based on the coreos.openshift.io/stream label.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label May 6, 2026
@hroyrh
Copy link
Copy Markdown
Contributor

hroyrh commented May 8, 2026

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label May 8, 2026
@sgoveas
Copy link
Copy Markdown

sgoveas commented May 19, 2026

/verified by sgoveas link

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label May 19, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@sgoveas: This PR has been marked as verified by sgoveas [link](https://redhat.atlassian.net/browse/METAL-1604?focusedCommentId=17027031).

Details

In response to this:

/verified by sgoveas link

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.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 19, 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 a8b4286 into openshift:main May 19, 2026
8 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants