Skip to content

ROSAENG-61801: feat: enable api-management v2 - #284

Merged
openshift-merge-bot[bot] merged 14 commits into
openshift-online:mainfrom
cdoan1:ROSAENG-61801-passthrough-types-rework-v2
Aug 6, 2026
Merged

ROSAENG-61801: feat: enable api-management v2#284
openshift-merge-bot[bot] merged 14 commits into
openshift-online:mainfrom
cdoan1:ROSAENG-61801-passthrough-types-rework-v2

Conversation

@cdoan1

@cdoan1 cdoan1 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Description

Consolidate 61801, 61802, 61803, 61805, and conversion of openapi to go types in api/v1alpha1/public

  1. Passthrough type system — Restructured HyperShift passthrough types (HostedClusterSpecPassthrough, NodePoolSpecPassthrough, ClusterConfiguration) into api/v1alpha1/ as the single source of truth, with per-field
    visibility/write-mode/feature-gate markers.
  2. Envelope field enrichment — Added DisplayName, DeleteProtection, Properties, Tags, AccountID, InternalID to ClusterSpec; similar additions to NodePoolSpec. Hidden fields (+k8s:openapi-gen=false) and write-mode markers control what's exposed
    to customers.
  3. Codegen pipeline — Wired up the full hack/api-codegen/ pipeline:
    - codegen-registry — scans markers → field_metadata.go/.json
    - codegen-conversion — generates REST types in api/v1alpha1/public/ (visible fields only) and Project/Unproject JSON roundtrip functions in platform-api/pkg/conversion/
    - verify-conversion — CI target to detect stale generated code
  4. REST types — 26 generated files in api/v1alpha1/public/ (package public) containing customer-visible types with hidden/service-set fields stripped. These are importable by the SDK/clientset.
  5. Field validation middleware — platform-api/pkg/validation/field_validator.go enforces write-mode rules (immutable, mutable, service-set) using the field metadata registry.
  6. Operator fixes — JSON roundtrip conversion in hyperfleet-operator/internal/render/ to bridge passthrough types back to HyperShift types; CRD manifests regenerated; Containerfile GOCACHE fix.
  7. OpenAPI alignment — Regenerated openapi.yaml reflecting current marker state, openapi-merge tool added, release field made mutable.
  8. CI/build — Added hack/api-codegen to platform-api build context, updated Tekton pipelines, promoted openshift/api to direct dependency.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • CI/CD or tooling change

Testing

  • Unit tests pass (make test)
  • Integration tests pass (if applicable)
  • Manual verification completed

Checklist

  • My code follows the project's coding conventions
  • I have updated documentation as needed
  • I have added tests that prove my fix/feature works
  • All new and existing tests pass

Summary by CodeRabbit

  • New Features

    • Added configurable cluster and node pool fields, including display names, labels, tags, properties, expiration, deletion protection, and service-assigned identifiers.
    • Added configuration APIs for kubelet, machine, networking, authentication, and related cluster settings.
    • Added management cluster, placement, and manifest resource APIs.
    • Added feature-gate support for controlling API capabilities.
  • Validation

    • Create and update requests now return structured validation errors for restricted, immutable, or unavailable fields.
  • Documentation

    • Updated API documentation and OpenAPI schemas to reflect the new resources and fields.

cdoan1 and others added 2 commits August 5, 2026 10:34
…el api/

Module path changes from .../hyperfleet-operator/api to .../api.
Updates all go.mod replace directives, Go imports, Makefile targets,
Tekton pipelines, Containerfiles, and documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move passthrough types (HostedClusterSpecPassthrough, NodePoolSpecPassthrough)
and configuration types into api/v1alpha1/ as the single source of truth.
Replace raw HyperShift embeds with passthrough types in ClusterSpec and
NodePoolSpec, add envelope fields with per-field markers, and wire JSON
roundtrip conversion in the operator render code.

- Add envelope fields to ClusterSpec (DisplayName, DeleteProtection,
  Properties, Tags, AccountID, InternalID) and NodePoolSpec (DisplayName,
  AutoRepair, Labels, AccountID, InternalPoolID) with write-mode markers
- Change HostedCluster/NodePool field types to passthrough types
- Add JSON roundtrip conversion in render package for passthrough→HyperShift
- Add CRD validation bounds (MaxItems, MaxProperties) to satisfy CEL cost
- Add codegen pipeline files (registry, featuregate, conversion) to platform-api
- Update marker scanner to recognize *Passthrough root types
- Add hack/api-codegen replace directive to platform-api/go.mod
- Create api/v1alpha1/public/ placeholder for future generated types
- Regenerate deepcopy, CRDs; all tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Aug 5, 2026
@openshift-ci-robot

openshift-ci-robot commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

@cdoan1: This pull request references ROSAENG-61801 which is a valid jira issue.

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

Details

In response to this:

Description

5th pass, as api repo evolves

We have one Cluster object (same for NodePool) with fields that fall into different buckets:

Fields customers can see and edit (displayName, deleteProtection) [ mutable ]
Fields customers can see but not change after creation (fips, systemReserved) [ system-set ]
Fields customers can't see at all, platform-managed (accountId, creatorARN, internalId) [ openapi=false ]
Fields behind feature gates that only some customers can access

We need the platform-api to enforce all of this at runtime, and we need to generate a customer-facing OpenAPI spec (and eventually a typed Go SDK) that only exposes the visible fields. Internally, the operator and admin tooling need to see and set everything.

api/
├── v1alpha1/                        source of truth (all fields, all markers)
   ├── cluster.go
   ├── nodepool.go
   ├── hostedcluster_passthrough.go   generated mirror of HyperShift
   └── public/   generated (customer-visible only)
       ├── openapi.json                   OpenAPI spec (visible fields only)
       └── types.gen.go                   Go types generated from the spec

The pipeline:

api/v1alpha1/ (source of truth, all fields, all markers)
   |
   v  openapi-gen (skips +openapi-gen=false fields)
   |
api/public/openapi.json (customer-visible schema)
   |
   v  oapi-codegen
   |
api/public/types.gen.go (generated public Go types)

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • CI/CD or tooling change

Testing

  • Unit tests pass (make test)
  • Integration tests pass (if applicable)
  • Manual verification completed

Checklist

  • My code follows the project's coding conventions
  • I have updated documentation as needed
  • I have added tests that prove my fix/feature works
  • All new and existing tests pass

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

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The change moves API types into a standalone api module. It adds passthrough and configuration schemas, feature-gated validation, OpenAPI generation, conversion error handling, service-managed identifiers, and updated build and deployment wiring.

Changes

API contracts and code generation

Layer / File(s) Summary
Standalone API contracts
api/v1alpha1/*
Adds Kubernetes resources, passthrough specifications, configuration types, scheme registration, and explicit cluster and node pool fields.
Code generation and OpenAPI
hack/api-codegen/*, api/v1alpha1/public/openapi.yaml
Adds passthrough marker handling, schema generation and merging, registry metadata, and generated public schemas.
Feature gates and validation
platform-api/internal/codegen/featuregate/*, platform-api/pkg/validation/*
Adds feature-gate definitions and validation for service-managed, immutable, mutable, and gated fields.

Operator and platform integration

Layer / File(s) Summary
Passthrough rendering
hyperfleet-operator/internal/render/*, hyperfleet-operator/internal/controller/*
Converts passthrough specifications into HyperShift resources and propagates contextual rendering errors.
Platform conversion and handlers
platform-api/pkg/clients/hyperfleetdb/*, platform-api/pkg/handlers/*
Populates service-managed identifiers, validates create and update requests, and preserves platform-controlled fields.
Module and build wiring
.tekton/*, Makefile, */go.mod, */Containerfile, clientset/*, README.md, CLAUDE.md
Updates module paths, dependency prefetching, container builds, code-generation targets, clientset references, and documentation.
CRD schemas
hyperfleet-operator/config/crd/bases/*
Regenerates Cluster and NodePool schemas with passthrough fields, configuration changes, and revised validation metadata.

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


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
No-Weak-Crypto ❌ Error The PR adds DES-CBC3-SHA to the packaged Cluster TLS cipher enum; this is 3DES and violates the no-weak-crypto check. Remove DES-CBC3-SHA from the TLS cipher enum and regenerate the chart CRD from an approved cipher profile.
Docstring Coverage ⚠️ Warning Docstring coverage is 26.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Ai-Attribution ⚠️ Warning Seven PR commits identify Claude Opus 4.6 via Co-Authored-By; no Assisted-by or Generated-by trailer is present. Replace the AI Co-Authored-By trailers in the PR commits with the required Red Hat Assisted-by or Generated-by attribution.
✅ Passed checks (8 passed)
Check name Status Explanation
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.
Container-Privileges ✅ Passed PR additions contain no privileged:true, hostPID, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation:true; runtime images use UID 65534 and workloads enforce non-root execution.
No-Sensitive-Data-In-Logs ✅ Passed Added logs contain only codegen paths, schema names, counts, and field metadata; no passwords, tokens, API keys, PII, hostnames, or customer values are logged.
No-Hardcoded-Secrets ✅ Passed PR additions contain no API keys, tokens, passwords, private keys, or credential-bearing URLs; matches are schema references, placeholder certificate text, and Go checksums.
No-Injection-Vectors ✅ Passed The PR diff contains no SQL construction, eval/exec, pickle.loads, yaml.load, os.system, shell=True, or dangerouslySetInnerHTML usage.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the API management v2 changes, including the new API layout, passthrough types, and code-generation pipeline.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

The platform-api go.mod now has a replace directive for hack/api-codegen,
so the Containerfile and Tekton prefetch-input paths need to include it.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

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

⚠️ Outside diff range comments (1)
hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml (1)

1883-1889: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Restore the NodePool scaling conflict validation.

The schema now accepts both spec.nodePool.replicas and spec.nodePool.autoScaling. These fields define competing desired-size controls. Reject this combination at admission time.

Add the equivalent XValidation marker to the passthrough source or generator so regeneration preserves the CEL rule.

🤖 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 `@hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml` around
lines 1883 - 1889, The NodePool schema must reject configurations that set both
spec.nodePool.replicas and spec.nodePool.autoScaling. Add the equivalent
XValidation marker in the passthrough source or generator for the NodePool spec,
using a CEL rule that enforces these fields are not simultaneously present, so
regenerated CRDs retain the admission validation.
🧹 Nitpick comments (5)
platform-api/internal/codegen/featuregate/registry.go (1)

5-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prevent mutation of the gate registry.

HyperFleetFeatureGates is an exported package-level map. Any importing package can add, change, or delete an entry. The registry decides which API fields are exposed and writable, so a mutation changes gating for the whole process. Concurrent reads and a write also race, because Go maps are not safe for concurrent use.

Make the map unexported and expose read-only accessors.

♻️ Proposed refactor
-// HyperFleetFeatureGates is the registry of all feature gates.
-var HyperFleetFeatureGates = map[string]FeatureGateInfo{
+// hyperFleetFeatureGates is the registry of all feature gates.
+var hyperFleetFeatureGates = map[string]FeatureGateInfo{
+// Gate returns the registered information for a gate.
+func Gate(gate string) (FeatureGateInfo, bool) {
+	info, exists := hyperFleetFeatureGates[gate]
+	return info, exists
+}
+
 // IsGateEnabled returns true if the given gate is enabled for the feature set.
 func IsGateEnabled(gate string, featureSet FeatureSet) bool {
-	info, exists := HyperFleetFeatureGates[gate]
+	info, exists := hyperFleetFeatureGates[gate]
 	if !exists {
 		return false
 	}
 	return featureSet.Includes(info.Stage)
 }

Update GatesForFeatureSet to read hyperFleetFeatureGates, and update any external reference to the old exported name.

🤖 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 `@platform-api/internal/codegen/featuregate/registry.go` around lines 5 - 6,
Make the feature-gate registry map unexported by renaming HyperFleetFeatureGates
to hyperFleetFeatureGates, update GatesForFeatureSet to use the private map, and
replace all external references with read-only accessor usage rather than
exposing mutable map state.
platform-api/internal/codegen/featuregate/types.go (1)

6-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document that Includes depends on this declaration order.

Includes compares stages numerically. The comparison is correct only while the constants stay ordered from least to most permissive. A new stage inserted between GA and TechPreview would silently change which gates are enabled for every feature set. Record the invariant next to the constants.

♻️ Proposed comment
 const (
+	// Stages must stay ordered from least to most permissive.
+	// FeatureSet.Includes relies on this numeric order.
 	GA FeatureStage = iota
 	TechPreview
 	DevPreview
 )
🤖 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 `@platform-api/internal/codegen/featuregate/types.go` around lines 6 - 10, Add
a concise comment immediately above the `GA`, `TechPreview`, and `DevPreview`
constants documenting that `Includes` compares their numeric values and
therefore requires the stages to remain ordered from least to most permissive;
preserve the existing declaration order.
hack/api-codegen/pkg/markers/scanner.go (1)

100-110: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Empty prefixes leave registry keys un-namespaced in a flat map. rootTypePrefix returns "" for every root type it does not recognize, and scanDir iterates dirCache in nondeterministic order. Two root types with the same JSON field name then produce one key, and the surviving entry depends on map order. The generated field_metadata.go already shows unprefixed keys such as maxPods next to kubelet.maxPods, which is the observable output of this behavior.

  • hack/api-codegen/pkg/markers/scanner.go#L100-L110: replace the string-prefix checks with an explicit prefix table, and return an error for a Passthrough type that has no mapped prefix.
  • hack/api-codegen/pkg/markers/scanner.go#L82-L92: propagate that error out of scanDir so an unmapped root type fails generation instead of writing flat keys.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/api-codegen/pkg/markers/scanner.go` around lines 100 - 110, The
rootTypePrefix/scanDir flow silently produces unnamespaced registry keys for
unmapped Passthrough root types. In
hack/api-codegen/pkg/markers/scanner.go:100-110, replace prefix checks with an
explicit type-to-prefix table and return an error when a Passthrough type lacks
a mapping; in hack/api-codegen/pkg/markers/scanner.go:82-92, propagate that
error from scanDir so generation stops before writing flat keys.
README.md (1)

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

Add api/ to the directory table.

The module layout block now documents api/go.mod as a standalone module. The directory table near the top of the file lists platform-api/, hyperfleet-operator/, hyperfleet-db/, and test/, but not api/. Add a row so a reader finds the CRD types module in both places.

🤖 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 `@README.md` around lines 25 - 27, Update the README directory table to add an
api/ row describing the standalone CRD types module, matching the existing table
format and the api/go.mod entry in the module layout block.
api/v1alpha1/cluster_types.go (1)

88-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use one required/optional marker style in this file.

HostedCluster uses +kubebuilder:validation:Required. Every other field in ClusterSpec uses +optional, and Cluster (Lines 153-164) uses +required. Switch to +required for consistency. Both markers produce the same CRD output, so this change is cosmetic.

♻️ Proposed marker change
 	// HostedCluster contains the upstream HyperShift fields, mirrored as
 	// passthrough types with per-field visibility and write-mode markers.
-	// +kubebuilder:validation:Required
+	// +required
 	HostedCluster HostedClusterSpecPassthrough `json:"hostedCluster"`
🤖 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 `@api/v1alpha1/cluster_types.go` around lines 88 - 91, Change the kubebuilder
marker immediately above the HostedCluster field in ClusterSpec from
+kubebuilder:validation:Required to +required, matching the marker style used by
the surrounding ClusterSpec and Cluster fields.
🤖 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 `@api/v1alpha1/cluster_types.go`:
- Around line 75-80: Update the CreatorARN validation pattern on the CreatorARN
field to accept the commercial, China, and GovCloud AWS partitions, matching the
repository’s established ARN pattern; leave the field’s other markers and
comment unchanged.

In `@api/v1alpha1/hostedclusterspec.passthrough.go`:
- Around line 96-103: Update the passthrough generator input that defines
Configuration to use the HyperFleet-owned ClusterConfiguration type instead of
hypershiftv1beta1.ClusterConfiguration, preserving the existing JSON tag and
markers. Do not modify the generated hostedclusterspec.passthrough.go file
directly.

In `@api/v1alpha1/manifest_types.go`:
- Around line 58-60: Restrict write access to Manifest objects exposed through
the manifest-editor-role: bind that role only to principals trusted with
equivalent management-cluster authority, or enforce an admission allow-list
covering permitted groups, kinds, namespaces, resources, and targets before
ApplyDesires forwards content and resource to kube-applier-aws. Preserve read
access and existing Manifest behavior for authorized callers.

In `@api/v1alpha1/nodepool_types.go`:
- Around line 55-65: Update the NodePool request decoding and conversion flow to
reject customer-supplied AccountID and InternalPoolID values before constructing
the NodePool resource. Use a customer-facing request type that omits these
fields or invoke the existing service-set validation on NodePoolSpec before
conversion, while preserving valid request handling.

In `@CLAUDE.md`:
- Around line 38-40: Update the CRD ownership entry in the “Key Conventions”
section of CLAUDE.md to state that CRD types live in the standalone api module
and are imported by hyperfleet-operator and platform-api. Remove the outdated
ownership wording while preserving the surrounding conventions.

In `@hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml`:
- Around line 89-96: Complete the truncated passthrough field descriptions in
hostedclusterspec.passthrough.go by preserving all lines from the corresponding
upstream doc comments, including the affected configuration, etcd,
imageContentSources, issuerURL, secretEncryption, olmCatalogPlacement, and
serviceAccountSigningKey fields. Then regenerate the CRD schema so hostedCluster
descriptions in hyperfleet.io_clusters.yaml contain the full sentences.
- Around line 3504-3510: Restore the upstream validation, immutability, format,
default, and bounds markers in api/v1alpha1/hostedclusterspec.passthrough.go for
every corresponding HostedClusterSpec field, including fips, clusterID, infraID,
issuerURL, capabilities, and the listed URL, RFC3339, hostname, collection, and
property constraints. Document any intentionally omitted constraint in the
passthrough struct, then regenerate the Cluster CRD so its schema preserves the
upstream behavior.
- Around line 8349-8358: Update the generator input for the hostedCluster
passthrough schema to define defaults for both required fields, using false for
fips and an empty object for sshKey; then regenerate the CRDs so the generated
schema reflects those defaults instead of editing this YAML directly.

In `@hyperfleet-operator/internal/render/cluster.go`:
- Around line 181-191: Update hostedCluster and the API conversion boundary
around toHostedClusterSpec so only the explicit public HostedCluster
specification is persisted and rendered; do not passthrough the complete
ClusterSpec. Strip or reject service-set fields including Platform, Etcd,
ServiceAccountSigningKey, SecretEncryption, and OperatorConfiguration before
storage and before rendering, while preserving the existing conversion error
handling.

In `@Makefile`:
- Around line 287-290: Update the generate target to run object generation
inside the api module by changing the command to execute cd api before invoking
CONTROLLER_GEN with paths="./..."; leave the existing hyperfleet-operator CRD
generation unchanged.

In `@platform-api/internal/codegen/conversion/cluster.go`:
- Around line 34-40: Update RewriteCloudURLWithID to normalize the URL separator
when combining baseURL and clusterID, preventing duplicate slashes if baseURL
already ends with one; add the strings import and preserve the existing nil-spec
behavior and cloudUrl assignment.

In `@platform-api/internal/codegen/registry/field_metadata.go`:
- Around line 1-22: Remove the unused generated registry under the registry
package, including its re-exported aliases, constants, and FieldRegistry
definition; do not add generation changes unless choosing instead to establish a
single target that consistently updates both registry copies.
- Around line 580-584: Remove the HyperFleetAutoScaling feature-gate marker from
the Tags field in api/v1alpha1/cluster_types.go, leaving the generated field
metadata unchanged. Ensure spec.tags remains mutable and included in the default
feature set.

In `@platform-api/pkg/types/cluster.go`:
- Line 6: Update ClusterCRToPlatform and the writeJSON serialization path so
Cluster responses omit the internal ClusterSpec fields accountId and internalId,
while preserving all customer-visible fields; alternatively, align the public
schema and visibility metadata to explicitly support those fields.

---

Outside diff comments:
In `@hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml`:
- Around line 1883-1889: The NodePool schema must reject configurations that set
both spec.nodePool.replicas and spec.nodePool.autoScaling. Add the equivalent
XValidation marker in the passthrough source or generator for the NodePool spec,
using a CEL rule that enforces these fields are not simultaneously present, so
regenerated CRDs retain the admission validation.

---

Nitpick comments:
In `@api/v1alpha1/cluster_types.go`:
- Around line 88-91: Change the kubebuilder marker immediately above the
HostedCluster field in ClusterSpec from +kubebuilder:validation:Required to
+required, matching the marker style used by the surrounding ClusterSpec and
Cluster fields.

In `@hack/api-codegen/pkg/markers/scanner.go`:
- Around line 100-110: The rootTypePrefix/scanDir flow silently produces
unnamespaced registry keys for unmapped Passthrough root types. In
hack/api-codegen/pkg/markers/scanner.go:100-110, replace prefix checks with an
explicit type-to-prefix table and return an error when a Passthrough type lacks
a mapping; in hack/api-codegen/pkg/markers/scanner.go:82-92, propagate that
error from scanDir so generation stops before writing flat keys.

In `@platform-api/internal/codegen/featuregate/registry.go`:
- Around line 5-6: Make the feature-gate registry map unexported by renaming
HyperFleetFeatureGates to hyperFleetFeatureGates, update GatesForFeatureSet to
use the private map, and replace all external references with read-only accessor
usage rather than exposing mutable map state.

In `@platform-api/internal/codegen/featuregate/types.go`:
- Around line 6-10: Add a concise comment immediately above the `GA`,
`TechPreview`, and `DevPreview` constants documenting that `Includes` compares
their numeric values and therefore requires the stages to remain ordered from
least to most permissive; preserve the existing declaration order.

In `@README.md`:
- Around line 25-27: Update the README directory table to add an api/ row
describing the standalone CRD types module, matching the existing table format
and the api/go.mod entry in the module layout block.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 04266c7d-689e-4667-97b3-648432d4937b

📥 Commits

Reviewing files that changed from the base of the PR and between afde279 and 9db611e.

⛔ Files ignored due to path filters (10)
  • api/go.sum is excluded by !**/*.sum
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • clientset/generated/fake/register.go is excluded by !**/generated/**
  • clientset/generated/scheme/register.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/cluster.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/fake/fake_cluster.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/fake/fake_nodepool.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/nodepool.go is excluded by !**/generated/**
  • hyperfleet-operator/api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • platform-api/go.sum is excluded by !**/*.sum
📒 Files selected for processing (70)
  • .tekton/rosa-hyperfleet-api-pull-request.yaml
  • .tekton/rosa-hyperfleet-api-push.yaml
  • .tekton/rosa-hyperfleet-operator-pull-request.yaml
  • .tekton/rosa-hyperfleet-operator-push.yaml
  • CLAUDE.md
  • Makefile
  • README.md
  • api/go.mod
  • api/v1alpha1/cluster_types.go
  • api/v1alpha1/configuration.go
  • api/v1alpha1/groupversion_info.go
  • api/v1alpha1/hostedclusterspec.passthrough.go
  • api/v1alpha1/install/install.go
  • api/v1alpha1/managementcluster_types.go
  • api/v1alpha1/manifest_types.go
  • api/v1alpha1/nodepool_types.go
  • api/v1alpha1/placement_types.go
  • api/v1alpha1/public/.gitkeep
  • clientset/docs/architecture.md
  • clientset/go.mod
  • clientset/wrappers/wire_wrappers_generated.go
  • clientset/wrappers/wrappers_test.go
  • hack/api-codegen/pkg/markers/scanner.go
  • hack/api-codegen/pkg/markers/scanner_test.go
  • hyperfleet-operator/Containerfile
  • hyperfleet-operator/PROJECT
  • hyperfleet-operator/cmd/manager/main.go
  • hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
  • hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml
  • hyperfleet-operator/go.mod
  • hyperfleet-operator/internal/controller/cluster_controller.go
  • hyperfleet-operator/internal/controller/cluster_controller_test.go
  • hyperfleet-operator/internal/controller/manifest_controller.go
  • hyperfleet-operator/internal/controller/manifest_controller_test.go
  • hyperfleet-operator/internal/controller/nodepool_controller.go
  • hyperfleet-operator/internal/controller/nodepool_controller_test.go
  • hyperfleet-operator/internal/controller/placement_controller.go
  • hyperfleet-operator/internal/controller/placement_controller_test.go
  • hyperfleet-operator/internal/controller/suite_test.go
  • hyperfleet-operator/internal/dynamo/statusstream/manager.go
  • hyperfleet-operator/internal/render/cluster.go
  • hyperfleet-operator/internal/render/cluster_test.go
  • hyperfleet-operator/internal/render/convert.go
  • hyperfleet-operator/internal/render/nodepool.go
  • hyperfleet-operator/internal/render/nodepool_test.go
  • hyperfleet-operator/test/cluster_test.go
  • hyperfleet-operator/test/helpers_test.go
  • hyperfleet-operator/test/manifest_test.go
  • hyperfleet-operator/test/suite_test.go
  • platform-api/Containerfile
  • platform-api/go.mod
  • platform-api/internal/codegen/conversion/cluster.go
  • platform-api/internal/codegen/featuregate/registry.go
  • platform-api/internal/codegen/featuregate/types.go
  • platform-api/internal/codegen/registry/field_metadata.go
  • platform-api/internal/codegen/registry/field_metadata.json
  • platform-api/pkg/clients/hyperfleetdb/client.go
  • platform-api/pkg/clients/hyperfleetdb/client_test.go
  • platform-api/pkg/clients/hyperfleetdb/convert.go
  • platform-api/pkg/clients/hyperfleetdb/convert_test.go
  • platform-api/pkg/handlers/cluster_test.go
  • platform-api/pkg/handlers/management_cluster.go
  • platform-api/pkg/handlers/zoa_test.go
  • platform-api/pkg/types/cluster.go
  • platform-api/pkg/types/nodepool.go
  • platform-api/pkg/zoa/jobbuilder.go
  • platform-api/pkg/zoa/reconciler.go
  • platform-api/pkg/zoa/reconciler_test.go
  • test/e2e-sdk/sdk_sanity_test.go
  • test/go.mod

Comment thread api/v1alpha1/cluster_types.go
Comment thread api/v1alpha1/hostedclusterspec.passthrough.go
Comment thread api/v1alpha1/nodepool_types.go
Comment thread CLAUDE.md
Comment thread hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
Comment thread Makefile Outdated
Comment thread platform-api/internal/codegen/conversion/cluster.go
Comment thread platform-api/internal/codegen/registry/field_metadata.go Outdated
Comment thread platform-api/internal/codegen/registry/field_metadata.go Outdated
Comment thread platform-api/pkg/types/cluster.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 14

Caution

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

⚠️ Outside diff range comments (1)
hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml (1)

1883-1889: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Restore the NodePool scaling conflict validation.

The schema now accepts both spec.nodePool.replicas and spec.nodePool.autoScaling. These fields define competing desired-size controls. Reject this combination at admission time.

Add the equivalent XValidation marker to the passthrough source or generator so regeneration preserves the CEL rule.

🤖 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 `@hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml` around
lines 1883 - 1889, The NodePool schema must reject configurations that set both
spec.nodePool.replicas and spec.nodePool.autoScaling. Add the equivalent
XValidation marker in the passthrough source or generator for the NodePool spec,
using a CEL rule that enforces these fields are not simultaneously present, so
regenerated CRDs retain the admission validation.
🧹 Nitpick comments (5)
platform-api/internal/codegen/featuregate/registry.go (1)

5-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prevent mutation of the gate registry.

HyperFleetFeatureGates is an exported package-level map. Any importing package can add, change, or delete an entry. The registry decides which API fields are exposed and writable, so a mutation changes gating for the whole process. Concurrent reads and a write also race, because Go maps are not safe for concurrent use.

Make the map unexported and expose read-only accessors.

♻️ Proposed refactor
-// HyperFleetFeatureGates is the registry of all feature gates.
-var HyperFleetFeatureGates = map[string]FeatureGateInfo{
+// hyperFleetFeatureGates is the registry of all feature gates.
+var hyperFleetFeatureGates = map[string]FeatureGateInfo{
+// Gate returns the registered information for a gate.
+func Gate(gate string) (FeatureGateInfo, bool) {
+	info, exists := hyperFleetFeatureGates[gate]
+	return info, exists
+}
+
 // IsGateEnabled returns true if the given gate is enabled for the feature set.
 func IsGateEnabled(gate string, featureSet FeatureSet) bool {
-	info, exists := HyperFleetFeatureGates[gate]
+	info, exists := hyperFleetFeatureGates[gate]
 	if !exists {
 		return false
 	}
 	return featureSet.Includes(info.Stage)
 }

Update GatesForFeatureSet to read hyperFleetFeatureGates, and update any external reference to the old exported name.

🤖 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 `@platform-api/internal/codegen/featuregate/registry.go` around lines 5 - 6,
Make the feature-gate registry map unexported by renaming HyperFleetFeatureGates
to hyperFleetFeatureGates, update GatesForFeatureSet to use the private map, and
replace all external references with read-only accessor usage rather than
exposing mutable map state.
platform-api/internal/codegen/featuregate/types.go (1)

6-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document that Includes depends on this declaration order.

Includes compares stages numerically. The comparison is correct only while the constants stay ordered from least to most permissive. A new stage inserted between GA and TechPreview would silently change which gates are enabled for every feature set. Record the invariant next to the constants.

♻️ Proposed comment
 const (
+	// Stages must stay ordered from least to most permissive.
+	// FeatureSet.Includes relies on this numeric order.
 	GA FeatureStage = iota
 	TechPreview
 	DevPreview
 )
🤖 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 `@platform-api/internal/codegen/featuregate/types.go` around lines 6 - 10, Add
a concise comment immediately above the `GA`, `TechPreview`, and `DevPreview`
constants documenting that `Includes` compares their numeric values and
therefore requires the stages to remain ordered from least to most permissive;
preserve the existing declaration order.
hack/api-codegen/pkg/markers/scanner.go (1)

100-110: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Empty prefixes leave registry keys un-namespaced in a flat map. rootTypePrefix returns "" for every root type it does not recognize, and scanDir iterates dirCache in nondeterministic order. Two root types with the same JSON field name then produce one key, and the surviving entry depends on map order. The generated field_metadata.go already shows unprefixed keys such as maxPods next to kubelet.maxPods, which is the observable output of this behavior.

  • hack/api-codegen/pkg/markers/scanner.go#L100-L110: replace the string-prefix checks with an explicit prefix table, and return an error for a Passthrough type that has no mapped prefix.
  • hack/api-codegen/pkg/markers/scanner.go#L82-L92: propagate that error out of scanDir so an unmapped root type fails generation instead of writing flat keys.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/api-codegen/pkg/markers/scanner.go` around lines 100 - 110, The
rootTypePrefix/scanDir flow silently produces unnamespaced registry keys for
unmapped Passthrough root types. In
hack/api-codegen/pkg/markers/scanner.go:100-110, replace prefix checks with an
explicit type-to-prefix table and return an error when a Passthrough type lacks
a mapping; in hack/api-codegen/pkg/markers/scanner.go:82-92, propagate that
error from scanDir so generation stops before writing flat keys.
README.md (1)

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

Add api/ to the directory table.

The module layout block now documents api/go.mod as a standalone module. The directory table near the top of the file lists platform-api/, hyperfleet-operator/, hyperfleet-db/, and test/, but not api/. Add a row so a reader finds the CRD types module in both places.

🤖 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 `@README.md` around lines 25 - 27, Update the README directory table to add an
api/ row describing the standalone CRD types module, matching the existing table
format and the api/go.mod entry in the module layout block.
api/v1alpha1/cluster_types.go (1)

88-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use one required/optional marker style in this file.

HostedCluster uses +kubebuilder:validation:Required. Every other field in ClusterSpec uses +optional, and Cluster (Lines 153-164) uses +required. Switch to +required for consistency. Both markers produce the same CRD output, so this change is cosmetic.

♻️ Proposed marker change
 	// HostedCluster contains the upstream HyperShift fields, mirrored as
 	// passthrough types with per-field visibility and write-mode markers.
-	// +kubebuilder:validation:Required
+	// +required
 	HostedCluster HostedClusterSpecPassthrough `json:"hostedCluster"`
🤖 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 `@api/v1alpha1/cluster_types.go` around lines 88 - 91, Change the kubebuilder
marker immediately above the HostedCluster field in ClusterSpec from
+kubebuilder:validation:Required to +required, matching the marker style used by
the surrounding ClusterSpec and Cluster fields.
🤖 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 `@api/v1alpha1/cluster_types.go`:
- Around line 75-80: Update the CreatorARN validation pattern on the CreatorARN
field to accept the commercial, China, and GovCloud AWS partitions, matching the
repository’s established ARN pattern; leave the field’s other markers and
comment unchanged.

In `@api/v1alpha1/hostedclusterspec.passthrough.go`:
- Around line 96-103: Update the passthrough generator input that defines
Configuration to use the HyperFleet-owned ClusterConfiguration type instead of
hypershiftv1beta1.ClusterConfiguration, preserving the existing JSON tag and
markers. Do not modify the generated hostedclusterspec.passthrough.go file
directly.

In `@api/v1alpha1/manifest_types.go`:
- Around line 58-60: Restrict write access to Manifest objects exposed through
the manifest-editor-role: bind that role only to principals trusted with
equivalent management-cluster authority, or enforce an admission allow-list
covering permitted groups, kinds, namespaces, resources, and targets before
ApplyDesires forwards content and resource to kube-applier-aws. Preserve read
access and existing Manifest behavior for authorized callers.

In `@api/v1alpha1/nodepool_types.go`:
- Around line 55-65: Update the NodePool request decoding and conversion flow to
reject customer-supplied AccountID and InternalPoolID values before constructing
the NodePool resource. Use a customer-facing request type that omits these
fields or invoke the existing service-set validation on NodePoolSpec before
conversion, while preserving valid request handling.

In `@CLAUDE.md`:
- Around line 38-40: Update the CRD ownership entry in the “Key Conventions”
section of CLAUDE.md to state that CRD types live in the standalone api module
and are imported by hyperfleet-operator and platform-api. Remove the outdated
ownership wording while preserving the surrounding conventions.

In `@hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml`:
- Around line 89-96: Complete the truncated passthrough field descriptions in
hostedclusterspec.passthrough.go by preserving all lines from the corresponding
upstream doc comments, including the affected configuration, etcd,
imageContentSources, issuerURL, secretEncryption, olmCatalogPlacement, and
serviceAccountSigningKey fields. Then regenerate the CRD schema so hostedCluster
descriptions in hyperfleet.io_clusters.yaml contain the full sentences.
- Around line 3504-3510: Restore the upstream validation, immutability, format,
default, and bounds markers in api/v1alpha1/hostedclusterspec.passthrough.go for
every corresponding HostedClusterSpec field, including fips, clusterID, infraID,
issuerURL, capabilities, and the listed URL, RFC3339, hostname, collection, and
property constraints. Document any intentionally omitted constraint in the
passthrough struct, then regenerate the Cluster CRD so its schema preserves the
upstream behavior.
- Around line 8349-8358: Update the generator input for the hostedCluster
passthrough schema to define defaults for both required fields, using false for
fips and an empty object for sshKey; then regenerate the CRDs so the generated
schema reflects those defaults instead of editing this YAML directly.

In `@hyperfleet-operator/internal/render/cluster.go`:
- Around line 181-191: Update hostedCluster and the API conversion boundary
around toHostedClusterSpec so only the explicit public HostedCluster
specification is persisted and rendered; do not passthrough the complete
ClusterSpec. Strip or reject service-set fields including Platform, Etcd,
ServiceAccountSigningKey, SecretEncryption, and OperatorConfiguration before
storage and before rendering, while preserving the existing conversion error
handling.

In `@Makefile`:
- Around line 287-290: Update the generate target to run object generation
inside the api module by changing the command to execute cd api before invoking
CONTROLLER_GEN with paths="./..."; leave the existing hyperfleet-operator CRD
generation unchanged.

In `@platform-api/internal/codegen/conversion/cluster.go`:
- Around line 34-40: Update RewriteCloudURLWithID to normalize the URL separator
when combining baseURL and clusterID, preventing duplicate slashes if baseURL
already ends with one; add the strings import and preserve the existing nil-spec
behavior and cloudUrl assignment.

In `@platform-api/internal/codegen/registry/field_metadata.go`:
- Around line 1-22: Remove the unused generated registry under the registry
package, including its re-exported aliases, constants, and FieldRegistry
definition; do not add generation changes unless choosing instead to establish a
single target that consistently updates both registry copies.
- Around line 580-584: Remove the HyperFleetAutoScaling feature-gate marker from
the Tags field in api/v1alpha1/cluster_types.go, leaving the generated field
metadata unchanged. Ensure spec.tags remains mutable and included in the default
feature set.

In `@platform-api/pkg/types/cluster.go`:
- Line 6: Update ClusterCRToPlatform and the writeJSON serialization path so
Cluster responses omit the internal ClusterSpec fields accountId and internalId,
while preserving all customer-visible fields; alternatively, align the public
schema and visibility metadata to explicitly support those fields.

---

Outside diff comments:
In `@hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml`:
- Around line 1883-1889: The NodePool schema must reject configurations that set
both spec.nodePool.replicas and spec.nodePool.autoScaling. Add the equivalent
XValidation marker in the passthrough source or generator for the NodePool spec,
using a CEL rule that enforces these fields are not simultaneously present, so
regenerated CRDs retain the admission validation.

---

Nitpick comments:
In `@api/v1alpha1/cluster_types.go`:
- Around line 88-91: Change the kubebuilder marker immediately above the
HostedCluster field in ClusterSpec from +kubebuilder:validation:Required to
+required, matching the marker style used by the surrounding ClusterSpec and
Cluster fields.

In `@hack/api-codegen/pkg/markers/scanner.go`:
- Around line 100-110: The rootTypePrefix/scanDir flow silently produces
unnamespaced registry keys for unmapped Passthrough root types. In
hack/api-codegen/pkg/markers/scanner.go:100-110, replace prefix checks with an
explicit type-to-prefix table and return an error when a Passthrough type lacks
a mapping; in hack/api-codegen/pkg/markers/scanner.go:82-92, propagate that
error from scanDir so generation stops before writing flat keys.

In `@platform-api/internal/codegen/featuregate/registry.go`:
- Around line 5-6: Make the feature-gate registry map unexported by renaming
HyperFleetFeatureGates to hyperFleetFeatureGates, update GatesForFeatureSet to
use the private map, and replace all external references with read-only accessor
usage rather than exposing mutable map state.

In `@platform-api/internal/codegen/featuregate/types.go`:
- Around line 6-10: Add a concise comment immediately above the `GA`,
`TechPreview`, and `DevPreview` constants documenting that `Includes` compares
their numeric values and therefore requires the stages to remain ordered from
least to most permissive; preserve the existing declaration order.

In `@README.md`:
- Around line 25-27: Update the README directory table to add an api/ row
describing the standalone CRD types module, matching the existing table format
and the api/go.mod entry in the module layout block.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 04266c7d-689e-4667-97b3-648432d4937b

📥 Commits

Reviewing files that changed from the base of the PR and between afde279 and 9db611e.

⛔ Files ignored due to path filters (10)
  • api/go.sum is excluded by !**/*.sum
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • clientset/generated/fake/register.go is excluded by !**/generated/**
  • clientset/generated/scheme/register.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/cluster.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/fake/fake_cluster.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/fake/fake_nodepool.go is excluded by !**/generated/**
  • clientset/generated/typed/v1alpha1/internalversion/nodepool.go is excluded by !**/generated/**
  • hyperfleet-operator/api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • platform-api/go.sum is excluded by !**/*.sum
📒 Files selected for processing (70)
  • .tekton/rosa-hyperfleet-api-pull-request.yaml
  • .tekton/rosa-hyperfleet-api-push.yaml
  • .tekton/rosa-hyperfleet-operator-pull-request.yaml
  • .tekton/rosa-hyperfleet-operator-push.yaml
  • CLAUDE.md
  • Makefile
  • README.md
  • api/go.mod
  • api/v1alpha1/cluster_types.go
  • api/v1alpha1/configuration.go
  • api/v1alpha1/groupversion_info.go
  • api/v1alpha1/hostedclusterspec.passthrough.go
  • api/v1alpha1/install/install.go
  • api/v1alpha1/managementcluster_types.go
  • api/v1alpha1/manifest_types.go
  • api/v1alpha1/nodepool_types.go
  • api/v1alpha1/placement_types.go
  • api/v1alpha1/public/.gitkeep
  • clientset/docs/architecture.md
  • clientset/go.mod
  • clientset/wrappers/wire_wrappers_generated.go
  • clientset/wrappers/wrappers_test.go
  • hack/api-codegen/pkg/markers/scanner.go
  • hack/api-codegen/pkg/markers/scanner_test.go
  • hyperfleet-operator/Containerfile
  • hyperfleet-operator/PROJECT
  • hyperfleet-operator/cmd/manager/main.go
  • hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
  • hyperfleet-operator/config/crd/bases/hyperfleet.io_nodepools.yaml
  • hyperfleet-operator/go.mod
  • hyperfleet-operator/internal/controller/cluster_controller.go
  • hyperfleet-operator/internal/controller/cluster_controller_test.go
  • hyperfleet-operator/internal/controller/manifest_controller.go
  • hyperfleet-operator/internal/controller/manifest_controller_test.go
  • hyperfleet-operator/internal/controller/nodepool_controller.go
  • hyperfleet-operator/internal/controller/nodepool_controller_test.go
  • hyperfleet-operator/internal/controller/placement_controller.go
  • hyperfleet-operator/internal/controller/placement_controller_test.go
  • hyperfleet-operator/internal/controller/suite_test.go
  • hyperfleet-operator/internal/dynamo/statusstream/manager.go
  • hyperfleet-operator/internal/render/cluster.go
  • hyperfleet-operator/internal/render/cluster_test.go
  • hyperfleet-operator/internal/render/convert.go
  • hyperfleet-operator/internal/render/nodepool.go
  • hyperfleet-operator/internal/render/nodepool_test.go
  • hyperfleet-operator/test/cluster_test.go
  • hyperfleet-operator/test/helpers_test.go
  • hyperfleet-operator/test/manifest_test.go
  • hyperfleet-operator/test/suite_test.go
  • platform-api/Containerfile
  • platform-api/go.mod
  • platform-api/internal/codegen/conversion/cluster.go
  • platform-api/internal/codegen/featuregate/registry.go
  • platform-api/internal/codegen/featuregate/types.go
  • platform-api/internal/codegen/registry/field_metadata.go
  • platform-api/internal/codegen/registry/field_metadata.json
  • platform-api/pkg/clients/hyperfleetdb/client.go
  • platform-api/pkg/clients/hyperfleetdb/client_test.go
  • platform-api/pkg/clients/hyperfleetdb/convert.go
  • platform-api/pkg/clients/hyperfleetdb/convert_test.go
  • platform-api/pkg/handlers/cluster_test.go
  • platform-api/pkg/handlers/management_cluster.go
  • platform-api/pkg/handlers/zoa_test.go
  • platform-api/pkg/types/cluster.go
  • platform-api/pkg/types/nodepool.go
  • platform-api/pkg/zoa/jobbuilder.go
  • platform-api/pkg/zoa/reconciler.go
  • platform-api/pkg/zoa/reconciler_test.go
  • test/e2e-sdk/sdk_sanity_test.go
  • test/go.mod
🛑 Comments failed to post (1)
api/v1alpha1/manifest_types.go (1)

58-60: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Manifest writers and the controller path that converts templates to desires.
rg -n -i -C 6 'manifests|kind:[[:space:]]*(Role|ClusterRole)|resources:' \
  -g '*.yaml' -g '*.yml' -g '*.go' .

rg -n -C 8 'ResourceTemplate|ManifestSpec|ApplyDesire|managementCluster' \
  -g '*.go' api hyperfleet-operator platform-api

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 50393


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- relevant files ---'
git ls-files | rg -i '(rbac|role|clusterrole|manifest|controller|reconciler|dynamo|desire)' | head -200

printf '%s\n' '--- Manifest API and controller references ---'
rg -n -C 5 'type Manifest|type ResourceTemplate|ManifestSpec|ResourceTemplate|BuildManifest|ApplyDesire|targetItem|ManagementCluster' \
  api/v1alpha1 hyperfleet-operator/internal platform-api/pkg \
  -g '*.go' | head -500

printf '%s\n' '--- authorization and admission configuration ---'
rg -n -i -C 5 'manifests|hyperfleet.io|rolebinding|clusterrolebinding|authorization|admission|validat' \
  . -g '*.yaml' -g '*.yml' -g '*.go' -g '*.json' \
  | head -500

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 50392


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Manifest RBAC ---'
for f in \
  hyperfleet-operator/config/rbac/manifest_admin_role.yaml \
  hyperfleet-operator/config/rbac/manifest_editor_role.yaml \
  hyperfleet-operator/config/rbac/manifest_viewer_role.yaml \
  hyperfleet-operator/config/rbac/cluster_admin_role.yaml \
  hyperfleet-operator/config/rbac/cluster_editor_role.yaml \
  hyperfleet-operator/config/rbac/cluster_viewer_role.yaml \
  hyperfleet-operator/config/rbac/role.yaml \
  hyperfleet-operator/charts/templates/clusterrole.yaml \
  hyperfleet-operator/charts/templates/clusterrolebinding.yaml
do
  if test -f "$f"; then
    echo "### $f"
    cat -n "$f"
  fi
done

printf '%s\n' '--- API type definitions ---'
rg -n -C 12 'type (Manifest|ManifestSpec|ResourceTemplate) struct' api/v1alpha1/manifest_types.go

printf '%s\n' '--- Manifest controller implementation ---'
cat -n hyperfleet-operator/internal/controller/manifest_controller.go

printf '%s\n' '--- Manifest design documentation ---'
cat -n hyperfleet-operator/docs/manifest-controller.md | head -260

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 45823


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

rbac = {}
for name in [
    "manifest_admin_role.yaml",
    "manifest_editor_role.yaml",
    "manifest_viewer_role.yaml",
]:
    p = Path("hyperfleet-operator/config/rbac") / name
    text = p.read_text()
    rbac[name] = {
        "writes_manifests": bool(re.search(
            r"resources:\s*\n\s*-\s*manifests\b.*?verbs:\s*\n((?:\s*-\s*[^\n]+\n)+)",
            text, re.S)),
        "has_create": bool(re.search(r"^\s*-\s*create\s*$", text, re.M)),
        "has_update": bool(re.search(r"^\s*-\s*update\s*$", text, re.M)),
    }

controller = Path("hyperfleet-operator/internal/controller/manifest_controller.go").read_text()
api = Path("api/v1alpha1/manifest_types.go").read_text()
docs = Path("hyperfleet-operator/docs/manifest-controller.md").read_text()

print("manifest_rbac:", rbac)
print("controller_passes_raw_content:",
      "KubeContent: &res.Content" in controller and
      "UpsertApplyDesire(ctx, specsPrefix, desire)" in controller)
print("controller_passes_user_resource:",
      "Resource:  res.Resource" in controller)
print("api_has_only_min_resource_validation:",
      "validation:MinLength=1" in api and
      "Resource is the plural resource name" in api)
print("docs_no_admission_validation:", "No admission-time validation" in docs)
print("docs_describes_arbitrary_apply:", "Deploys arbitrary Kubernetes resources" in docs)
PY

printf '%s\n' '--- bindings to manifest editor/admin roles ---'
rg -n -C 5 'manifest-(editor|admin|viewer)-role|roleRef:|kind:[[:space:]]*(RoleBinding|ClusterRoleBinding)' \
  hyperfleet-operator/config hyperfleet-operator/charts \
  -g '*.yaml' -g '*.yml' || true

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 7638


Authorization Bypass (CWE-269): Improper Privilege Management

Reachability: External · Exploitability: Moderate

Reachability path
● Entry
  platform-api/pkg/zoa/reconciler_test.go:515
  Run
│
▼
● Hop
  platform-api/pkg/zoa/reconciler.go:385
  ManifestPhaseApplied
│
▼
● Sink
  api/v1alpha1/manifest_types.go

Restrict Manifest write access to trusted principals.

manifest-editor-role grants create and update access to arbitrary Manifest objects. The operator forwards content and resource into ApplyDesires that kube-applier-aws applies to the management cluster. Bind this role only to principals with equivalent management-cluster authority, or add an admission allow-list for permitted groups, kinds, namespaces, resources, and targets.

🤖 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 `@api/v1alpha1/manifest_types.go` around lines 58 - 60, Restrict write access
to Manifest objects exposed through the manifest-editor-role: bind that role
only to principals trusted with equivalent management-cluster authority, or
enforce an admission allow-list covering permitted groups, kinds, namespaces,
resources, and targets before ApplyDesires forwards content and resource to
kube-applier-aws. Preserve read access and existing Manifest behavior for
authorized callers.

Source: Path instructions

Set GOCACHE to /workspace/.cache/go-build so the non-root USER 1001
can write build cache during the container image build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cdoan1 cdoan1 added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 5, 2026
…argets

Port field validation from ROSAENG-61802 branch: FieldValidator with
write-mode enforcement wired into cluster and nodepool handlers.
Add codegen Makefile targets (codegen-passthrough, codegen-registry,
codegen-verify, verify-codegen) refactored for api/v1alpha1 layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (1)
Makefile (1)

293-299: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Run object generation inside the api module.

The object-generation recipe in Lines 293-299 runs from the repository root with paths="./api/...". If the root has no go.mod or go.work, controller-gen cannot load the standalone api module. This blocks the dependent codegen targets in Lines 331-339 and 342-345.

This is the same issue reported in the previous review. Move only the object-generation command into api, or provide a root workspace.

Proposed fix
 generate:
-	$(CONTROLLER_GEN) object paths="./api/..."
+	cd api && $(CONTROLLER_GEN) object paths="./..."

Run this verification script:

#!/bin/bash
set -euo pipefail

echo "=== module and workspace files ==="
fd -t f -g 'go.mod' -g 'go.work' -g 'go.work.sum' | sort

echo "=== Go module context ==="
for dir in . api hyperfleet-operator platform-api; do
  if [ -d "$dir" ]; then
    printf '%s: ' "$dir"
    (cd "$dir" && GOTOOLCHAIN=local go env GOMOD GOWORK 2>&1) || true
  fi
done

echo "=== relevant Makefile recipes ==="
sed -n '284,345p' Makefile

Also applies to: 331-339, 342-345

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

In `@Makefile` around lines 293 - 299, Update the object-generation recipe under
generate to run $(CONTROLLER_GEN) from the api directory, while preserving its
existing object paths and dependencies. Move only this command into api so
dependent generate-clientset and related codegen targets can load the standalone
api module; do not add a root workspace or alter unrelated recipes.
🤖 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 `@hack/api-codegen/README.md`:
- Around line 71-78: Add the text language identifier to the fenced
dependency-tree block in the README by changing its opening fence to ```text,
while leaving the tree content unchanged.

In `@Makefile`:
- Line 340: Keep the Makefile target codegen dependent only on codegen-verify,
leaving passthrough generation manual. Update Makefile lines 116-117 to state
that verification covers automatic outputs and passthrough generation is manual,
and align hack/api-codegen/README.md lines 63-67 with these target behaviors and
descriptions.

In `@platform-api/pkg/handlers/cluster.go`:
- Around line 312-321: Handle the return value of json.Encoder.Encode in both
writeValidationErrors helpers: platform-api/pkg/handlers/cluster.go lines
312-321 and platform-api/pkg/handlers/nodepool.go lines 266-275. When encoding
fails, log the error through the respective handler logger instead of discarding
it, while preserving the existing validation response behavior.

In `@platform-api/pkg/validation/field_validator_test.go`:
- Around line 3-253: Convert the tests in the FieldValidator suite from
testing.T to Ginkgo/Gomega, replacing test functions and assertions with Ginkgo
specs and Gomega matchers while preserving all existing scenarios and
expectations. Add the package-level Ginkgo RunSpecs bootstrap, and update module
dependencies so Ginkgo remains at v2.28.1 and Gomega v1.42.1 is declared
directly.

In `@platform-api/pkg/validation/field_validator.go`:
- Around line 131-139: Update ValidateUpdate and the request-to-resource
conversion paths to preserve existing values for registry.ServiceSet fields
instead of allowing zero or omitted fields to clear them. Merge only
customer-owned fields, or use field-presence information to distinguish omission
from an explicit write and reject explicit writes, while keeping
platform-managed values from req.Spec from replacing the existing spec.
- Around line 140-153: The update handlers ApplyPlatformUpdateToClusterCR and
ApplyPlatformUpdateToNodePoolCR must preserve immutable fields omitted from
partial updates. Merge incoming specs with the existing spec before replacement,
or validate the merged union using correctly prefixed nested paths so
registry.Immutable entries such as
spec.hostedCluster.configuration.machineConfig.fips are checked. Add regression
tests covering omitted immutable fields for both handlers.

---

Duplicate comments:
In `@Makefile`:
- Around line 293-299: Update the object-generation recipe under generate to run
$(CONTROLLER_GEN) from the api directory, while preserving its existing object
paths and dependencies. Move only this command into api so dependent
generate-clientset and related codegen targets can load the standalone api
module; do not add a root workspace or alter unrelated recipes.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: eb11b37d-a248-4125-9d12-aa8d62109a48

📥 Commits

Reviewing files that changed from the base of the PR and between 39e6800 and 981c997.

📒 Files selected for processing (7)
  • .gitignore
  • Makefile
  • hack/api-codegen/README.md
  • platform-api/pkg/handlers/cluster.go
  • platform-api/pkg/handlers/nodepool.go
  • platform-api/pkg/validation/field_validator.go
  • platform-api/pkg/validation/field_validator_test.go

Comment thread hack/api-codegen/README.md
Comment thread Makefile
Comment thread platform-api/pkg/handlers/cluster.go
Comment thread platform-api/pkg/validation/field_validator_test.go
Comment thread platform-api/pkg/validation/field_validator.go
Comment thread platform-api/pkg/validation/field_validator.go
…, and codegen enhancements

- Scanner verbose mode for marker debugging
- Service-set field preservation via snapshot pattern in update handlers
- AccountID/InternalID population on create for clusters and node pools
- Registry relocated from platform-api/internal/codegen to hack/api-codegen/pkg
- OpenAPI merge command and generator enhancements (passthrough collapse, ref targets)
- imageContentSources changed to mutable with openapi-gen=true
- Configuration field uses local ClusterConfiguration type
- OpenAPI spec moved to api/v1alpha1/public/openapi.yaml
- CRD regenerated with allowDangerousTypes=true
- Makefile codegen targets updated for api/v1alpha1 layout

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
platform-api/pkg/clients/hyperfleetdb/convert.go (1)

76-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the parameter order of the two conversion functions.

PlatformCreateToClusterCR takes (clusterID, accountID string, ...). PlatformCreateToNodePoolCR takes (accountID, internalPoolID string, ...). Both leading parameters are plain strings, so a caller that swaps them still compiles. The result is an account ID stored as an internal ID and an internal ID used as the tenant label.

Use the same order in both functions, or introduce distinct named string types for the account ID and the internal ID so the compiler rejects a swap.

Also applies to: 152-155

🤖 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 `@platform-api/pkg/clients/hyperfleetdb/convert.go` around lines 76 - 79, Align
PlatformCreateToClusterCR with PlatformCreateToNodePoolCR by using the same
accountID-then-internalID parameter order, and update all call sites
accordingly. Ensure the assignments to spec.AccountID and spec.InternalID remain
mapped to the correct arguments, including the corresponding conversion logic
around PlatformCreateToNodePoolCR.
platform-api/pkg/clients/hyperfleetdb/convert_test.go (1)

41-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case where the request spec already carries AccountID and the internal ID.

Both tests start from a spec that leaves AccountID, InternalID, and InternalPoolID empty, so they prove only that the fields are populated. The important guarantee of this change is that the server value wins. Add a case that sets Spec.AccountID and Spec.InternalID (or Spec.InternalPoolID) to attacker-supplied values in the request, then assert that the converted resource holds the server-provided values instead.

Also applies to: 74-80

🤖 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 `@platform-api/pkg/clients/hyperfleetdb/convert_test.go` around lines 41 - 47,
Extend the conversion tests around the existing np.Spec.AccountID and
np.Spec.InternalPoolID assertions with a request whose spec pre-populates
AccountID and InternalID or InternalPoolID with different attacker-supplied
values. Assert that the converted resource retains the server-provided values,
confirming they override request values while preserving the existing
empty-field coverage.
hack/api-codegen/pkg/openapi/generator.go (1)

153-165: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Three hardcoded maps describe the same type set and can drift apart.

typeToRegistryPrefix (Lines 157-165), refTargets (Lines 231-236), and the two literal slices inside collapsePassthroughTypes (Lines 244 and 269) all enumerate the same passthrough and configuration types. A change to one map does not force a change to the others. For example, a new nested configuration type needs an entry in the prefix map, an entry in refTargets, and possibly a slice entry, and nothing detects an omission.

Consider one table keyed by type name that holds the registry prefix, the passthrough flag, and the child field-to-definition mapping. Derive all three current behaviors from that table.

Also applies to: 229-236

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

In `@hack/api-codegen/pkg/openapi/generator.go` around lines 153 - 165, Replace
the separate type lists in typeToRegistryPrefix, refTargets, and
collapsePassthroughTypes with one type-name-keyed metadata table containing each
type’s registry prefix, passthrough status, and child field-to-definition
mappings. Update refTargets and both collapsePassthroughTypes paths to derive
their current behavior from this table, preserving existing mappings while
ensuring adding a type requires only one entry.
hack/api-codegen/pkg/openapi/generator_test.go (1)

9-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add assertions for the new collapse behavior, and fix the directory variable name.

This test covers hidden-field pruning only. The same change adds collapsePassthroughTypes, which is untested here. Add assertions for:

  • HostedClusterSpecPassthrough and NodePoolSpecPassthrough have AdditionalProperties.Allows == true.
  • ClusterSpec.Properties["hostedCluster"].Ref points to HostedClusterSpecPassthrough.
  • A non-$ref property of the passthrough type, for example autoNode, has no nested Properties and no Required entries.

The variable v2alpha1Dir at Line 14 holds the v1alpha1 path. Rename it to v1alpha1Dir.

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

In `@hack/api-codegen/pkg/openapi/generator_test.go` around lines 9 - 72, Extend
TestConfigurationUsesLocalType to assert collapsePassthroughTypes behavior:
verify HostedClusterSpecPassthrough and NodePoolSpecPassthrough allow additional
properties, confirm ClusterSpec’s hostedCluster property references
HostedClusterSpecPassthrough, and verify a non-$ref property such as autoNode
has no nested Properties or Required entries. Rename the incorrectly named
v2alpha1Dir variable and update its uses to v1alpha1Dir.
🤖 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 `@api/v1alpha1/public/openapi.yaml`:
- Around line 2957-3013: Regenerate HostedClusterSpecPassthrough from the
current markers so it includes the visible etcd, networking, and platform
properties and requires etcd, fips, networking, and platform. Remove autoNode
from its required list while preserving it as optional; leave hidden service-set
fields and the empty NodePoolSpecPassthrough unchanged.

In `@hack/api-codegen/cmd/openapi-merge/main.go`:
- Around line 216-227: The YAML line-processing logic must avoid bufio.Scanner’s
token limit and handle errors explicitly. In
hack/api-codegen/cmd/openapi-merge/main.go:216-227, replace the scanner-based
splitting around the current prefix-writing logic with a non-truncating
implementation that preserves blank-line handling; in
hack/api-codegen/cmd/openapi-merge/main.go:252-259, make the same replacement
for the other scanner-based splitter. Ensure both paths propagate any read or
processing errors instead of ignoring them.

In `@hack/api-codegen/pkg/openapi/generator.go`:
- Around line 183-187: Update filterHiddenFields around typeToRegistryPrefix so
an unmapped definition produces an explicit error instead of continuing without
pruning; propagate that error through Generate and its callers. Preserve pruning
for mapped definitions, and use the registry-derived prefix when available
rather than silently falling back to the definition name.

In `@hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml`:
- Around line 503-522: Update ClusterConfiguration and its generated CRD schema
for apiServer, authentication, featureGate, image, ingress, network, oauth,
proxy, and scheduler: remove these placeholder fields if unsupported, or mark
their source definitions with +kubebuilder:pruning:PreserveUnknownFields and
regenerate the CRD so nested configuration is retained.

In `@platform-api/pkg/handlers/nodepool.go`:
- Around line 198-208: Update the nodepool update flow around
ApplyPlatformUpdateToNodePoolCR to preserve every omitted service-set field,
including ClusterName, Release, Platform, AccountID, and InternalPoolID, rather
than restoring only two fields; preferably merge only mutable fields. Apply the
same nested service-set preservation logic in the cluster update handler.

---

Nitpick comments:
In `@hack/api-codegen/pkg/openapi/generator_test.go`:
- Around line 9-72: Extend TestConfigurationUsesLocalType to assert
collapsePassthroughTypes behavior: verify HostedClusterSpecPassthrough and
NodePoolSpecPassthrough allow additional properties, confirm ClusterSpec’s
hostedCluster property references HostedClusterSpecPassthrough, and verify a
non-$ref property such as autoNode has no nested Properties or Required entries.
Rename the incorrectly named v2alpha1Dir variable and update its uses to
v1alpha1Dir.

In `@hack/api-codegen/pkg/openapi/generator.go`:
- Around line 153-165: Replace the separate type lists in typeToRegistryPrefix,
refTargets, and collapsePassthroughTypes with one type-name-keyed metadata table
containing each type’s registry prefix, passthrough status, and child
field-to-definition mappings. Update refTargets and both
collapsePassthroughTypes paths to derive their current behavior from this table,
preserving existing mappings while ensuring adding a type requires only one
entry.

In `@platform-api/pkg/clients/hyperfleetdb/convert_test.go`:
- Around line 41-47: Extend the conversion tests around the existing
np.Spec.AccountID and np.Spec.InternalPoolID assertions with a request whose
spec pre-populates AccountID and InternalID or InternalPoolID with different
attacker-supplied values. Assert that the converted resource retains the
server-provided values, confirming they override request values while preserving
the existing empty-field coverage.

In `@platform-api/pkg/clients/hyperfleetdb/convert.go`:
- Around line 76-79: Align PlatformCreateToClusterCR with
PlatformCreateToNodePoolCR by using the same accountID-then-internalID parameter
order, and update all call sites accordingly. Ensure the assignments to
spec.AccountID and spec.InternalID remain mapped to the correct arguments,
including the corresponding conversion logic around PlatformCreateToNodePoolCR.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: a9df3855-ed12-4470-86ab-175103e22808

📥 Commits

Reviewing files that changed from the base of the PR and between 981c997 and c8de9b7.

⛔ Files ignored due to path filters (1)
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
📒 Files selected for processing (22)
  • .gitignore
  • Makefile
  • README.md
  • api/v1alpha1/hostedclusterspec.passthrough.go
  • api/v1alpha1/public/openapi.yaml
  • hack/api-codegen/cmd/marker-scanner/main.go
  • hack/api-codegen/cmd/openapi-merge/main.go
  • hack/api-codegen/pkg/markers/gated_writemode_test.go
  • hack/api-codegen/pkg/markers/scanner.go
  • hack/api-codegen/pkg/markers/scanner_test.go
  • hack/api-codegen/pkg/markers/types.go
  • hack/api-codegen/pkg/openapi/generator.go
  • hack/api-codegen/pkg/openapi/generator_test.go
  • hack/api-codegen/pkg/registry/field_metadata.go
  • hack/api-codegen/pkg/registry/field_metadata.json
  • hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
  • platform-api/pkg/clients/hyperfleetdb/convert.go
  • platform-api/pkg/clients/hyperfleetdb/convert_test.go
  • platform-api/pkg/handlers/cluster.go
  • platform-api/pkg/handlers/nodepool.go
  • platform-api/pkg/validation/field_validator.go
  • platform-api/pkg/validation/field_validator_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • .gitignore
  • README.md
  • api/v1alpha1/hostedclusterspec.passthrough.go
  • platform-api/pkg/validation/field_validator.go
  • hack/api-codegen/pkg/markers/scanner_test.go
  • platform-api/pkg/validation/field_validator_test.go
  • platform-api/pkg/handlers/cluster.go

Comment thread api/v1alpha1/public/openapi.yaml
Comment thread hack/api-codegen/cmd/openapi-merge/main.go Outdated
Comment thread hack/api-codegen/pkg/openapi/generator.go
Comment on lines +503 to +522
properties:
apiServer:
description: apiServer contains advanced network settings
for the API server.
type: object
authentication:
description: authentication contains configuration for the
cluster authentication.
type: object
featureGate:
description: featureGate contains the desired configuration
for feature gates.
type: object
image:
description: image contains the configuration for internal
registry.
type: object
ingress:
description: ingress contains the configuration for ingress.
type: object

Copy link
Copy Markdown
Contributor

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the opaque configuration sub-objects are declared, populated, or rendered anywhere.
set -uo pipefail

echo "=== local ClusterConfiguration type ==="
fd -t f 'configuration.go' | while IFS= read -r f; do
  echo "--- $f"
  cat -n "$f"
done

echo "=== producers/consumers of configuration sub-objects ==="
rg -n -C4 '\.Configuration\b|Configuration:\s*&|APIServer:|Authentication:|FeatureGate:|Ingress:|Network:|OAuth:|Proxy:|Scheduler:' \
  --glob '*.go' -g '!**/vendor/**' | head -120

echo "=== preserve-unknown-fields markers present in api types ==="
rg -n 'PreserveUnknownFields|x-kubernetes-preserve-unknown-fields' --glob '*.go' --glob '*.yaml' -g '!**/vendor/**' | head -40

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 9263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== generated schema sections ==="
sed -n '475,535p' hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
sed -n '630,685p' hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml

echo "=== schema-level preservation markers ==="
rg -n -C3 'x-kubernetes-preserve-unknown-fields|hostedCluster|configuration:' \
  hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml | head -160

echo "=== source markers and placeholder declarations ==="
rg -n -C3 'openapi-gen=false|PreserveUnknownFields|type (APIServerNetworkConfiguration|ClusterAuthentication|FeatureGateConfiguration|ImageConfiguration|IngressConfiguration|NetworkConfiguration|OAuthConfiguration|SchedulerConfiguration|ProxyConfiguration) struct' \
  api hyperfleet-operator --glob '*.go' --glob '*.yaml' --glob '*.yml' 2>/dev/null | head -160

echo "=== all configuration field usages ==="
rg -n -C3 'Configuration\.(APIServer|Authentication|FeatureGate|Image|Ingress|Network|OAuth|Proxy|Scheduler)|Configuration:\s*&|\.Configuration\b' \
  --glob '*.go' -g '!**/vendor/**' | head -240

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 25398


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

crd = Path("hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml").read_text()
source = Path("api/v1alpha1/configuration.go").read_text()

fields = ["apiServer", "authentication", "featureGate", "image", "ingress",
          "network", "oauth", "proxy", "scheduler"]

# Inspect each configuration child schema block until the next sibling.
start = crd.index("                  configuration:")
end = crd.index("                  controlPlaneRelease:", start)
schema = crd[start:end]

for field in fields:
    match = re.search(
        rf"^                      {re.escape(field)}:\n(?P<body>.*?)(?=^                      \w|\\Z)",
        schema, re.MULTILINE | re.DOTALL,
    )
    assert match, field
    body = match.group("body")
    assert re.search(r"^\s+type: object$", body, re.MULTILINE), field
    assert "x-kubernetes-preserve-unknown-fields: true" not in body, field
    assert "properties:" not in body, field
    print(f"{field}: empty object schema; unknown fields are not preserved")

# Model the relevant structural-schema pruning operation for a concrete payload.
payload = {
    "configuration": {
        "network": {"networkType": " OVNKubernetes "},
        "proxy": {"httpProxy": "http://proxy.example"},
    }
}
for field in ("network", "proxy"):
    payload["configuration"][field] = {}

assert payload == {"configuration": {"network": {}, "proxy": {}}}
print("network.networkType and proxy.httpProxy are removed by this schema shape")

# Confirm the source uses placeholder types and that no fields are declared in them.
for typ in [
    "APIServerNetworkConfiguration", "ClusterAuthentication",
    "FeatureGateConfiguration", "ImageConfiguration", "IngressConfiguration",
    "NetworkConfiguration", "OAuthConfiguration",
    "SchedulerConfiguration", "ProxyConfiguration",
]:
    match = re.search(rf"type {typ} struct \{{(?P<body>.*?)\}}", source, re.DOTALL)
    assert match and not match.group("body").strip(), typ
    print(f"{typ}: empty placeholder type")
PY

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 785


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== scheduler schema ==="
sed -n '650,680p' hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml

python3 - <<'PY'
from pathlib import Path
import re

crd = Path("hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml").read_text()
start = crd.index("                  configuration:")
end = crd.index("                  controlPlaneRelease:", start)
schema = crd[start:end]

fields = ["apiServer", "authentication", "featureGate", "image", "ingress",
          "network", "oauth", "proxy", "scheduler"]

# Split on exact YAML sibling indentation instead of a generic word boundary.
children = re.split(r"(?m)^                      (?=[A-Za-z][A-Za-z0-9]*:\n)", schema)
blocks = {}
for child in children[1:]:
    name = child.split(":", 1)[0]
    blocks[name] = child

for field in fields:
    body = blocks[field]
    assert re.search(r"(?m)^\s+type: object$", body), field
    assert "x-kubernetes-preserve-unknown-fields: true" not in body, field
    assert "properties:" not in body, field
    print(f"{field}: empty object schema; unknown fields are not preserved")

payload = {
    "configuration": {
        "network": {"networkType": "OVNKubernetes"},
        "proxy": {"httpProxy": "http://proxy.example"},
        "scheduler": {"policy": "default"},
    }
}
for field in ("network", "proxy", "scheduler"):
    payload["configuration"][field] = {}
assert payload == {"configuration": {"network": {}, "proxy": {}, "scheduler": {}}}
print("nested fields under network, proxy, and scheduler are pruned by this schema shape")
PY

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 2292


Remove or preserve the placeholder configuration objects.

The CRD defines apiServer, authentication, featureGate, image, ingress, network, oauth, proxy, and scheduler as empty object schemas. The Kubernetes API server prunes fields inside these objects because the schema defines no properties and does not set x-kubernetes-preserve-unknown-fields: true.

These fields use empty placeholder types. If they are not supported, remove them from ClusterConfiguration. If they must accept opaque configuration, add +kubebuilder:pruning:PreserveUnknownFields and regenerate the CRD.

🤖 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 `@hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml` around
lines 503 - 522, Update ClusterConfiguration and its generated CRD schema for
apiServer, authentication, featureGate, image, ingress, network, oauth, proxy,
and scheduler: remove these placeholder fields if unsupported, or mark their
source definitions with +kubebuilder:pruning:PreserveUnknownFields and
regenerate the CRD so nested configuration is retained.

Comment thread platform-api/pkg/handlers/nodepool.go Outdated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml`:
- Around line 523-659: Add the requested validation bounds to the source
definitions for KubeletConfig and MachineConfigSpec: set MaxItems on every
listed array, MaxProperties on the six kubelet map fields, and MaxLength on
FileSpec.Contents, SystemdUnit.Contents, and SystemdDropin.Contents. Resolve the
FIPS source of truth between MachineConfigSpec.FIPS and
HostedClusterSpecPassthrough.FIPS, then remove or retain the duplicate
consistently; regenerate the CRD so these validations appear in the schema.

In `@platform-api/pkg/clients/hyperfleetdb/convert.go`:
- Around line 78-79: Update ClusterCRToPlatform and NodePoolCRToPlatform to
explicitly map only customer-visible Spec fields instead of copying complete CR
Specs; omit service-managed accountID, internalID, and internalPoolID from the
REST response objects while preserving all supported customer fields. Add JSON
regression assertions covering these hidden fields’ absence in both converted
responses.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: b728cef6-c8d8-4540-84e1-52c7054a35f8

📥 Commits

Reviewing files that changed from the base of the PR and between 981c997 and b82eb87.

⛔ Files ignored due to path filters (1)
  • api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
📒 Files selected for processing (22)
  • .gitignore
  • Makefile
  • README.md
  • api/v1alpha1/hostedclusterspec.passthrough.go
  • api/v1alpha1/public/openapi.yaml
  • hack/api-codegen/cmd/marker-scanner/main.go
  • hack/api-codegen/cmd/openapi-merge/main.go
  • hack/api-codegen/pkg/markers/gated_writemode_test.go
  • hack/api-codegen/pkg/markers/scanner.go
  • hack/api-codegen/pkg/markers/scanner_test.go
  • hack/api-codegen/pkg/markers/types.go
  • hack/api-codegen/pkg/openapi/generator.go
  • hack/api-codegen/pkg/openapi/generator_test.go
  • hack/api-codegen/pkg/registry/field_metadata.go
  • hack/api-codegen/pkg/registry/field_metadata.json
  • hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
  • platform-api/pkg/clients/hyperfleetdb/convert.go
  • platform-api/pkg/clients/hyperfleetdb/convert_test.go
  • platform-api/pkg/handlers/cluster.go
  • platform-api/pkg/handlers/nodepool.go
  • platform-api/pkg/validation/field_validator.go
  • platform-api/pkg/validation/field_validator_test.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • .gitignore
  • README.md
  • platform-api/pkg/handlers/cluster.go
  • platform-api/pkg/handlers/nodepool.go
  • api/v1alpha1/hostedclusterspec.passthrough.go
  • hack/api-codegen/pkg/markers/scanner.go
  • platform-api/pkg/validation/field_validator_test.go
  • platform-api/pkg/validation/field_validator.go

Comment thread hyperfleet-operator/config/crd/bases/hyperfleet.io_clusters.yaml
Comment thread platform-api/pkg/clients/hyperfleetdb/convert.go
cdoan1 and others added 2 commits August 5, 2026 17:27
…/v1alpha1/public

- Add --rest-output-dir and --rest-package flags to conversion-gen CLI
  to decouple REST type output from conversion functions
- Fix generator: non-struct types (ClusterPhase etc.) correctly qualified,
  runtime.RawExtension import support, REST-local type references unqualified,
  inlined enrichCRD to avoid redeclaration across files
- REST types (visible fields only) generated to api/v1alpha1/public/ (package public),
  importable by SDK and clientset
- Conversion functions (Project/Unproject) generated to platform-api/pkg/conversion/
- ServiceSetFields struct generated to platform-api/pkg/conversion/types.go
- Makefile: codegen-conversion, verify-conversion targets
- Regenerated OpenAPI spec to reflect current marker state

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

The generated ServiceSetFields type references configv1.URL directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cdoan1 cdoan1 changed the title ROSAENG-61801: feat: Adding passthrough types rework v2 ROSAENG-61801: feat: enable api-management v2 Aug 6, 2026
cdoan1 and others added 4 commits August 5, 2026 18:21
…r on unmapped hidden fields

Add MaxProperties/MaxItems/MaxLength constraints to KubeletConfig maps,
MachineConfigSpec arrays, and content string fields. Remove MachineConfigSpec.FIPS
(duplicate of HostedClusterSpecPassthrough.FIPS). Make filterHiddenFields return
an error when an unmapped definition has properties matching hidden registry entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…vice-set fields

Replace full spec replacement with JSON merge in ApplyPlatformUpdateToClusterCR
and ApplyPlatformUpdateToNodePoolCR. Omitted fields (zero/nil with omitempty)
are preserved in the existing spec, eliminating the need for manual restoration
and covering all nested service-set fields in passthrough types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove bufio.Scanner's 64KB token limit from YAML line processing in
jsonSchemaToYAML and splitLines. Use strings.Split which has no size limit
and needs no error handling for in-memory data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mark Replicas as +k8s:openapi-gen=true and +hyperfleet:write-mode=mutable
so customers can set desired node count. Regenerate field registry, REST
types, OpenAPI spec, and conversion types to reflect the change.

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

cdoan1 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/test on-demand-e2e

@typeid typeid left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's looking great, added a few final comments to address then this should be good to merge.

Comment thread platform-api/pkg/clients/hyperfleetdb/convert.go Outdated
Comment thread platform-api/pkg/conversion/types.go Outdated
Comment thread hyperfleet-operator/internal/render/cluster.go
Comment thread platform-api/pkg/handlers/cluster_test.go
…ge, buildFieldPath, test compilation

Address blocking and non-blocking review comments from typeid on PR openshift-online#284:

Blocking:
- Replace typed mergeSpec with raw JSON MergeSpecJSON to prevent data loss
  on non-omitempty passthrough fields (hostedCluster, nodePool)
- Merge only APIServer into existing Configuration instead of overwriting

Non-blocking:
- Fix buildFieldPath to produce correct registry paths for KubeletConfig,
  MachineConfigSpec, and ClusterConfiguration nested types
- Regenerate ServiceSetFields with correct types ([]string, []FileSpec, etc.)
- Fix integration tests: HostedClusterSpec → HostedClusterSpecPassthrough,
  add missing defaultClusterExpiration arg to NewClusterHandler
- Remove stale public/machineconfigspec_types.go hidden fields
- Fix gofmt in convert.go

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

cdoan1 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Blocking 1 — mergeSpec data loss on non-omitempty fields:

  • Root cause: mergeSpec marshaled the typed Go struct, which serializes zero-value non-omitempty fields (hostedCluster, nodePool, platform, release, etc.) as empty objects, overwriting real values.
  • Fix: Handlers now buffer the raw request body, extract the "spec" field as json.RawMessage, and pass it to MergeSpecJSON. Since the raw JSON only contains fields the caller actually sent, omitted fields are never touched.
  • Removed: ApplyPlatformUpdateToClusterCR, ApplyPlatformUpdateToNodePoolCR, mergeSpec (all replaced by MergeSpecJSON).

Blocking 2 — Configuration overwrite drops customer kubelet/machineConfig:

  • Root cause: hcSpec.Configuration = apiServerConfiguration() unconditionally replaced the entire Configuration with only the API server serving cert config.
  • Fix: If Configuration is already set (customer provided kubelet/machineConfig), only set the APIServer field. If nil, set the whole struct.

Non-blocking fixes:
5. hack/api-codegen/pkg/conversion/generator.go — Fixed buildFieldPath to produce correct registry paths for KubeletConfig, MachineConfigSpec, ClusterConfiguration before the generic Spec suffix
6. platform-api/pkg/conversion/types.go — Regenerated with correct types (Extensions []string, Files []v1alpha1.FileSpec, etc.)
7. platform-api/pkg/handlers/cluster_test.go — Fixed HostedClusterSpec → HostedClusterSpecPassthrough, added missing defaultClusterExpiration arg to NewClusterHandler
8. api/v1alpha1/public/machineconfigspec_types.go — Deleted (stale generated file for removed MachineConfigSpec.FIPS field)

@cdoan1

cdoan1 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

/test on-demand-e2e

@typeid typeid left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

/lgtm
/approve

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Aug 6, 2026
@openshift-ci

openshift-ci Bot commented Aug 6, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: cdoan1, typeid

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-merge-bot
openshift-merge-bot Bot merged commit ff8e995 into openshift-online:main Aug 6, 2026
14 checks passed
@cdoan1
cdoan1 deleted the ROSAENG-61801-passthrough-types-rework-v2 branch August 6, 2026 17:09
openshift-merge-bot Bot pushed a commit that referenced this pull request Aug 7, 2026
Reflects the repo restructure from PRs #283 and #284: CRD types moved
from hyperfleet-operator/api/ to top-level api/, new clientset and
codegen modules added. Fixes stale binary paths in rate-limit docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants