Skip to content

Add DNS record creation support for servers#546

Merged
stefanhipfel merged 14 commits intomainfrom
dnsRecord
Jan 19, 2026
Merged

Add DNS record creation support for servers#546
stefanhipfel merged 14 commits intomainfrom
dnsRecord

Conversation

@stefanhipfel
Copy link
Contributor

@stefanhipfel stefanhipfel commented Dec 2, 2025

Proposed Changes

  • Introduced DNSRecordTemplateConfigMap field in BMCReconciler to specify the ConfigMap for DNS record templates.
  • Implemented logic to create DNS records for servers during discovery if the ConfigMap is configured.
  • Added a new ConfigMap for DNS record templates with example structure.
  • Enhanced unit tests to verify DNS record creation functionality.

Summary by CodeRabbit

  • New Features
    • Added hostname to BMC spec, DNS record templating, and a CLI flag to enable automatic DNSRecord creation during reconciliation.
  • Documentation
    • Updated API reference and BMC concepts with hostname and DNS templating examples and usage.
  • Tests
    • Added tests validating DNSRecord creation from templates during reconciliation.
  • Chores
    • Expanded test-data coverage to include YAML files for annotation/license checks.

✏️ Tip: You can customize this high-level summary in your review settings.

…figMap

- Introduced DNSRecordTemplateConfigMap field in BMCReconciler to specify the ConfigMap for DNS record templates.
- Implemented logic to create DNS records for servers during discovery if the ConfigMap is configured.
- Added a new ConfigMap for DNS record templates with example structure.
- Enhanced unit tests to verify DNS record creation functionality.
@github-actions github-actions bot added the size/L label Dec 2, 2025
@stefanhipfel stefanhipfel changed the title Add DNS record creation support for servers using a template from Con… Add DNS record creation support for servers using a template from cm Dec 2, 2025
@afritzler afritzler added the enhancement New feature or request label Dec 2, 2025
@github-actions github-actions bot added api-change documentation Improvements or additions to documentation labels Dec 2, 2025
@afritzler
Copy link
Member

Btw. it would be also good if we update the documentation of the metal-operator describing how to use that feature. I would suggest adding a configuration.md under https://github.com/ironcore-dev/metal-operator/tree/main/docs/usage

@afritzler afritzler changed the title Add DNS record creation support for servers using a template from cm Add DNS record creation support for servers Dec 15, 2025
- Introduced LoadTemplate function to load DNS record templates from files.
- Updated BMCReconciler to use DNSRecordTemplate instead of DNSRecordTemplatePath.
- Enhanced documentation to include usage of DNS record templates.
- Added tests for DNS record template loading.
@stefanhipfel stefanhipfel requested a review from a team as a code owner December 16, 2025 09:35
@afritzler
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Walkthrough

Adds optional hostname to BMCSpec and CRD, implements DNS-record templating (loader, CLI flag, ConfigMap template), integrates template rendering and apply into the BMC reconciler, updates tests to load the template, and includes YAML test data in REUSE annotations.

Changes

Cohort / File(s) Summary
API Schema & CRD
api/v1alpha1/bmc_types.go, api/v1alpha1/zz_generated.deepcopy.go, config/crd/bases/metal.ironcore.dev_bmcs.yaml, docs/api-reference/api.md
Add optional hostname (*string) to BMCSpec, add deepcopy handling, update CRD schema and API docs to accept the new field.
DNS Template Loader & Test Data
internal/cmd/dns/dns.go, test/data/dns_record_template.yaml, REUSE.toml
Add LoadTemplate(templatePath string) (string, error) to read templates; add test DNS record template file; include test/data/*.yaml in REUSE.toml annotations.
Manager Startup Integration
cmd/manager/main.go, config/manager/dns-record-template.yaml
Add --dns-record-template-path CLI flag, load/validate template at startup, pass loaded template into BMCReconciler, and add a ConfigMap manifest containing a DNS record template.
Controller / Reconciler Logic
internal/controller/bmc_controller.go
Add DNSRecordTemplate string to BMCReconciler; introduce DNSRecordTemplateData and helper to render template, unmarshal to unstructured, set owner refs, and apply DNSRecord resources during reconciliation.
Tests & Test Suite
internal/controller/suite_test.go, internal/controller/bmc_controller_test.go
Load DNS template in test setup and assign to reconciler; add test asserting DNSRecord creation for a BMC with hostname; adjust test setup usage and remove redundant logger init.
Docs / Concepts
docs/concepts/bmcs.md
Document the hostname field and DNS record templating workflow with example template and reconciliation step.

Sequence Diagram(s)

sequenceDiagram
  actor Operator
  participant Manager
  participant DNSLoader as dns.LoadTemplate
  participant Reconciler
  participant K8sAPI as Kubernetes API

  Operator->>Manager: start with --dns-record-template-path=path
  Manager->>DNSLoader: LoadTemplate(path)
  DNSLoader-->>Manager: template string / error
  Manager->>Reconciler: initialize with DNSRecordTemplate
  Reconciler->>K8sAPI: watch / reconcile BMC resources
  Note right of Reconciler: On BMC reconcile\n(render & apply per BMC)
  Reconciler->>Reconciler: render template (DNSRecordTemplateData)
  Reconciler->>K8sAPI: CreateOrPatch DNSRecord (unstructured, set ownerRef)
  K8sAPI-->>Reconciler: created/updated
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • Nuckal777
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add DNS record creation support for servers' clearly and concisely summarizes the main change in the changeset, which introduces DNS record creation functionality during server discovery.
Description check ✅ Passed The pull request description follows the required template structure with a 'Proposed Changes' section that clearly outlines the key modifications, including the DNSRecordTemplateConfigMap field, DNS record creation logic, ConfigMap template addition, and test enhancements.

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

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 562ddd1 and c90e96d.

📒 Files selected for processing (1)
  • internal/controller/bmc_controller.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Code must follow standard Go formatting and idioms; use clear, explicit error handling with minimal global state
Use small, focused functions; introduce interfaces only when justified

Files:

  • internal/controller/bmc_controller.go
🧠 Learnings (15)
📓 Common learnings
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.
📚 Learning: 2026-01-15T14:28:36.694Z
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Flag non-idempotent reconcile logic, Spec mutations during reconciliation, and custom patterns duplicating standard Kubernetes behavior

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status updates must be performed via `Status().Update()` or `Status().Patch()` and separated from spec mutations

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Avoid silent failures; ensure logging includes sufficient context (namespaced name, resource identifiers)

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use `client.Client`, `controllerutil.CreateOrUpdate`, and OwnerReferences from controller-runtime rather than direct API calls

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Use structured logging with controller-runtime logger; avoid excessive log noise in hot paths

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Follow Kubernetes API and controller patterns rather than inventing new abstractions

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use retries via requeue or error return in controllers, not explicit loops

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Include relevant identifiers in logs; emit events for meaningful user-facing state changes

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Errors must be propagated correctly to trigger retries; distinguish between transient and permanent errors where possible

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Prevent hot loops caused by status-only changes triggering reconciliation

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Reconciliation must be based on current cluster state, not assumptions; handle NotFound errors correctly and gracefully manage partial failures

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status must reflect observed state, not desired state; conditions must be updated consistently and deterministically

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-14T19:27:09.972Z
Learnt from: Nuckal777
Repo: ironcore-dev/metal-operator PR: 471
File: internal/controller/bmcsettingsset_controller.go:233-236
Timestamp: 2026-01-14T19:27:09.972Z
Learning: In the ironcore-dev/metal-operator repository, BMCSettings.Spec.BMCRef is marked with +required kubebuilder marker, ensuring API-level validation that prevents nil values; defensive nil checks are not needed when iterating BMCSettings from List operations.

Applied to files:

  • internal/controller/bmc_controller.go
🧬 Code graph analysis (1)
internal/controller/bmc_controller.go (2)
api/v1alpha1/bmc_types.go (3)
  • BMCSpec (25-68)
  • BMCStatus (169-222)
  • BMC (252-258)
bmc/bmc.go (1)
  • BMC (36-123)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run test chart
  • GitHub Check: Run test e2e
  • GitHub Check: Run tests
🔇 Additional comments (4)
internal/controller/bmc_controller.go (4)

7-18: LGTM — imports align with DNS templating flow.

The added packages match the new template rendering and unstructured apply logic.


176-181: LGTM — DNS record creation is properly gated.

Good to see the creation step only runs when both namespace and template are configured.


282-289: LGTM — template data struct is clear and focused.

The embedded spec/status and labels make template usage straightforward.


291-335: LGTM — template rendering + SSA apply is solid.

Parsing, validation, ownership, and apply flow are clean and easy to follow.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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.

❤️ Share

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

Copy link
Contributor

@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: 6

🤖 Fix all issues with AI agents
In `@docs/concepts/bmcs.md`:
- Line 81: Fix the typo in the "Create DNS Record" bullet: remove the duplicated
word "the" in the phrase "via the the managers `dns-record-template-path` flag"
so it reads "via the managers `dns-record-template-path` flag" (reference the
"Create DNS Record" bullet and the `dns-record-template-path` flag).
- Around line 99-106: The docs incorrectly state that "Spec" and "Status" are
injected as top-level template fields; in reality DNSRecordTemplateData embeds
metalv1alpha1.BMCSpec and metalv1alpha1.BMCStatus so their fields (e.g.,
Hostname, IP) are available at the top level in Go templates. Update the docs
section describing the injected template data to either list the actual injected
fields (for example Hostname from BMCSpec and IP from BMCStatus) or explicitly
explain that DNSRecordTemplateData embeds metalv1alpha1.BMCSpec and
metalv1alpha1.BMCStatus so template authors can reference embedded fields
directly (e.g., {{ .Hostname }} and {{ .IP }}) without using {{ .Spec.* }} or {{
.Status.* }}.

In `@internal/controller/bmc_controller_test.go`:
- Around line 327-336: The test deletes a Server before ensuring the reconciler
created it and also skips waiting for deletions; modify the cleanup to first
wait for the server to exist using the same pattern as other tests (e.g.,
Eventually(Object(server)) or Eventually(Get(...)).Should(Succeed())) to ensure
creation has occurred, then delete bmc, bmcSecret, server, and dnsRecord, and
after each Delete use Eventually(Get(...)).Should(Satisfy(apierrors.IsNotFound))
(or equivalent Eventually checks) to wait for actual removal; update references
to server, bmc, bmcSecret, and dnsRecord and reuse the existing
Eventually(Object(...)) / Get(...) helpers used elsewhere in this file.

In `@internal/controller/bmc_controller.go`:
- Around line 325-336: The CreateOrPatch mutation passed to
controllerutil.CreateOrPatch (in the block that operates on dnsRecord) never
sets any fields and thus never applies updates; change the anonymous mutation
function to copy the desired state into the existing object (e.g., set
dnsRecord.Spec, dnsRecord.Labels and any other mutable fields from the
desired/template DNS record) so updates are applied on patch, or if records are
intended to be immutable replace the CreateOrPatch call with a Create and handle
already-exists errors (IgnoreAlreadyExists) for clarity.
- Line 338: The log message uses the wrong identifier: replace the use of
server.Name in the log.Info call that reports DNS record creation/patch (the
line logging "Created or patched DNS record", "Record", server.Name,
"Operation", opResult) with the DNS record name sourced from bmcObj.Name (the
variable used when creating the DNS record earlier). Update the log invocation
to reference bmcObj.Name so the logged "Record" value matches the actual DNS
record created/patched.

In `@test/data/dns_record_template.yaml`:
- Around line 1-16: The test template currently renders a ConfigMap which breaks
createDNSRecordForServer() expecting a DNSRecord; update the YAML to use
apiVersion: dns.ironcore.dev/v1alpha1 and kind: DNSRecord and move the resource
payload from the data section into the DNSRecord spec (e.g., spec.hostname,
spec.ip, spec.recordType, spec.record, spec.zoneName, spec.ttl) while keeping
metadata (name, namespace, labels) the same so the rendered template unmarshals
as a DNSRecord for createDNSRecordForServer().
🧹 Nitpick comments (6)
api/v1alpha1/bmc_types.go (1)

64-67: LGTM - Hostname field follows Kubernetes API conventions.

The new optional Hostname field correctly uses:

  • PascalCase for the Go struct field
  • camelCase for the JSON serialization
  • Pointer type for optional semantics
  • Proper kubebuilder markers

Consider adding hostname format validation (e.g., RFC 1123 subdomain) via kubebuilder markers if strict DNS-compliant hostnames are required:

// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
test/data/dns_record_template.yaml (1)

6-9: Quote label values to prevent YAML parsing errors.

When labels contain special characters (e.g., colons, quotes, or values starting with @, *), the rendered YAML may be invalid. The static analysis error on line 8 is expected for template files, but the underlying concern is valid for rendered output.

♻️ Suggested fix
   labels:
   {{- range $key, $value := .Labels }}
-    {{ $key }}: {{ $value }}
+    "{{ $key }}": "{{ $value }}"
   {{- end }}
config/manager/dns-record-template.yaml (1)

39-42: Quote label keys and values in the production template.

Same concern as the test template—label keys or values with special characters could produce invalid YAML when rendered.

♻️ Suggested fix
     labels:
       {{- range $key, $value := .Labels }}
-      {{ $key }}: {{ $value }}
+      "{{ $key }}": "{{ $value }}"
       {{- end }}
internal/controller/bmc_controller.go (3)

70-72: Fix misleading comment.

The comment states "path to the file" but the field actually holds the template content (loaded at startup), not the file path.

📝 Suggested fix
-	// DNSRecordTemplatePath is the path to the file containing the DNSRecord template.
+	// DNSRecordTemplate holds the DNS record template content for creating DNS records.
 	DNSRecordTemplate string

268-275: Remove unnecessary IsNotFound check.

createDNSRecordForServer uses CreateOrPatch, which creates the resource if it doesn't exist. It won't return NotFound errors, making this check dead code that could mask future issues if the function's behavior changes.

♻️ Suggested fix
 		// Create DNS record for the server if template path is configured
 		if r.ManagerNamespace != "" && r.DNSRecordTemplate != "" {
-			if err := r.createDNSRecordForServer(ctx, log, bmcObj, server); err != nil && !apierrors.IsNotFound(err) {
+			if err := r.createDNSRecordForServer(ctx, log, bmcObj, server); err != nil {
 				log.Error(err, "failed to create DNS record for server", "Server", server.Name)
 				errs = append(errs, fmt.Errorf("failed to create DNS record for server %s: %w", server.Name, err))
 			}
 		}

303-307: Parse template once at startup instead of on every reconciliation.

Parsing the template on every call to createDNSRecordForServer is inefficient. Since the template content doesn't change after startup, parse it once and store the parsed *template.Template in the reconciler.

♻️ Suggested approach
  1. Change DNSRecordTemplate string to DNSRecordTemplate *template.Template in BMCReconciler
  2. Parse the template in main.go after loading:
if dnsRecordTemplatePath != "" {
    templateContent, err := dns.LoadTemplate(dnsRecordTemplatePath)
    if err != nil {
        setupLog.Error(err, "unable to load DNS record template")
        os.Exit(1)
    }
    dnsRecordTemplate, err = template.New("dnsRecord").Parse(templateContent)
    if err != nil {
        setupLog.Error(err, "unable to parse DNS record template")
        os.Exit(1)
    }
}
  1. Use the pre-parsed template directly in createDNSRecordForServer

This also provides early validation of the template syntax at startup.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37bd373 and af579e5.

⛔ Files ignored due to path filters (1)
  • dist/chart/templates/crd/metal.ironcore.dev_bmcs.yaml is excluded by !**/dist/**
📒 Files selected for processing (13)
  • REUSE.toml
  • api/v1alpha1/bmc_types.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • cmd/manager/main.go
  • config/crd/bases/metal.ironcore.dev_bmcs.yaml
  • config/manager/dns-record-template.yaml
  • docs/api-reference/api.md
  • docs/concepts/bmcs.md
  • internal/cmd/dns/dns.go
  • internal/controller/bmc_controller.go
  • internal/controller/bmc_controller_test.go
  • internal/controller/suite_test.go
  • test/data/dns_record_template.yaml
🧰 Additional context used
📓 Path-based instructions (4)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Code must follow standard Go formatting and idioms; use clear, explicit error handling with minimal global state
Use small, focused functions; introduce interfaces only when justified

Files:

  • internal/cmd/dns/dns.go
  • api/v1alpha1/bmc_types.go
  • cmd/manager/main.go
  • internal/controller/suite_test.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • internal/controller/bmc_controller_test.go
  • internal/controller/bmc_controller.go
**/api/**/*_types.go

📄 CodeRabbit inference engine (AGENTS.md)

**/api/**/*_types.go: API type fields must follow Kubernetes naming conventions: camelCase in JSON, PascalCase in Go structs
Use metav1.Time, metav1.Condition, and resource.Quantity for standard Kubernetes types in API definitions
Spec fields must be declarative and user-facing; Status fields must be controller-owned
Conditions in Status must follow Kubernetes conventions with stable Type, correct Status, Reason, and Message fields
No mutable fields in Spec that belong in Status; maintain clear separation between desired state and observed state
Do not make breaking changes to existing API fields without versioning; ensure additive-only changes for existing API versions
Clearly mark and document deprecated fields in API types

Files:

  • api/v1alpha1/bmc_types.go
**/api/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Implement defaulting and validation via webhooks (preferred) or OpenAPI schema annotations

Files:

  • api/v1alpha1/bmc_types.go
  • api/v1alpha1/zz_generated.deepcopy.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Tests must be deterministic and not rely on timing assumptions

Files:

  • internal/controller/suite_test.go
  • internal/controller/bmc_controller_test.go
🧠 Learnings (16)
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use `client.Client`, `controllerutil.CreateOrUpdate`, and OwnerReferences from controller-runtime rather than direct API calls

Applied to files:

  • cmd/manager/main.go
  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/api/**/*_types.go : Use `metav1.Time`, `metav1.Condition`, and `resource.Quantity` for standard Kubernetes types in API definitions

Applied to files:

  • cmd/manager/main.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller_test.go : Write unit tests for reconcile logic and pure functions

Applied to files:

  • internal/controller/suite_test.go
  • internal/controller/bmc_controller_test.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller_test.go : Use Envtest-based tests for controller behavior and API interactions

Applied to files:

  • internal/controller/suite_test.go
  • internal/controller/bmc_controller_test.go
📚 Learning: 2026-01-14T19:27:09.972Z
Learnt from: Nuckal777
Repo: ironcore-dev/metal-operator PR: 471
File: internal/controller/bmcsettingsset_controller.go:233-236
Timestamp: 2026-01-14T19:27:09.972Z
Learning: In the ironcore-dev/metal-operator repository, BMCSettings.Spec.BMCRef is marked with +required kubebuilder marker, ensuring API-level validation that prevents nil values; defensive nil checks are not needed when iterating BMCSettings from List operations.

Applied to files:

  • docs/api-reference/api.md
  • internal/controller/bmc_controller_test.go
  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_test.go : Use fake clients appropriately in tests

Applied to files:

  • internal/controller/bmc_controller_test.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Flag non-idempotent reconcile logic, Spec mutations during reconciliation, and custom patterns duplicating standard Kubernetes behavior

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Use structured logging with controller-runtime logger; avoid excessive log noise in hot paths

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Follow Kubernetes API and controller patterns rather than inventing new abstractions

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use retries via requeue or error return in controllers, not explicit loops

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Avoid silent failures; ensure logging includes sufficient context (namespaced name, resource identifiers)

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Include relevant identifiers in logs; emit events for meaningful user-facing state changes

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Errors must be propagated correctly to trigger retries; distinguish between transient and permanent errors where possible

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Prevent hot loops caused by status-only changes triggering reconciliation

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Reconciliation must be based on current cluster state, not assumptions; handle NotFound errors correctly and gracefully manage partial failures

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status must reflect observed state, not desired state; conditions must be updated consistently and deterministically

Applied to files:

  • internal/controller/bmc_controller.go
🧬 Code graph analysis (4)
cmd/manager/main.go (1)
internal/cmd/dns/dns.go (1)
  • LoadTemplate (12-23)
internal/controller/suite_test.go (2)
internal/cmd/dns/dns.go (1)
  • LoadTemplate (12-23)
internal/controller/bmc_controller.go (1)
  • BMCReconciler (58-73)
internal/controller/bmc_controller_test.go (3)
internal/controller/suite_test.go (1)
  • SetupTest (105-332)
api/v1alpha1/bmcsecret_types.go (2)
  • BMCSecret (24-57)
  • BMCSecretUsernameKeyName (13-13)
internal/bmcutils/bmcutils.go (1)
  • GetServerNameFromBMCandIndex (219-221)
internal/controller/bmc_controller.go (1)
api/v1alpha1/bmc_types.go (3)
  • BMCSpec (25-68)
  • BMCStatus (169-222)
  • BMC (252-258)
🪛 YAMLlint (1.37.1)
test/data/dns_record_template.yaml

[error] 8-8: syntax error: could not find expected ':'

(syntax)

🔇 Additional comments (16)
api/v1alpha1/zz_generated.deepcopy.go (1)

798-802: Auto-generated deepcopy is correct.

The generated deepcopy for Hostname *string follows the standard controller-gen pattern for pointer fields and is consistent with other pointer field handling in this file.

docs/api-reference/api.md (1)

550-550: Documentation accurately reflects the API.

The hostname field documentation correctly shows the field type and description, consistent with the BMCSpec definition in bmc_types.go.

REUSE.toml (1)

26-27: LGTM - REUSE annotations extended for YAML test data.

Adding test/data/*.yaml to the annotations path ensures the new DNS record template test fixtures are properly covered by SPDX license annotations, consistent with the existing JSON test data coverage.

config/crd/bases/metal.ironcore.dev_bmcs.yaml (1)

167-169: CRD schema correctly reflects the API type definition.

The hostname field is properly defined as an optional string field, consistent with the BMCSpec.Hostname field in the Go API types.

internal/cmd/dns/dns.go (1)

11-22: LGTM!

The function is clean, focused, and follows Go idioms with proper error handling and descriptive error messages.

config/manager/dns-record-template.yaml (1)

43-47: LGTM!

The spec section is well-structured with sensible defaults for recordType and ttl. The template variables for hostname and ip are appropriately used.

internal/controller/bmc_controller.go (2)

283-290: LGTM!

The struct design with embedded BMCSpec and BMCStatus provides flexible template access while explicitly controlling exposed fields through the Labels map.


320-323: LGTM!

Using controllerutil.SetControllerReference follows controller-runtime best practices. Based on learnings, this is the correct approach for owner references.

cmd/manager/main.go (3)

151-152: LGTM!

The flag definition follows the existing patterns in the codebase and has a clear description.


172-181: LGTM!

Loading the template at startup with proper error handling follows the pattern requested in previous reviews. Failing fast on startup if the template cannot be loaded is appropriate.


348-348: LGTM!

The loaded template is correctly passed to the BMCReconciler.

internal/controller/bmc_controller_test.go (2)

23-23: LGTM!

The change from discarding (_) to capturing (ns) the return value of SetupTest is correct, as the namespace is now needed for the DNS record assertion on line 310.


313-326: LGTM!

The assertions correctly verify:

  1. Owner reference pointing to the BMC with controller=true
  2. DNS record data fields (hostname, ip, recordType, ttl)

The test validates the core DNS record creation functionality as configured via the template. Based on learnings, this follows the Envtest-based testing approach for controller behavior.

internal/controller/suite_test.go (3)

20-20: LGTM!

The import for internal/cmd/dns is correctly added to support the dns.LoadTemplate function usage.


167-169: LGTM!

The DNS template loading follows the established test setup patterns:

  • Relative path correctly resolves from internal/controller/ to test/data/
  • Error handling is consistent with other setup assertions
  • Template is loaded in BeforeEach, ensuring each test spec gets a fresh manager with the template configured

170-179: Consider: DNS template is now always enabled for all tests.

The DNSRecordTemplate is configured for all tests using SetupTest. This means every test in the suite will have DNS record creation enabled, which could affect tests that aren't explicitly testing DNS functionality.

However, looking at the test file, the DNS record is only created when bmc.Spec.Hostname is set (based on the new test case on line 301), so existing tests without a hostname should remain unaffected. This is acceptable.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@internal/controller/bmc_controller.go`:
- Around line 293-301: The function createDNSRecordForServer has an unused
server parameter; remove the parameter and rename the function to
createDNSRecordForBMC (or keep name but drop the unused arg) and update all call
sites to match the new signature; specifically change the method signature on
BMCReconciler (remove the server *metalv1alpha1.Server param), update any
references to createDNSRecordForServer to the new name, and ensure you delete
any unused local/server references inside the function body or replace them with
server-specific template fields only if you intend to actually generate
per-server DNS records.
♻️ Duplicate comments (2)
internal/controller/bmc_controller.go (1)

320-326: CreateOrPatch mutation doesn't preserve template-rendered data on updates.

When the DNS record already exists, CreateOrPatch fetches and overwrites dnsRecord with the existing object's data before running the mutation function. Since the mutation only sets the controller reference, the template-rendered spec/data is lost on subsequent reconciliations.

If DNS records should be updatable, store the desired state before CreateOrPatch and restore it in the mutation. If they should be immutable after creation, use Create with IgnoreAlreadyExists for clarity.

🔧 Fix for updatable DNS records
+	// Store desired state before CreateOrPatch overwrites dnsRecord
+	desiredObject := dnsRecord.DeepCopy()
+
 	// Create or patch the DNS record
 	opResult, err := controllerutil.CreateOrPatch(ctx, r.Client, dnsRecord, func() error {
+		// Restore template-rendered data
+		for key, value := range desiredObject.Object {
+			if key != "metadata" {
+				dnsRecord.Object[key] = value
+			}
+		}
 		return controllerutil.SetControllerReference(bmcObj, dnsRecord, r.Scheme)
 	})
docs/concepts/bmcs.md (1)

81-81: Grammar: missing possessive apostrophe.

"the managers" should be "the manager's" (possessive form).

-- **Create DNS Record**: Automatically create DNS record using a provided Go YAML template via the managers `dns-record-template-path` flag.
+- **Create DNS Record**: Automatically create DNS record using a provided Go YAML template via the manager's `dns-record-template-path` flag.
🧹 Nitpick comments (3)
internal/controller/bmc_controller.go (2)

70-72: Comment doesn't match field name.

The comment says "DNSRecordTemplatePath" but the field is named DNSRecordTemplate. Since this holds the template content (not a path), the comment should be updated for accuracy.

-	// DNSRecordTemplatePath is the path to the file containing the DNSRecord template.
+	// DNSRecordTemplate is the template content for creating DNSRecord resources.
 	DNSRecordTemplate string

303-312: Consider parsing template once at startup instead of per-reconciliation.

The template is parsed on every call to createDNSRecordForServer. Since the template content doesn't change during runtime, parsing once at startup (or lazily on first use with caching) would be more efficient.

♻️ Suggested approach

Store the parsed template in the reconciler struct:

type BMCReconciler struct {
    // ... existing fields
    DNSRecordTemplate       string
    parsedDNSRecordTemplate *template.Template // Add cached parsed template
}

Parse once during setup or lazily:

func (r *BMCReconciler) getParsedDNSTemplate() (*template.Template, error) {
    if r.parsedDNSRecordTemplate == nil {
        tmpl, err := template.New("dnsRecord").Parse(r.DNSRecordTemplate)
        if err != nil {
            return nil, err
        }
        r.parsedDNSRecordTemplate = tmpl
    }
    return r.parsedDNSRecordTemplate, nil
}

Based on learnings, template loading was suggested to move to startup in main.go.

docs/concepts/bmcs.md (1)

99-106: Clarify that Spec and Status fields are directly accessible (not nested).

The documentation says "Spec" and "Status" are injected, but since these are embedded structs in DNSRecordTemplateData, their fields are accessible at the top level (e.g., {{ .Hostname }} not {{ .Spec.Hostname }}). This aligns with the template example on lines 66-67, but the bullet list could be clearer.

📝 Suggested clarification
 6. **Create BMC DNS Record**: If a DNS record template path is provided via the managers `dns-record-template-path` flag,
 the controller will create an instance using this template. The following BMC data is injected to the Go YAML template:
 - Namespace
 - Name
-- Spec (the full `Spec` object is injected into the template context, allowing direct access to its properties)
-- Status (the full `Status` object is injected into the template context, allowing direct access to its properties)
+- All fields from BMCSpec (e.g., `Hostname`, `Protocol`) - accessible directly as `{{ .Hostname }}`
+- All fields from BMCStatus (e.g., `IP`, `MACAddress`) - accessible directly as `{{ .IP }}`
 - Labels
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af579e5 and 1314c9d.

📒 Files selected for processing (2)
  • docs/concepts/bmcs.md
  • internal/controller/bmc_controller.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Code must follow standard Go formatting and idioms; use clear, explicit error handling with minimal global state
Use small, focused functions; introduce interfaces only when justified

Files:

  • internal/controller/bmc_controller.go
🧠 Learnings (18)
📓 Common learnings
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.
📚 Learning: 2026-01-15T14:28:36.694Z
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.

Applied to files:

  • docs/concepts/bmcs.md
  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/api/**/*_types.go : No mutable fields in Spec that belong in Status; maintain clear separation between desired state and observed state

Applied to files:

  • docs/concepts/bmcs.md
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/api/**/*_types.go : Spec fields must be declarative and user-facing; Status fields must be controller-owned

Applied to files:

  • docs/concepts/bmcs.md
📚 Learning: 2026-01-14T19:27:09.972Z
Learnt from: Nuckal777
Repo: ironcore-dev/metal-operator PR: 471
File: internal/controller/bmcsettingsset_controller.go:233-236
Timestamp: 2026-01-14T19:27:09.972Z
Learning: In the ironcore-dev/metal-operator repository, BMCSettings.Spec.BMCRef is marked with +required kubebuilder marker, ensuring API-level validation that prevents nil values; defensive nil checks are not needed when iterating BMCSettings from List operations.

Applied to files:

  • docs/concepts/bmcs.md
  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/api/**/*_types.go : Conditions in Status must follow Kubernetes conventions with stable Type, correct Status, Reason, and Message fields

Applied to files:

  • docs/concepts/bmcs.md
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status updates must be performed via `Status().Update()` or `Status().Patch()` and separated from spec mutations

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Flag non-idempotent reconcile logic, Spec mutations during reconciliation, and custom patterns duplicating standard Kubernetes behavior

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Avoid silent failures; ensure logging includes sufficient context (namespaced name, resource identifiers)

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use `client.Client`, `controllerutil.CreateOrUpdate`, and OwnerReferences from controller-runtime rather than direct API calls

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Use structured logging with controller-runtime logger; avoid excessive log noise in hot paths

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Follow Kubernetes API and controller patterns rather than inventing new abstractions

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use retries via requeue or error return in controllers, not explicit loops

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Include relevant identifiers in logs; emit events for meaningful user-facing state changes

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Errors must be propagated correctly to trigger retries; distinguish between transient and permanent errors where possible

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Prevent hot loops caused by status-only changes triggering reconciliation

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Reconciliation must be based on current cluster state, not assumptions; handle NotFound errors correctly and gracefully manage partial failures

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status must reflect observed state, not desired state; conditions must be updated consistently and deterministically

Applied to files:

  • internal/controller/bmc_controller.go
🧬 Code graph analysis (1)
internal/controller/bmc_controller.go (3)
api/v1alpha1/bmc_types.go (3)
  • BMCSpec (25-68)
  • BMCStatus (169-222)
  • BMC (252-258)
bmc/bmc.go (2)
  • BMC (36-123)
  • Server (182-189)
internal/api/registry/server.go (1)
  • Server (17-26)
🔇 Additional comments (4)
internal/controller/bmc_controller.go (2)

268-275: LGTM on conditional invocation.

The guards for ManagerNamespace and DNSRecordTemplate appropriately make this an opt-in feature. Error handling correctly logs and accumulates errors without blocking other servers.


283-290: LGTM on template data structure.

The embedded BMCSpec and BMCStatus structs allow template authors to reference their fields directly (e.g., {{ .Hostname }}, {{ .IP }}), which aligns with the documented template examples.

docs/concepts/bmcs.md (2)

29-29: LGTM on hostname field example.

The example correctly demonstrates the new hostname field in the BMC spec.


53-72: LGTM on template example.

The template example correctly demonstrates direct field access ({{ .Hostname }}, {{ .IP }}) which works because BMCSpec and BMCStatus are embedded in the template data structure.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@internal/controller/bmc_controller.go`:
- Around line 70-72: The struct comment for DNSRecordTemplate is incorrect: it
says "path to the file" but the field DNSRecordTemplate actually holds the
template content (used by createDNSRecordForServer). Update the comment to
reflect that DNSRecordTemplate contains the DNSRecord template content (not a
file path) and ensure any related docs or other comments in the same struct
align with that semantic.
- Around line 268-275: The DNS creation block inside the server iteration is
incorrect because createDNSRecordForServer only uses bmcObj and not per-server
data; move the entire if r.ManagerNamespace != "" && r.DNSRecordTemplate != "" {
... } block (including the call to r.createDNSRecordForServer and its error
handling) out of the per-server loop so it runs once per BMC reconciliation
(using bmcObj.Name in logs/errors rather than server.Name), and keep existing
checks for apierrors.IsNotFound when handling the returned error.
♻️ Duplicate comments (1)
internal/controller/bmc_controller.go (1)

292-293: Function name is misleading after refactor.

The function name createDNSRecordForServer implies server-specific DNS records, but the server parameter was removed and the function creates records based solely on BMC data. Rename for clarity.

📝 Suggested rename
-// createDNSRecordForServer creates a DNS record resource from a YAML template loaded from ConfigMap
-func (r *BMCReconciler) createDNSRecordForServer(ctx context.Context, log logr.Logger, bmcObj *metalv1alpha1.BMC) error {
+// createDNSRecordForBMC creates a DNS record resource from a YAML template
+func (r *BMCReconciler) createDNSRecordForBMC(ctx context.Context, log logr.Logger, bmcObj *metalv1alpha1.BMC) error {
🧹 Nitpick comments (1)
internal/controller/bmc_controller.go (1)

303-307: Consider caching the parsed template.

The template is parsed on every reconciliation call. Since r.DNSRecordTemplate doesn't change during runtime, parsing once at startup (or lazily on first use with sync.Once) would be more efficient.

♻️ Example: Parse template once

Add a field to BMCReconciler:

type BMCReconciler struct {
    // ...
    DNSRecordTemplate       string
    dnsRecordTemplateParsed *template.Template
    templateOnce            sync.Once
}

Then in the method:

r.templateOnce.Do(func() {
    r.dnsRecordTemplateParsed, _ = template.New("dnsRecord").Parse(r.DNSRecordTemplate)
})
if r.dnsRecordTemplateParsed == nil {
    return fmt.Errorf("DNS record template not parsed")
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1314c9d and 873f479.

📒 Files selected for processing (1)
  • internal/controller/bmc_controller.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Code must follow standard Go formatting and idioms; use clear, explicit error handling with minimal global state
Use small, focused functions; introduce interfaces only when justified

Files:

  • internal/controller/bmc_controller.go
🧠 Learnings (15)
📓 Common learnings
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.
📚 Learning: 2026-01-15T14:28:36.694Z
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Flag non-idempotent reconcile logic, Spec mutations during reconciliation, and custom patterns duplicating standard Kubernetes behavior

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status updates must be performed via `Status().Update()` or `Status().Patch()` and separated from spec mutations

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Avoid silent failures; ensure logging includes sufficient context (namespaced name, resource identifiers)

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use `client.Client`, `controllerutil.CreateOrUpdate`, and OwnerReferences from controller-runtime rather than direct API calls

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Use structured logging with controller-runtime logger; avoid excessive log noise in hot paths

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Follow Kubernetes API and controller patterns rather than inventing new abstractions

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use retries via requeue or error return in controllers, not explicit loops

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Include relevant identifiers in logs; emit events for meaningful user-facing state changes

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Errors must be propagated correctly to trigger retries; distinguish between transient and permanent errors where possible

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Prevent hot loops caused by status-only changes triggering reconciliation

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Reconciliation must be based on current cluster state, not assumptions; handle NotFound errors correctly and gracefully manage partial failures

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status must reflect observed state, not desired state; conditions must be updated consistently and deterministically

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-14T19:27:09.972Z
Learnt from: Nuckal777
Repo: ironcore-dev/metal-operator PR: 471
File: internal/controller/bmcsettingsset_controller.go:233-236
Timestamp: 2026-01-14T19:27:09.972Z
Learning: In the ironcore-dev/metal-operator repository, BMCSettings.Spec.BMCRef is marked with +required kubebuilder marker, ensuring API-level validation that prevents nil values; defensive nil checks are not needed when iterating BMCSettings from List operations.

Applied to files:

  • internal/controller/bmc_controller.go
🧬 Code graph analysis (1)
internal/controller/bmc_controller.go (1)
api/v1alpha1/bmc_types.go (3)
  • BMCSpec (25-68)
  • BMCStatus (169-222)
  • BMC (252-258)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run linter
  • GitHub Check: Run test chart
  • GitHub Check: Run tests
  • GitHub Check: Run test e2e
🔇 Additional comments (2)
internal/controller/bmc_controller.go (2)

7-18: LGTM on imports.

The new imports are appropriate for the DNS record templating feature: bytes for buffered rendering, text/template for YAML templating, and unstructured/yaml for dynamic Kubernetes resource handling.


320-326: CreateOrPatch mutation now correctly sets owner reference.

The mutation function properly sets the controller reference, ensuring garbage collection works when the BMC is deleted. This addresses the previous review feedback.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +320 to +322
opResult, err := controllerutil.CreateOrPatch(ctx, r.Client, dnsRecord, func() error {
return controllerutil.SetControllerReference(bmcObj, dnsRecord, r.Scheme)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this doesn't work for patching an existing record, due to CreateOrPatch() comparing the return value of the mutateFn with object passed, which produces no diff, if the owner isn't changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok I think SSA should be the easier way to solve it

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@internal/controller/bmc_controller.go`:
- Around line 282-334: The template rendering currently silently substitutes
missing fields with "<no value>"; update the template creation in
createDNSRecord by calling template.New("dnsRecord").Option("missingkey=error")
before Parse (so the template will error on missing keys), handle and propagate
that parse error (same fmt.Errorf pattern) and ensure the Execute error path
already present will now surface missing-key issues for the DNSRecord template.
♻️ Duplicate comments (1)
internal/controller/bmc_controller.go (1)

70-72: Fix DNSRecordTemplate comment to match actual semantics.

The field stores template content, not a file path (Line 70). This doc mismatch is confusing.

✏️ Proposed fix
-	// DNSRecordTemplatePath is the path to the file containing the DNSRecord template.
+	// DNSRecordTemplate is the DNS record YAML template content.
 	DNSRecordTemplate string
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c98191e and 19d904a.

📒 Files selected for processing (1)
  • internal/controller/bmc_controller.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Code must follow standard Go formatting and idioms; use clear, explicit error handling with minimal global state
Use small, focused functions; introduce interfaces only when justified

Files:

  • internal/controller/bmc_controller.go
🧠 Learnings (15)
📓 Common learnings
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.
📚 Learning: 2026-01-15T14:28:36.694Z
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Flag non-idempotent reconcile logic, Spec mutations during reconciliation, and custom patterns duplicating standard Kubernetes behavior

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status updates must be performed via `Status().Update()` or `Status().Patch()` and separated from spec mutations

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Avoid silent failures; ensure logging includes sufficient context (namespaced name, resource identifiers)

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use `client.Client`, `controllerutil.CreateOrUpdate`, and OwnerReferences from controller-runtime rather than direct API calls

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Use structured logging with controller-runtime logger; avoid excessive log noise in hot paths

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Follow Kubernetes API and controller patterns rather than inventing new abstractions

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use retries via requeue or error return in controllers, not explicit loops

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Include relevant identifiers in logs; emit events for meaningful user-facing state changes

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Errors must be propagated correctly to trigger retries; distinguish between transient and permanent errors where possible

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Prevent hot loops caused by status-only changes triggering reconciliation

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Reconciliation must be based on current cluster state, not assumptions; handle NotFound errors correctly and gracefully manage partial failures

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status must reflect observed state, not desired state; conditions must be updated consistently and deterministically

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-14T19:27:09.972Z
Learnt from: Nuckal777
Repo: ironcore-dev/metal-operator PR: 471
File: internal/controller/bmcsettingsset_controller.go:233-236
Timestamp: 2026-01-14T19:27:09.972Z
Learning: In the ironcore-dev/metal-operator repository, BMCSettings.Spec.BMCRef is marked with +required kubebuilder marker, ensuring API-level validation that prevents nil values; defensive nil checks are not needed when iterating BMCSettings from List operations.

Applied to files:

  • internal/controller/bmc_controller.go
🧬 Code graph analysis (1)
internal/controller/bmc_controller.go (2)
api/v1alpha1/bmc_types.go (3)
  • BMCSpec (25-68)
  • BMCStatus (169-222)
  • BMC (252-258)
bmc/bmc.go (1)
  • BMC (36-123)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run tests
  • GitHub Check: Run test chart
  • GitHub Check: Run test e2e
  • GitHub Check: Run linter
🔇 Additional comments (2)
internal/controller/bmc_controller.go (2)

6-19: No review comment for this hunk.


176-181: LGTM: DNS record creation is correctly gated by configuration.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@internal/controller/bmc_controller.go`:
- Around line 176-181: Update the comment and simplify error handling around DNS
record creation: change the comment above the block referencing
r.ManagerNamespace and r.DNSRecordTemplate to say that a DNS record will be
created when the template is configured (not "template path"), and remove the
unnecessary apierrors.IsNotFound(err) check from the if that calls
r.createDNSRecord(ctx, log, bmcObj) so any error returned is propagated (leave
the existing fmt.Errorf wrapping intact); refer to r.ManagerNamespace,
r.DNSRecordTemplate, and createDNSRecord to locate the code to edit.
♻️ Duplicate comments (1)
internal/controller/bmc_controller.go (1)

70-72: Comment doesn't match field semantics.

The comment says "path to the file" but the field DNSRecordTemplate stores the template content directly (used at line 302 in Parse(r.DNSRecordTemplate)), not a file path.

📝 Suggested fix
-	// DNSRecordTemplatePath is the path to the file containing the DNSRecord template.
+	// DNSRecordTemplate is the DNS record YAML template content.
 	DNSRecordTemplate string
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19d904a and 562ddd1.

📒 Files selected for processing (1)
  • internal/controller/bmc_controller.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Code must follow standard Go formatting and idioms; use clear, explicit error handling with minimal global state
Use small, focused functions; introduce interfaces only when justified

Files:

  • internal/controller/bmc_controller.go
🧠 Learnings (15)
📓 Common learnings
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.
📚 Learning: 2026-01-15T14:28:36.694Z
Learnt from: afritzler
Repo: ironcore-dev/metal-operator PR: 546
File: test/data/dns_record_template.yaml:1-16
Timestamp: 2026-01-15T14:28:36.694Z
Learning: In unit tests for the metal-operator BMC controller, when testing DNS record creation functionality without real DNS controller CRDs installed, a ConfigMap is used as a fallback/mock object in the test template (test/data/dns_record_template.yaml) instead of an actual DNSRecord resource.

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Flag non-idempotent reconcile logic, Spec mutations during reconciliation, and custom patterns duplicating standard Kubernetes behavior

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status updates must be performed via `Status().Update()` or `Status().Patch()` and separated from spec mutations

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Avoid silent failures; ensure logging includes sufficient context (namespaced name, resource identifiers)

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use `client.Client`, `controllerutil.CreateOrUpdate`, and OwnerReferences from controller-runtime rather than direct API calls

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Use structured logging with controller-runtime logger; avoid excessive log noise in hot paths

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Follow Kubernetes API and controller patterns rather than inventing new abstractions

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Use retries via requeue or error return in controllers, not explicit loops

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*.go : Include relevant identifiers in logs; emit events for meaningful user-facing state changes

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Errors must be propagated correctly to trigger retries; distinguish between transient and permanent errors where possible

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Prevent hot loops caused by status-only changes triggering reconciliation

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Reconciliation must be based on current cluster state, not assumptions; handle NotFound errors correctly and gracefully manage partial failures

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-09T15:31:43.862Z
Learnt from: CR
Repo: ironcore-dev/metal-operator PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T15:31:43.862Z
Learning: Applies to **/controllers/**/*_controller.go : Status must reflect observed state, not desired state; conditions must be updated consistently and deterministically

Applied to files:

  • internal/controller/bmc_controller.go
📚 Learning: 2026-01-14T19:27:09.972Z
Learnt from: Nuckal777
Repo: ironcore-dev/metal-operator PR: 471
File: internal/controller/bmcsettingsset_controller.go:233-236
Timestamp: 2026-01-14T19:27:09.972Z
Learning: In the ironcore-dev/metal-operator repository, BMCSettings.Spec.BMCRef is marked with +required kubebuilder marker, ensuring API-level validation that prevents nil values; defensive nil checks are not needed when iterating BMCSettings from List operations.

Applied to files:

  • internal/controller/bmc_controller.go
🧬 Code graph analysis (1)
internal/controller/bmc_controller.go (1)
api/v1alpha1/bmc_types.go (3)
  • BMCSpec (25-68)
  • BMCStatus (169-222)
  • BMC (252-258)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run test e2e
  • GitHub Check: Run test chart
  • GitHub Check: Run tests
🔇 Additional comments (3)
internal/controller/bmc_controller.go (3)

7-18: LGTM!

The new imports are appropriate for the DNS record templating functionality: bytes for buffering template output, text/template for parsing, unstructured for dynamic Kubernetes objects, and yaml for unmarshaling.


282-289: LGTM!

The DNSRecordTemplateData struct with embedded BMCSpec and BMCStatus provides a clean template interface, allowing direct field access (e.g., {{.IP}}, {{.Hostname}}) rather than nested paths. The field names between spec and status don't collide based on the type definitions.


291-336: Implementation is correct; fieldOwner is properly defined and accessible.

The SSA-based approach with client.Apply and ForceOwnership is the correct pattern for declarative resource management. The implementation includes solid defensive practices:

  • Option("missingkey=error") for fail-fast on template issues
  • GVK and name validation before applying
  • Proper controller reference for garbage collection
  • fieldOwner correctly scoped in the controller package

The template is parsed on every reconciliation, which is acceptable for current performance needs.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@stefanhipfel stefanhipfel merged commit d767521 into main Jan 19, 2026
15 of 16 checks passed
@stefanhipfel stefanhipfel deleted the dnsRecord branch January 19, 2026 09:54
@github-project-automation github-project-automation bot moved this to Done in Roadmap Jan 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-change area/metal-automation documentation Improvements or additions to documentation enhancement New feature or request size/L

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants