Skip to content

HYPERFLEET-1158 - feat: config-driven entity registration via LoadDescriptors#267

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift-hyperfleet:mainfrom
tirthct:hyperfleet-1158
Jul 2, 2026
Merged

HYPERFLEET-1158 - feat: config-driven entity registration via LoadDescriptors#267
openshift-merge-bot[bot] merged 1 commit into
openshift-hyperfleet:mainfrom
tirthct:hyperfleet-1158

Conversation

@tirthct

@tirthct tirthct commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves generic resource entity registration (Channel, Version, WifConfig) from
compile-time Go plugin init() functions to the application YAML config. Adding a
new generic entity type now requires a config entry, a matching OpenAPI spec schema,
and a redeploy — no new Go plugin or route wiring.

Cluster and NodePool remain on their legacy typed plugins and are unchanged.

What changed

  • entities config sectionApplicationConfig unmarshals entities: YAML
    entries directly into []registry.EntityDescriptor via mapstructure tags
  • LoadDescriptors + Validate + ValidateSchemas — called in
    Env.Initialize() before routes are built; panics on unknown parent_kind,
    duplicate plural, missing OpenAPI spec schemas, and invalid reference constraints
  • Centralized routesplugins/entities/plugin.go replaces per-entity plugins;
    iterates registry.All() and auto-generates top-level routes for root entities and
    nested routes for child entities
  • Schema validationbuildSchemasMap() now errors (not warns) when a registered
    entity's spec_schema_name is missing from the OpenAPI spec
  • ReferenceDescriptor — added to EntityDescriptor so references config
    sub-field is parseable (implementation in HYPERFLEET-1156)
  • DumpConfig — shows entity count and kind names in startup logs
  • Helm — default values.yaml includes Channel/Version/WifConfig; configmap.yaml
    templates all entity fields explicitly; values.schema.json validates entity structure

Deleted

  • plugins/channels/plugin.go
  • plugins/versions/plugin.go
  • plugins/wifconfigs/plugin.go

Design notes

Before After
plugins/channels, plugins/versions, plugins/wifconfigs register descriptors + routes in init() Single plugins/entities plugin; descriptors from YAML
SpecSchemaName / SearchDisallowedFields stored but unused at validation/search layer SpecSchemaName validated at startup via ValidateSchemas; SearchDisallowedFields parsed but enforcement deferred (see docs/search-disallowed-fields-gap.md)
Missing OpenAPI schema logged as warning and skipped Missing schema causes startup error

Example config:

entities:
  - kind: Channel
    plural: channels
    spec_schema_name: ChannelSpec
    search_disallowed_fields: [spec]

  - kind: Version
    plural: versions
    parent_kind: Channel
    on_parent_delete: restrict
    spec_schema_name: VersionSpec
    search_disallowed_fields: [spec]

Out of scope

  • SearchDisallowedFields enforcement at the search layer — pre-existing gap documented
    in docs/search-disallowed-fields-gap.md, requires per-query ListArguments plumbing
  • Cluster/NodePool migration to generic layer (HYPERFLEET-1159)
  • Resource references implementation (HYPERFLEET-1156)

Test plan

  • make verify-all — 1311 tests pass
  • make test-helm — chart lint + template validation
  • Unit tests: LoadDescriptors, ValidateSchemas, RegisterEntityRoutes,
    reference validation in Validate()
  • make test-integration — channels/versions/wifconfigs CRUD with config-driven
    registration (requires database)

@openshift-ci openshift-ci Bot requested review from rh-amarin and vkareh July 1, 2026 03:28
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: a2eb2b3a-44c1-4b9b-8358-8145d934444a

📥 Commits

Reviewing files that changed from the base of the PR and between eac8644 and da302d4.

📒 Files selected for processing (26)
  • CLAUDE.md
  • charts/CLAUDE.md
  • charts/README.md
  • charts/templates/configmap.yaml
  • charts/values.schema.json
  • charts/values.yaml
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • configs/config.yaml.example
  • pkg/config/config.go
  • pkg/config/dump.go
  • pkg/config/loader.go
  • pkg/registry/descriptor.go
  • pkg/registry/registry.go
  • pkg/registry/registry_test.go
  • pkg/validators/schema_validator.go
  • pkg/validators/schema_validator_test.go
  • plugins/CLAUDE.md
  • plugins/channels/plugin.go
  • plugins/entities/plugin.go
  • plugins/entities/plugin_test.go
  • plugins/versions/plugin.go
  • plugins/wifconfigs/plugin.go
  • test/helper.go
  • test/integration/versions_test.go
  • test/integration/wifconfigs_test.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift-hyperfleet/architecture (manual)
  • openshift-hyperfleet/hyperfleet-api (manual)
  • openshift-hyperfleet/hyperfleet-sentinel (manual)
  • openshift-hyperfleet/hyperfleet-adapter (manual)
  • openshift-hyperfleet/hyperfleet-broker (manual)
💤 Files with no reviewable changes (6)
  • test/integration/wifconfigs_test.go
  • plugins/channels/plugin.go
  • pkg/validators/schema_validator.go
  • plugins/wifconfigs/plugin.go
  • test/integration/versions_test.go
  • plugins/versions/plugin.go
✅ Files skipped from review due to trivial changes (6)
  • charts/CLAUDE.md
  • configs/config.yaml.example
  • pkg/config/loader.go
  • CLAUDE.md
  • charts/README.md
  • plugins/CLAUDE.md
🚧 Files skipped from review as they are similar to previous changes (13)
  • charts/templates/configmap.yaml
  • cmd/hyperfleet-api/main.go
  • charts/values.schema.json
  • pkg/config/config.go
  • plugins/entities/plugin.go
  • charts/values.yaml
  • pkg/config/dump.go
  • pkg/registry/registry.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • plugins/entities/plugin_test.go
  • pkg/registry/registry_test.go
  • test/helper.go
  • pkg/registry/descriptor.go

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features
    • Added startup-configured entity types via YAML/Helm (config.entities) with auto-generated REST endpoints.
    • Supports parent/child relationships and reference metadata with validation (including cardinality constraints).
  • Documentation
    • Updated configuration docs, Helm values, and schemas to describe config.entities, defaults, and behavior when set empty.
    • Refreshed plugin documentation for the new registration approach.
  • Tests
    • Added coverage for descriptor loading, validation (references), and entity route registration (top-level vs nested).

Walkthrough

Generic entity descriptors move to config.yaml/Helm values, are loaded into registry at startup, validated for reference consistency, and used by plugins/entities to generate top-level and nested CRUD routes. ApplicationConfig now carries Entities, and tests were updated to load default descriptors through the test helper. Legacy typed plugins for Cluster and NodePool remain separate.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant main as main.go
  participant runServe as servecmd.runServe
  participant registry as registry
  participant entities as plugins/entities
  participant router as apiV1Router

  main->>runServe: start
  runServe->>registry: LoadDescriptors(cfg.Entities)
  runServe->>registry: Validate()
  entities->>registry: All()
  entities->>router: RegisterEntityRoutes()
  router-->>entities: CRUD routes wired
Loading

Related Issues: None provided.
Related PRs: None provided.
Suggested labels: area/config, area/registry, area/routes, breaking-change
Suggested reviewers: Not determinable from the diff.

CWE-284: Route shape now depends on config.entities and ParentKind; review any values injection path for unintended route exposure.
CWE-20: Validate() still panics on invalid descriptors; keep this startup-only to avoid runtime DoS.
Supply chain: Removed side-effect plugin imports and replaced them with plugins/entities; verify no other import path can re-enable the deleted init-based registration.

Poem:
YAML names the kinds,
registry checks the links,
routes grow from descriptors.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Sec-02: Secrets In Log Output ❌ Error FAIL: logger.Info(ctx, config.DumpConfig(...)) logs Password: in startup output; CWE-532 sensitive info appears in a non-test log path. Remove the password field from DumpConfig or stop logging that config blob; keep only redacted, non-sensitive summaries.
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.
No Injection Vectors ⚠️ Warning Config-driven EntityDescriptor.Kind now reaches ResourceService.List/ListByOwner kindFilter fmt.Sprintf SQL; registry.LoadDescriptors accepts arbitrary Kind/Plural, so CWE-89 is reachable from YAML. Parameterize the kind/owner filters (or build the AST with bound values) and add strict regex validation for Kind/Plural before LoadDescriptors.
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: config-driven entity registration via LoadDescriptors.
Description check ✅ Passed The description is directly related and accurately describes the config-driven entity registration changes.
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.
No Hardcoded Secrets ✅ Passed No hardcoded credentials in modified non-test code; only test fixture PEM/password and example placeholders, which the check excludes. CWE-798 not present.
No Weak Cryptography ✅ Passed No crypto/md5, crypto/des, crypto/rc4, SHA1-for-security, ECB, custom crypto, or secret-comparison anti-patterns appear in the PR changes (CWE-327).
No Privileged Containers ✅ Passed No new privileged settings in touched Helm/Docker files; charts set runAsNonRoot=true and allowPrivilegeEscalation=false, and Dockerfile USER root is justified before switching to 1001.
No Pii Or Sensitive Data In Logs ✅ Passed No CWE-532 issue found: new startup config logging adds only entity count/kinds; DB username/password are redacted and no PII, session, or body logging was added.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

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

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

🧹 Nitpick comments (4)
pkg/registry/registry.go (2)

86-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Decompose Validate() — now over 50 lines with 7+ branches.

Adding the reference checks pushed Validate() well past the size/branching threshold. Extract the reference-integrity checks (target-kind, duplicate ref_type, max<min) into a helper, e.g. validateReferences(d EntityDescriptor).

As per path instructions, "Functions >50 lines or >5 branching paths — flag for decomposition."

♻️ Suggested decomposition
+func validateReferences(d EntityDescriptor) {
+	refTypes := make(map[string]bool, len(d.References))
+	for _, ref := range d.References {
+		if _, ok := descriptors[ref.TargetKind]; !ok {
+			panic(fmt.Sprintf(
+				"entity %q: reference %q targets unregistered kind %q",
+				d.Kind, ref.RefType, ref.TargetKind,
+			))
+		}
+		if refTypes[ref.RefType] {
+			panic(fmt.Sprintf(
+				"entity %q: duplicate ref_type %q in references",
+				d.Kind, ref.RefType,
+			))
+		}
+		refTypes[ref.RefType] = true
+		if ref.Max > 0 && ref.Max < ref.Min {
+			panic(fmt.Sprintf(
+				"entity %q: reference %q has max (%d) < min (%d)",
+				d.Kind, ref.RefType, ref.Max, ref.Min,
+			))
+		}
+	}
+}
+
 func Validate() {
 	plurals := make(map[string]string, len(descriptors))
 	for _, d := range descriptors {
 		...
 		plurals[d.Plural] = d.Kind
-		// Track seen RefType values ...
-		refTypes := make(map[string]bool, len(d.References))
-		for _, ref := range d.References { ... }
+		validateReferences(d)
 	}
 }
🤖 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 `@pkg/registry/registry.go` around lines 86 - 138, The Validate() function now
exceeds the size and branching threshold due to inline reference-integrity
logic. Refactor pkg/registry/registry.go by extracting the per-entity reference
checks from Validate() into a helper such as validateReferences(d
EntityDescriptor), and move the target-kind lookup, duplicate ref_type
detection, and max<min validation there while keeping Validate() focused on the
top-level descriptor checks.

Source: Path instructions


140-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate "schema exists" check vs pkg/validators/schema_validator.go buildSchemasMap.

Both ValidateSchemas here and buildSchemasMap load the OpenAPI doc and check d.SpecSchemaName against doc.Components.Schemas for every registered descriptor. The messages have already diverged ("not found in OpenAPI spec at %s" here vs "not found in OpenAPI spec" there) — evidence this is going to drift further. Extract a shared checkSchemaPresence(doc, descriptors) used by both, or drop one of the two enforcement points if they always run in the same startup path.

🤖 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 `@pkg/registry/registry.go` around lines 140 - 166, The OpenAPI schema
existence validation is duplicated between ValidateSchemas and buildSchemasMap,
which will keep drifting in behavior and error messaging. Refactor the shared
schema-presence logic into a common helper that checks each descriptor’s
SpecSchemaName against doc.Components.Schemas and is used by both
ValidateSchemas and pkg/validators/schema_validator.go’s buildSchemasMap, or
remove one enforcement point if startup already guarantees the other runs.
pkg/validators/schema_validator_test.go (1)

175-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate test + misleading name.

TestValidate_ErrorsWhenRegisteredEntitySchemaNotLoaded (248-287) is functionally identical to TestNewSchemaValidator_RegisteredEntityMissingOpenAPISchema_Errors (175-215) — same registry setup, same fixture, same assertions, and it never actually calls Validate() (construction fails in NewSchemaValidator first, so the name misrepresents what's tested). Consolidate into one test, or convert the four "missing schema" variants (126-152, 175-215, 217-246, 248-287) into a single table-driven test with t.Run().

As per path instructions: "Table-driven tests with t.Run() for repeated patterns" and "Test names describe the scenario, not the implementation."

Also applies to: 248-287

🤖 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 `@pkg/validators/schema_validator_test.go` around lines 175 - 215, Remove the
duplicate “missing schema” test coverage by consolidating
TestNewSchemaValidator_RegisteredEntityMissingOpenAPISchema_Errors with
TestValidate_ErrorsWhenRegisteredEntitySchemaNotLoaded, since they use the same
registry setup, fixture, and assertions. Rename or restructure the remaining
test so the name matches the actual behavior in NewSchemaValidator (construction
failing before Validate is reached), and consider folding the four
schema-missing cases into a single table-driven test with t.Run() to cover each
scenario once.

Source: Path instructions

plugins/entities/plugin_test.go (1)

33-59: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing test for unresolvable ParentKind — the exact panic path in plugin.go.

Add a case registering a child descriptor whose ParentKind has no matching registered Kind, and assert the documented behavior (Expect(func(){...}).To(Panic()) if panic is intended, or an error return if MustGet is replaced with a guarded lookup per the companion comment on plugin.go). Right now this failure mode is untested and undocumented.

As per path instructions for **/*_test.go: "Error paths SHOULD be tested, not just happy paths."

🤖 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 `@plugins/entities/plugin_test.go` around lines 33 - 59, Add an error-path test
in RegisterEntityRoutes coverage for an unresolvable ParentKind: register a
child EntityDescriptor whose ParentKind has no matching registered Kind, then
assert the behavior at the plugin.go lookup path (either a panic via
Expect(...).To(Panic()) if RegisterEntityRoutes/GetParentKind still uses
MustGet, or an error result if that lookup is changed to be guarded). Place the
new case alongside TestRegisterEntityRoutes_ChildEntity_NestedOnly so the
failure mode is explicitly covered with the existing
registry/RegisterEntityRoutes helpers.

Source: Path instructions

🤖 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 `@charts/templates/configmap.yaml`:
- Around line 145-153: The entities block in the configmap template is rendering
several string fields without quoting, which can produce malformed YAML or type
coercion when values are overridden. Update the entity entries in the template
to pipe `kind`, `plural`, `spec_schema_name`, `parent_kind`, `on_parent_delete`,
`ref_type`, and `target_kind` through `| quote`, matching the existing pattern
used for other settings, so `LoadDescriptors` receives valid string values.

In `@cmd/hyperfleet-api/servecmd/cmd.go`:
- Line 22: The registry validation path in runServe currently lets
`registry.LoadDescriptors`, `Validate`, and `ValidateSchemas` panic, which
bypasses the existing fail-fast logging flow. Add a local `recover()` around
those calls in `cmd/hyperfleet-api/servecmd/cmd.go` and convert any panic into a
`logger.WithError(...).Error(...)` followed by `os.Exit(1)`, matching the
handling used for config load and `Initialize()`. Use the `runServe` function
and the `registry` package calls as the main points to update, and keep the
startup behavior consistent with the rest of the command.

In `@plugins/CLAUDE.md`:
- Around line 3-25: The registry reference path in the docs is stale and should
point to the actual loader location used by this PR. Update the “Reference”
entry under the config-driven entities section to use the correct registry
symbol location, `pkg/registry/registry.go`, so it matches the implementation.
Keep the surrounding guidance intact and ensure any other references to the
loader path in this doc are consistent with the same symbol.

---

Nitpick comments:
In `@pkg/registry/registry.go`:
- Around line 86-138: The Validate() function now exceeds the size and branching
threshold due to inline reference-integrity logic. Refactor
pkg/registry/registry.go by extracting the per-entity reference checks from
Validate() into a helper such as validateReferences(d EntityDescriptor), and
move the target-kind lookup, duplicate ref_type detection, and max<min
validation there while keeping Validate() focused on the top-level descriptor
checks.
- Around line 140-166: The OpenAPI schema existence validation is duplicated
between ValidateSchemas and buildSchemasMap, which will keep drifting in
behavior and error messaging. Refactor the shared schema-presence logic into a
common helper that checks each descriptor’s SpecSchemaName against
doc.Components.Schemas and is used by both ValidateSchemas and
pkg/validators/schema_validator.go’s buildSchemasMap, or remove one enforcement
point if startup already guarantees the other runs.

In `@pkg/validators/schema_validator_test.go`:
- Around line 175-215: Remove the duplicate “missing schema” test coverage by
consolidating TestNewSchemaValidator_RegisteredEntityMissingOpenAPISchema_Errors
with TestValidate_ErrorsWhenRegisteredEntitySchemaNotLoaded, since they use the
same registry setup, fixture, and assertions. Rename or restructure the
remaining test so the name matches the actual behavior in NewSchemaValidator
(construction failing before Validate is reached), and consider folding the four
schema-missing cases into a single table-driven test with t.Run() to cover each
scenario once.

In `@plugins/entities/plugin_test.go`:
- Around line 33-59: Add an error-path test in RegisterEntityRoutes coverage for
an unresolvable ParentKind: register a child EntityDescriptor whose ParentKind
has no matching registered Kind, then assert the behavior at the plugin.go
lookup path (either a panic via Expect(...).To(Panic()) if
RegisterEntityRoutes/GetParentKind still uses MustGet, or an error result if
that lookup is changed to be guarded). Place the new case alongside
TestRegisterEntityRoutes_ChildEntity_NestedOnly so the failure mode is
explicitly covered with the existing registry/RegisterEntityRoutes helpers.
🪄 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: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: b4ddec05-b9c5-42a8-85b5-5df773428538

📥 Commits

Reviewing files that changed from the base of the PR and between 8f20481 and c8cc63d.

📒 Files selected for processing (26)
  • CLAUDE.md
  • charts/CLAUDE.md
  • charts/README.md
  • charts/templates/configmap.yaml
  • charts/values.schema.json
  • charts/values.yaml
  • cmd/hyperfleet-api/main.go
  • cmd/hyperfleet-api/servecmd/cmd.go
  • configs/config.yaml.example
  • pkg/config/config.go
  • pkg/config/dump.go
  • pkg/config/loader.go
  • pkg/registry/descriptor.go
  • pkg/registry/registry.go
  • pkg/registry/registry_test.go
  • pkg/validators/schema_validator.go
  • pkg/validators/schema_validator_test.go
  • plugins/CLAUDE.md
  • plugins/channels/plugin.go
  • plugins/entities/plugin.go
  • plugins/entities/plugin_test.go
  • plugins/versions/plugin.go
  • plugins/wifconfigs/plugin.go
  • test/helper.go
  • test/integration/versions_test.go
  • test/integration/wifconfigs_test.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift-hyperfleet/architecture (manual)
  • openshift-hyperfleet/hyperfleet-api (manual)
  • openshift-hyperfleet/hyperfleet-sentinel (manual)
  • openshift-hyperfleet/hyperfleet-adapter (manual)
  • openshift-hyperfleet/hyperfleet-broker (manual)
💤 Files with no reviewable changes (5)
  • plugins/wifconfigs/plugin.go
  • plugins/versions/plugin.go
  • plugins/channels/plugin.go
  • test/integration/wifconfigs_test.go
  • test/integration/versions_test.go

Comment on lines +145 to +153
- kind: {{ .kind }}
plural: {{ .plural }}
{{- if .parent_kind }}
parent_kind: {{ .parent_kind }}
{{- end }}
{{- if .on_parent_delete }}
on_parent_delete: {{ .on_parent_delete }}
{{- end }}
spec_schema_name: {{ .spec_schema_name }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Unquoted entity scalars — YAML injection/malformed-config risk (CWE-1284-adjacent).

Every other string value in this template is piped through | quote (see hostname, cert_file, issuer_url, dialect, level, etc.). The new entities block does not quote kind, plural, spec_schema_name, parent_kind, on_parent_delete, ref_type, target_kind. Since config.entities is documented as overridable via --set-json, an operator-supplied value containing :, or a bare yes/no/null-like token, will corrupt the rendered YAML or get silently reinterpreted as a non-string type, breaking LoadDescriptors at startup.

🔒 Proposed fix
       - kind: {{ .kind }}
-        plural: {{ .plural }}
+      - kind: {{ .kind | quote }}
+        plural: {{ .plural | quote }}
 {{- if .parent_kind }}
-        parent_kind: {{ .parent_kind }}
+        parent_kind: {{ .parent_kind | quote }}
 {{- end }}
 {{- if .on_parent_delete }}
-        on_parent_delete: {{ .on_parent_delete }}
+        on_parent_delete: {{ .on_parent_delete | quote }}
 {{- end }}
-        spec_schema_name: {{ .spec_schema_name }}
+        spec_schema_name: {{ .spec_schema_name | quote }}
@@
-          - ref_type: {{ .ref_type }}
-            target_kind: {{ .target_kind }}
+          - ref_type: {{ .ref_type | quote }}
+            target_kind: {{ .target_kind | quote }}

Also applies to: 169-170

🤖 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 `@charts/templates/configmap.yaml` around lines 145 - 153, The entities block
in the configmap template is rendering several string fields without quoting,
which can produce malformed YAML or type coercion when values are overridden.
Update the entity entries in the template to pipe `kind`, `plural`,
`spec_schema_name`, `parent_kind`, `on_parent_delete`, `ref_type`, and
`target_kind` through `| quote`, matching the existing pattern used for other
settings, so `LoadDescriptors` receives valid string values.

"github.com/openshift-hyperfleet/hyperfleet-api/pkg/health"
"github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
"github.com/openshift-hyperfleet/hyperfleet-api/pkg/metrics"
"github.com/openshift-hyperfleet/hyperfleet-api/pkg/registry"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Panic-based startup validation breaks the established fail-fast pattern (CWE-248).

registry.LoadDescriptors/Validate/ValidateSchemas all panic() on bad config (per pkg/registry/registry.go), but every other failure path in this same function (config load, Initialize()) uses logger.WithError(ctx, err).Error(...) + os.Exit(1). Since this call happens before initLogger() (line 74), a malformed config.entities (e.g. bad Helm --set-json) crashes with a raw Go stack trace on stderr instead of the app's structured logger/format — inconsistent operator experience and harder to correlate in log aggregation. Wrap the calls with a local recover() and route through logger.WithError(...).Error(...) + os.Exit(1) for parity with the rest of runServe, or have the registry package return error instead of panicking.

🛡️ Proposed fix
-	registry.LoadDescriptors(cfg.Entities)
-	registry.Validate()
-	registry.ValidateSchemas(cfg.Server.OpenAPISchemaPath)
+	func() {
+		defer func() {
+			if r := recover(); r != nil {
+				logger.With(ctx, "panic", r).Error("Entity registry validation failed")
+				os.Exit(1)
+			}
+		}()
+		registry.LoadDescriptors(cfg.Entities)
+		registry.Validate()
+		registry.ValidateSchemas(cfg.Server.OpenAPISchemaPath)
+	}()

Also applies to: 59-65

🤖 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/hyperfleet-api/servecmd/cmd.go` at line 22, The registry validation path
in runServe currently lets `registry.LoadDescriptors`, `Validate`, and
`ValidateSchemas` panic, which bypasses the existing fail-fast logging flow. Add
a local `recover()` around those calls in `cmd/hyperfleet-api/servecmd/cmd.go`
and convert any panic into a `logger.WithError(...).Error(...)` followed by
`os.Exit(1)`, matching the handling used for config load and `Initialize()`. Use
the `runServe` function and the `registry` package calls as the main points to
update, and keep the startup behavior consistent with the rest of the command.

Comment thread plugins/CLAUDE.md
@hyperfleet-ci-bot

hyperfleet-ci-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

Risk Score: 5 — risk/high

Signal Detail Points
PR size 958 lines (>500) +2
Sensitive paths cmd/ +2
Test coverage Missing tests for: cmd/hyperfleet-api cmd/hyperfleet-api/servecmd pkg/config plugins/channels plugins/versions plugins/wifconfigs test +1

Computed by hyperfleet-risk-scorer

@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.

🧹 Nitpick comments (1)
pkg/registry/registry.go (1)

23-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Parameter shadows package-level descriptors map.

LoadDescriptors(descriptors []EntityDescriptor) shadows the package-level descriptors registry map used by Register, All, and Validate. Currently harmless since the body never touches the global inside this scope, but it's a footgun (CWE-710-style maintainability defect) for future edits that assume descriptors refers to the registry.

🧹 Proposed rename
-func LoadDescriptors(descriptors []EntityDescriptor) {
-	for _, d := range descriptors {
+func LoadDescriptors(entityDescriptors []EntityDescriptor) {
+	for _, d := range entityDescriptors {
 		Register(d)
 	}
 }
🤖 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 `@pkg/registry/registry.go` around lines 23 - 27, Rename the LoadDescriptors
parameter so it does not shadow the package-level descriptors registry map used
by Register, All, and Validate. Update the function signature and its local loop
variable references accordingly, keeping the behavior the same but using a
clearer name like a slice of entity descriptors to avoid future confusion.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@pkg/registry/registry.go`:
- Around line 23-27: Rename the LoadDescriptors parameter so it does not shadow
the package-level descriptors registry map used by Register, All, and Validate.
Update the function signature and its local loop variable references
accordingly, keeping the behavior the same but using a clearer name like a slice
of entity descriptors to avoid future confusion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 8d031c5e-f791-4664-af6d-f5559627bc0d

📥 Commits

Reviewing files that changed from the base of the PR and between 92bcc7e and eac8644.

📒 Files selected for processing (8)
  • charts/README.md
  • charts/values.yaml
  • cmd/hyperfleet-api/servecmd/cmd.go
  • pkg/registry/descriptor.go
  • pkg/registry/registry.go
  • pkg/registry/registry_test.go
  • pkg/validators/schema_validator_test.go
  • test/helper.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift-hyperfleet/architecture (manual)
  • openshift-hyperfleet/hyperfleet-api (manual)
  • openshift-hyperfleet/hyperfleet-sentinel (manual)
  • openshift-hyperfleet/hyperfleet-adapter (manual)
  • openshift-hyperfleet/hyperfleet-broker (manual)
💤 Files with no reviewable changes (2)
  • test/helper.go
  • cmd/hyperfleet-api/servecmd/cmd.go
✅ Files skipped from review due to trivial changes (2)
  • charts/README.md
  • pkg/validators/schema_validator_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/registry/descriptor.go
  • charts/values.yaml

@tirthct tirthct force-pushed the hyperfleet-1158 branch from eac8644 to da302d4 Compare July 2, 2026 05:04
@kuudori

kuudori commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@openshift-ci

openshift-ci Bot commented Jul 2, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: kuudori

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 label Jul 2, 2026
@openshift-merge-bot openshift-merge-bot Bot merged commit 4c4266f into openshift-hyperfleet:main Jul 2, 2026
10 checks passed
@tirthct tirthct deleted the hyperfleet-1158 branch July 2, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants