Skip to content

WIP: merge plugin system#497

Open
honza wants to merge 6 commits into
openshift:mainfrom
honza:merge-upstream-2026-06-11
Open

WIP: merge plugin system#497
honza wants to merge 6 commits into
openshift:mainfrom
honza:merge-upstream-2026-06-11

Conversation

@honza

@honza honza commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Plugin-based provisioner model with a new --provisioner selection; runtime now ships additional provisioner plugins plus a compiled-in fixture.
  • Documentation

    • Added a plugin provisioning guide and updated developer setup with CGO/toolchain and run-mode guidance.
  • Chores

    • Build and container flows refactored to enable CGO, produce plugin artifacts, and include plugins in runtime images; local/dev tooling adjusted.
  • Tests

    • Added a plugin-load-test tool and tests to validate plugin loading and behavior.

s3rj1k and others added 3 commits June 5, 2026 19:40
Signed-off-by: s3rj1k <evasive.gyron@gmail.com>
Signed-off-by: s3rj1k <evasive.gyron@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

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: f2735dfe-b9d5-431c-92eb-412a1544254d

📥 Commits

Reviewing files that changed from the base of the PR and between 0d5a25f and 076108d.

📒 Files selected for processing (1)
  • Makefile
🚧 Files skipped from review as they are similar to previous changes (1)
  • Makefile

Walkthrough

Adds a runtime Go plugin system for provisioners: load .so plugins from /plugins, validate PluginName/NewProvisionerFactory (optional HostConfigure), replace mode flags with --provisioner, and update builds/Makefile/Tiltfile/Dockerfiles to build, test, and ship plugin artifacts.

Changes

Go Plugin System for Provisioners

Layer / File(s) Summary
Container build: multi-stage, CGO, plugins, runtime copy
Dockerfile, Dockerfile.ocp
Refactor Dockerfiles to use an sdk stage, enable CGO, build manager and plugin .so artifacts, and copy plugins into /plugins/ in the final image.
Plugin testing and patcher tooling
Dockerfile.plugin-test, hack/plugin-test/*, cmd/plugin-load-test/*
Add Dockerfile.plugin-test to build an isolated demo plugin and run plugin-load-test in-image; include hack/plugin-test/patcher to rewrite demo GetHealth; add cmd/plugin-load-test CLI to validate plugin load/HostConfigure/GetHealth.
Makefile & Tiltfile: build targets and live-update
Makefile, Tiltfile
Makefile adds plugin build targets and docker-build-sdk helpers; docker-debug LDFLAGS cleared. Tiltfile builds CGO-enabled manager and ironic-provisioner.so, and syncs plugin into /plugins/ for live-update.
Plugin contract and core loading infrastructure
pkg/provisioner/plugin.go, pkg/provisioner/plugin_test.go
Defines HostFeature flags, PluginConfig and HostConfigure types, Plugin wrapper and Open() loader that validates required symbols/signatures; tests cover feature checks, missing-file errors, name validation, and HostConfigure absence.
Manager integration of plugin loading
main.go
Replaces --test-mode/--demo-mode with --provisioner flag, validates name, resolves plugin path under configured dir, opens plugin via provisioner.Open(), runs HostConfigure, merges scheme/cache requirements, and selects fixture or plugin-provided factory.
Demo and Ironic provisioner plugins
pkg/provisioner/demo/plugin/main.go, pkg/provisioner/ironic/plugin/main.go
Add plugin entrypoints exporting PluginName and NewProvisionerFactory; Ironic plugin implements HostConfigure to register Ironic scheme and optionally scope cache.
Configuration and documentation
config/overlays/fixture/kustomization.yaml, docs/*
Fixture overlay switched to --provisioner=fixture. Docs updated with plugin provisioning guidance, dev toolchain/CGO notes, and new plugin-provisioners.md describing selection, required symbols, HostConfigure contract, process-wide plugin caveats, and compatibility constraints.
Lint config, controller/test tweaks
.golangci.yaml, internal/controller/*, pkg/provisioner/ironic/register_test.go
Add golangci-lint config, minor whitespace/explicit-return edits, and small test error-message improvements.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 12 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title is vague and non-descriptive. 'WIP: merge plugin system' uses generic terminology without conveying what specific aspect of the plugin system is being merged or what the primary change entails. Replace with a more specific title that describes the main change, such as 'Add provisioner plugin system with Go plugin support' or 'Implement runtime plugin loading for provisioners'.
Test Structure And Quality ❓ Inconclusive The custom check instructions specify review of "Ginkgo test code" with "It blocks", "BeforeEach/AfterEach", and "Eventually/Consistently calls". However, this repository uses standard Go testing (... The check cannot be applied: PR contains standard Go tests, not Ginkgo tests. If Ginkgo is required, verify if this should be changed. If standard Go testing is acceptable, clarify the check scope.
✅ Passed checks (12 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 All Ginkgo test names across 16 e2e test files and all unit tests use static, descriptive strings with no dynamic values. Dynamic content (pod names, namespaces, etc.) properly appear only in test...
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests added in this PR; only unit tests added (pkg/provisioner/plugin_test.go uses standard Go testing, not Ginkgo). Check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests were added in this PR. Test additions are standard Go unit tests (testing.T), not Ginkgo tests.
Topology-Aware Scheduling Compatibility ✅ Passed PR refactors provisioner plugin architecture. No deployment manifests with topology-unaware scheduling constraints (affinity, PDB, replicas, control-plane selectors) were added or modified.
Ote Binary Stdout Contract ✅ Passed The main operator binary (baremetal-operator/manager) in main.go properly configures logging to stderr via zap with no stdout violations. The init() function performs no logging, var declarations c...
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests were added in this PR. The only new test file (pkg/provisioner/plugin_test.go) is a standard Go unit test using testing.T, with no IPv4 assumptions or external connectivity requ...
No-Weak-Crypto ✅ Passed No weak cryptography detected: no MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB mode, or custom crypto implementations in PR changes; only crypto/tls, x509, and ecdh used legitimately.
Container-Privileges ✅ Passed Container privilege check passed: no privileged:true, hostPID/hostNetwork/hostIPC, SYS_ADMIN, allowPrivilegeEscalation:true, or root user found. All images/manifests run as non-root with restricted...
No-Sensitive-Data-In-Logs ✅ Passed No sensitive data logging found. Plugin paths, names, and env var names are logged safely; actual credential values are never logged. All error messages reference environment variable names only, n...

✏️ 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 added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 11, 2026
@openshift-ci openshift-ci Bot requested review from dtantsur and elfosardo June 11, 2026 12:51
@openshift-ci

openshift-ci Bot commented Jun 11, 2026

Copy link
Copy Markdown

[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 Jun 11, 2026
Enable CGO_ENABLED=1 for the builder stage (required by Go's plugin
package which uses dlopen/dlsym) and copy the ironic and demo
provisioner plugin .so files to /plugins/ in the final image.

The base-rhel9 runtime image already includes glibc, so no base image
change is needed (unlike upstream which switched from distroless/static
to distroless/base-debian13).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
hack/plugin-test/patcher/main.go (1)

65-74: 💤 Low value

Guard type assertion to prevent panic.

Line 70 performs an unchecked type assertion bodyFile.Decls[0].(*ast.FuncDecl). While newBodySrc is currently a constant that parses correctly, if the constant is modified incorrectly in the future, this will panic rather than returning a helpful error.

🛡️ Proposed fix to add type assertion guard
-	newBody := bodyFile.Decls[0].(*ast.FuncDecl).Body
+	funcDecl, ok := bodyFile.Decls[0].(*ast.FuncDecl)
+	if !ok || funcDecl.Body == nil {
+		die("internal error: newBodySrc did not parse to a function with body")
+	}
+	newBody := funcDecl.Body
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/plugin-test/patcher/main.go` around lines 65 - 74, The unchecked type
assertion on bodyFile.Decls[0] can panic; guard it by verifying bodyFile.Decls
has at least one declaration and using the comma-ok form to assert *ast.FuncDecl
(e.g. replace the direct assertion bodyFile.Decls[0].(*ast.FuncDecl) with a safe
check: ensure len(bodyFile.Decls) > 0, do decl, ok :=
bodyFile.Decls[0].(*ast.FuncDecl) and if !ok or decl.Body == nil call die with a
clear message), then use decl.Body as newBody when calling
replaceMethodBody("demoProvisioner", "GetHealth", newBody).
Dockerfile.plugin-test (1)

61-65: ⚡ Quick win

Harden the final test stage.

Line 61 still uses the full Go SDK image, and this stage never drops root before executing plugin-load-test on Line 65. Since this stage only needs the two compiled binaries plus the .so, switch it to a minimal runtime base and set a non-root USER; if you use a shell-less base, make Line 65 an exec-form RUN. As per coding guidelines, Multi-stage builds; no build tools in final image and USER non-root; never run as root.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile.plugin-test` around lines 61 - 65, Change the plugin-load-tester
stage to use a minimal runtime base (not the full Go SDK) and run as a non-root
user: replace FROM $BUILD_IMAGE AS plugin-load-tester with a lightweight runtime
image (e.g., alpine or scratch + required libc) and create or switch to a
non-root user (USER <nonroot>), ensure the copied artifacts remain
/usr/local/bin/baremetal-operator, /usr/local/bin/plugin-load-test and
/plugins/demo-provisioner.so, and make the plugin test invocation use the exec
form (RUN
["/usr/local/bin/plugin-load-test","/plugins/demo-provisioner.so","demo","healthy"])
if you pick a shell-less base so the test runs without root privileges.

Sources: Coding guidelines, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/plugin-load-test/main.go`:
- Around line 76-87: The code uses context.Background() for calling
prov.GetHealth which can hang; change to a cancellable context with a timeout by
creating a context via context.WithTimeout (e.g., 5s or configurable), defer
cancel(), and pass that ctx into factory.NewProvisioner and prov.GetHealth;
update uses of ctx and ensure the timeout context is used for the GetHealth
validation to avoid indefinite blocking when prov.GetHealth hangs.

In `@Dockerfile`:
- Line 3: The ARG BASE_IMAGE currently points to gcr.io/distroless/base-debian13
which violates the repo policy; update the Dockerfile to use a compliant base
(either a UBI minimal image or a distroless image hosted on catalog.redhat.com)
by replacing the value of ARG BASE_IMAGE with the appropriate catalog.redhat.com
image (retain digest pinning) and keep the ARG name BASE_IMAGE unchanged so
downstream references (ARG BASE_IMAGE) and subsequent FROM instructions continue
to work.

In `@hack/plugin-test/patcher/main.go`:
- Around line 76-84: The current logic opens the target file with os.Create
(out) before calling format.Node, which can truncate the file if formatting
fails; change this to write to a temporary file and atomically rename it into
place: use os.CreateTemp (or ioutil.TempFile) to create a temp file in the same
directory, call format.Node writing to that temp file, close it, set file mode
to match the original (use os.Stat to get permissions if needed), then rename
the temp file over the original via os.Rename; ensure you remove the temp file
on any error and keep the die(...) error handling for failures (referencing
os.Create, format.Node, defer out.Close, die, os.Rename, os.CreateTemp/os.Stat).

In `@Makefile`:
- Around line 201-210: The Makefile's manager, run, and demo targets build/run
main.go without CGO, causing divergence from Dockerfile/Tiltfile; update the
targets (manager, run, demo) to set CGO_ENABLED=1 when invoking go build or go
run (e.g., prefix commands with CGO_ENABLED=1) so the manager binary is built
with cgo enabled for dynamic plugin loading, ensuring consistency with the
Dockerfile and Tiltfile behavior.

---

Nitpick comments:
In `@Dockerfile.plugin-test`:
- Around line 61-65: Change the plugin-load-tester stage to use a minimal
runtime base (not the full Go SDK) and run as a non-root user: replace FROM
$BUILD_IMAGE AS plugin-load-tester with a lightweight runtime image (e.g.,
alpine or scratch + required libc) and create or switch to a non-root user (USER
<nonroot>), ensure the copied artifacts remain
/usr/local/bin/baremetal-operator, /usr/local/bin/plugin-load-test and
/plugins/demo-provisioner.so, and make the plugin test invocation use the exec
form (RUN
["/usr/local/bin/plugin-load-test","/plugins/demo-provisioner.so","demo","healthy"])
if you pick a shell-less base so the test runs without root privileges.

In `@hack/plugin-test/patcher/main.go`:
- Around line 65-74: The unchecked type assertion on bodyFile.Decls[0] can
panic; guard it by verifying bodyFile.Decls has at least one declaration and
using the comma-ok form to assert *ast.FuncDecl (e.g. replace the direct
assertion bodyFile.Decls[0].(*ast.FuncDecl) with a safe check: ensure
len(bodyFile.Decls) > 0, do decl, ok := bodyFile.Decls[0].(*ast.FuncDecl) and if
!ok or decl.Body == nil call die with a clear message), then use decl.Body as
newBody when calling replaceMethodBody("demoProvisioner", "GetHealth", newBody).
🪄 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: Enterprise

Run ID: 40d21e6a-71dd-47ee-a8ae-98af6f634b16

📥 Commits

Reviewing files that changed from the base of the PR and between fbb5bf8 and e45e042.

📒 Files selected for processing (15)
  • Dockerfile
  • Dockerfile.plugin-test
  • Makefile
  • Tiltfile
  • cmd/plugin-load-test/main.go
  • config/overlays/fixture/kustomization.yaml
  • docs/configuration.md
  • docs/dev-setup.md
  • docs/plugin-provisioners.md
  • hack/plugin-test/patcher/main.go
  • main.go
  • pkg/provisioner/demo/plugin/main.go
  • pkg/provisioner/ironic/plugin/main.go
  • pkg/provisioner/plugin.go
  • pkg/provisioner/plugin_test.go

Comment on lines +76 to +87
ctx := context.Background()

prov, err := factory.NewProvisioner(ctx, provisioner.HostData{}, func(_, _ string) {})
if err != nil {
log.Fatalf("NewProvisioner: %v", err)
}
if prov == nil {
log.Fatal("NewProvisioner returned nil")
}

health := prov.GetHealth(ctx)
log.Printf("GetHealth OK output=%q", health)

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 | 🟡 Minor | ⚡ Quick win

Add timeout to context for GetHealth validation.

The tool uses context.Background() without a timeout when calling plugin methods. If a plugin's GetHealth() implementation hangs or blocks indefinitely, this validation tool will also hang, making it difficult to detect misbehaving plugins in CI/test environments.

⏱️ Proposed fix to add context timeout
-	ctx := context.Background()
+	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+	defer cancel()
 
 	prov, err := factory.NewProvisioner(ctx, provisioner.HostData{}, func(_, _ string) {})

As per coding guidelines, Go code should use "context.Context for cancellation and timeouts" to prevent operations from hanging indefinitely.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/plugin-load-test/main.go` around lines 76 - 87, The code uses
context.Background() for calling prov.GetHealth which can hang; change to a
cancellable context with a timeout by creating a context via context.WithTimeout
(e.g., 5s or configurable), defer cancel(), and pass that ctx into
factory.NewProvisioner and prov.GetHealth; update uses of ctx and ensure the
timeout context is used for the GetHealth validation to avoid indefinite
blocking when prov.GetHealth hangs.

Source: Coding guidelines

Comment thread Dockerfile
# Support FROM override
ARG BUILD_IMAGE=docker.io/golang:1.25.11@sha256:dd7d32e19b28621cd982082397fc0510d396805b717d5e77466aa2dd692340de
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:9ecc53c269509f63c69a266168e4a687c7eb8c0cfd753bd8bfcaa4f58a90876f
ARG BASE_IMAGE=gcr.io/distroless/base-debian13:nonroot@sha256:a557d784ac275c287d2bdf3172f47bece8d2a0ef3c0fdefb712e95084a04a562

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 | ⚡ Quick win

Use a supported runtime base image.

Line 3 moves the shipped manager image to gcr.io/distroless/..., but this repo’s container policy requires a UBI minimal base or a distroless image from catalog.redhat.com. That makes the new runtime image non-compliant even though it is digest-pinned. As per coding guidelines, Base image: UBI minimal or distroless from catalog.redhat.com.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 3, The ARG BASE_IMAGE currently points to
gcr.io/distroless/base-debian13 which violates the repo policy; update the
Dockerfile to use a compliant base (either a UBI minimal image or a distroless
image hosted on catalog.redhat.com) by replacing the value of ARG BASE_IMAGE
with the appropriate catalog.redhat.com image (retain digest pinning) and keep
the ARG name BASE_IMAGE unchanged so downstream references (ARG BASE_IMAGE) and
subsequent FROM instructions continue to work.

Source: Coding guidelines

Comment on lines +76 to +84
out, err := os.Create(path)
if err != nil {
die("open %s: %v", path, err)
}
defer out.Close()

if err := format.Node(out, fset, file); err != nil {
die("format: %v", err)
}

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 | ⚡ Quick win

Replace in-place file overwrite with atomic write.

The patcher opens the target file for writing (line 76) before format.Node() completes (line 82). If formatting fails, the original file is truncated or partially overwritten, causing data loss. This is especially problematic during development when the AST manipulation logic might have bugs.

💾 Proposed fix using temp file and atomic rename
-	out, err := os.Create(path)
+	tmpPath := path + ".tmp"
+	out, err := os.Create(tmpPath)
 	if err != nil {
-		die("open %s: %v", path, err)
+		die("create temp %s: %v", tmpPath, err)
 	}
-	defer out.Close()
 
 	if err := format.Node(out, fset, file); err != nil {
+		out.Close()
+		os.Remove(tmpPath)
 		die("format: %v", err)
 	}
+
+	if err := out.Close(); err != nil {
+		os.Remove(tmpPath)
+		die("close temp: %v", err)
+	}
+
+	if err := os.Rename(tmpPath, path); err != nil {
+		die("rename %s to %s: %v", tmpPath, path, err)
+	}
📝 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.

Suggested change
out, err := os.Create(path)
if err != nil {
die("open %s: %v", path, err)
}
defer out.Close()
if err := format.Node(out, fset, file); err != nil {
die("format: %v", err)
}
tmpPath := path + ".tmp"
out, err := os.Create(tmpPath)
if err != nil {
die("create temp %s: %v", tmpPath, err)
}
if err := format.Node(out, fset, file); err != nil {
out.Close()
os.Remove(tmpPath)
die("format: %v", err)
}
if err := out.Close(); err != nil {
os.Remove(tmpPath)
die("close temp: %v", err)
}
if err := os.Rename(tmpPath, path); err != nil {
die("rename %s to %s: %v", tmpPath, path, err)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/plugin-test/patcher/main.go` around lines 76 - 84, The current logic
opens the target file with os.Create (out) before calling format.Node, which can
truncate the file if formatting fails; change this to write to a temporary file
and atomically rename it into place: use os.CreateTemp (or ioutil.TempFile) to
create a temp file in the same directory, call format.Node writing to that temp
file, close it, set file mode to match the original (use os.Stat to get
permissions if needed), then rename the temp file over the original via
os.Rename; ensure you remove the temp file on any error and keep the die(...)
error handling for failures (referencing os.Create, format.Node, defer
out.Close, die, os.Rename, os.CreateTemp/os.Stat).

Comment thread Makefile
Comment on lines +201 to +210
manager: generate lint ironic-plugin demo-plugin ## Build manager binary and bundled provisioner plugins
go build -ldflags $(LDFLAGS) -o bin/$(OPERATOR_NAME) main.go

.PHONY: run
run: generate lint manifests ## Run against the configured Kubernetes cluster in ~/.kube/config
go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -webhook-port=0 $(RUN_FLAGS)
run: generate lint manifests ironic-plugin ## Run against the configured Kubernetes cluster in ~/.kube/config
PROVISIONER_PLUGIN_DIR=$(BIN_DIR) go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -provisioner=ironic -webhook-port=0 $(RUN_FLAGS)

.PHONY: demo
demo: generate lint manifests ## Run in demo mode
go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -demo-mode -webhook-port=0 $(RUN_FLAGS)
demo: generate lint manifests demo-plugin ## Run in demo mode
PROVISIONER_PLUGIN_DIR=$(BIN_DIR) go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -provisioner=demo -webhook-port=0 $(RUN_FLAGS)

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '=== Makefile plugin-enabled targets ===\n'
sed -n '200,210p' Makefile

printf '\n=== Dockerfile build flags ===\n'
sed -n '21,37p' Dockerfile

printf '\n=== Tiltfile build flags ===\n'
sed -n '99,106p' Tiltfile

Repository: openshift/baremetal-operator

Length of output: 1983


Align Makefile local plugin-enabled manager/run/demo builds with CGO-enabled plugin loading.
Makefile’s manager, run, and demo targets build/run main.go without CGO_ENABLED=1, while the Dockerfile sets ENV CGO_ENABLED=1 and Tiltfile builds the manager with CGO_ENABLED=1 (comment: “CGO and dynamic linking are required for the manager to load provisioner plugins”). Add CGO_ENABLED=1 to the Makefile targets to prevent local/prod build divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 201 - 210, The Makefile's manager, run, and demo
targets build/run main.go without CGO, causing divergence from
Dockerfile/Tiltfile; update the targets (manager, run, demo) to set
CGO_ENABLED=1 when invoking go build or go run (e.g., prefix commands with
CGO_ENABLED=1) so the manager binary is built with cgo enabled for dynamic
plugin loading, ensuring consistency with the Dockerfile and Tiltfile behavior.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
Dockerfile.ocp (2)

14-24: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Run the runtime image as non-root.

The final stage has no USER directive, so the container defaults to root. Please set an explicit non-root UID/GID in the runtime stage before shipping plugin artifacts.

As per coding guidelines, "USER non-root; never run as root".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile.ocp` around lines 14 - 24, The runtime Docker stage leaves the
container running as root; update the final stage (the one using FROM
registry.ci.openshift.org/ocp/5.0:base-rhel9 and the COPY lines for
baremetal-operator, get-hardware-details, make-bm-worker, make-virt-host and
/plugins/*) to create or use a non-root UID/GID (e.g., 1001), chown the copied
binaries/plugins and manifests to that UID:GID, and add an explicit USER
<uid>:<gid> directive so the container runs non-root at runtime; ensure
ownership changes apply to /plugins and all copied files before switching USER.

Source: Coding guidelines


14-24: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add a container HEALTHCHECK in the runtime stage.

There is no health check in the final image. Add a lightweight probe so orchestrators can detect unhealthy instances.

As per coding guidelines, "HEALTHCHECK defined".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile.ocp` around lines 14 - 24, Add a Docker HEALTHCHECK to the
final/runtime stage (after the LABEL io.openshift.release.operator=true) so
orchestrators can detect unhealthy containers; configure a lightweight probe
(interval, timeout, start-period, retries) that validates the running
baremetal-operator binary (for example by checking the process via pgrep -f
baremetal-operator or hitting the operator health endpoint if it exposes one)
and returns non-zero on failure; ensure the instruction is added to
Dockerfile.ocp in the final stage where files like baremetal-operator and
plugins are copied.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Dockerfile.ocp`:
- Around line 14-24: The runtime Docker stage leaves the container running as
root; update the final stage (the one using FROM
registry.ci.openshift.org/ocp/5.0:base-rhel9 and the COPY lines for
baremetal-operator, get-hardware-details, make-bm-worker, make-virt-host and
/plugins/*) to create or use a non-root UID/GID (e.g., 1001), chown the copied
binaries/plugins and manifests to that UID:GID, and add an explicit USER
<uid>:<gid> directive so the container runs non-root at runtime; ensure
ownership changes apply to /plugins and all copied files before switching USER.
- Around line 14-24: Add a Docker HEALTHCHECK to the final/runtime stage (after
the LABEL io.openshift.release.operator=true) so orchestrators can detect
unhealthy containers; configure a lightweight probe (interval, timeout,
start-period, retries) that validates the running baremetal-operator binary (for
example by checking the process via pgrep -f baremetal-operator or hitting the
operator health endpoint if it exposes one) and returns non-zero on failure;
ensure the instruction is added to Dockerfile.ocp in the final stage where files
like baremetal-operator and plugins are copied.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: c2971134-5c35-45aa-b368-5bca4a343110

📥 Commits

Reviewing files that changed from the base of the PR and between e45e042 and 1c7ab2c.

📒 Files selected for processing (1)
  • Dockerfile.ocp

honza and others added 2 commits June 11, 2026 10:38
Downstream was missing the upstream .golangci.yaml which configures
golangci-lint to suppress deprecated API warnings (SA1019), De Morgan's
law suggestions (QF1001), and embedded field selector hints (QF1008).
Without it, the default linter config flagged 29 issues that upstream
intentionally suppresses.

Also fix the 4 real lint issues caught by the upstream config:
- gci: import grouping in action_result.go
- govet: variable shadowing in register_test.go (2 instances)
- nakedret: naked return in host_state_machine.go ensureRegistered
- unused: restore nolint directive on deleteComplete.actionComplete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test/ module imports libvirt.org/go/libvirt which requires
libvirt-dev C headers for CGO compilation. These are not available
in the downstream CI environment. Downstream does not run upstream
e2e tests so linting this module is not needed.

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

openshift-ci Bot commented Jun 11, 2026

Copy link
Copy Markdown

@honza: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-metal-ipi-serial-ipv4 076108d link true /test e2e-metal-ipi-serial-ipv4
ci/prow/e2e-metal-ipi-virtualmedia 076108d link true /test e2e-metal-ipi-virtualmedia
ci/prow/e2e-metal-ipi-ovn-ipv6 076108d link true /test e2e-metal-ipi-ovn-ipv6

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.

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. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants