Skip to content

ECOPROJECT-3992 | feat: sizer - support hosted control plane nodes#970

Merged
openshift-merge-bot[bot] merged 1 commit intokubev2v:mainfrom
ronenav:3992-hosted-control-plane
Mar 19, 2026
Merged

ECOPROJECT-3992 | feat: sizer - support hosted control plane nodes#970
openshift-merge-bot[bot] merged 1 commit intokubev2v:mainfrom
ronenav:3992-hosted-control-plane

Conversation

@ronenav
Copy link
Copy Markdown
Collaborator

@ronenav ronenav commented Mar 10, 2026

When hostedControlPlane is true, omit control plane machine set and workload from the sizer payload and return 0 control plane nodes.

Summary by CodeRabbit

  • New Features

    • Added hostedControlPlane flag to the cluster requirements API to opt out of control-plane sizing so only worker nodes are sized.
  • Bug Fixes / Behavior

    • Validation and sizing now enforce hostedControlPlane rules, rejecting control-plane fields when hosted.
    • Field descriptions clarified and explicit default values removed for several control-plane settings.
  • Tests

    • Added tests covering hosted control plane validation and sizing behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 10, 2026

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

Walkthrough

Adds a new boolean field hostedControlPlane to the ClusterRequirementsRequest schema and propagates it through mapping, validation, and sizing logic so control-plane fields are excluded when the flag is true; updates generated OpenAPI/spec payload and adjusts tests accordingly.

Changes

Cohort / File(s) Summary
OpenAPI & generated specs/types
api/v1alpha1/openapi.yaml, api/v1alpha1/spec.gen.go, api/v1alpha1/types.gen.go
Add hostedControlPlane to OpenAPI schema, remove some explicit defaults in descriptions, regenerate embedded swagger payload, and add HostedControlPlane *bool to generated types.
API → internal mapping
internal/handlers/v1alpha1/mappers/inbound.go, internal/service/mappers/inbound.go
Propagate HostedControlPlane from API request into internal ClusterRequirementsRequestForm; add form field and conditionally populate control-plane fields.
Handler validation
internal/handlers/v1alpha1/sizer.go
Add validation to forbid specifying control-plane fields when hostedControlPlane is true; gate prior control-plane validations behind the flag.
Service sizing logic
internal/service/sizer.go
Introduce internal helper to compute effective control-plane node count (0 when hosted), invert includeControlPlane, pass effective count into payload/tracing/transform, and record hosted state.
Tests
internal/handlers/v1alpha1/sizer_test.go, internal/service/sizer_test.go
Add unit tests for hosted control-plane scenarios and validation cases that assert errors when control-plane fields are provided while hosted.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant APIHandler as "API Handler\n(inbound mapper)"
  participant Mapper as "Service Mapper\n(ClusterRequirementsRequestForm)"
  participant SizerSvc as "Sizing Service"
  participant External as "External Sizer"

  Client->>APIHandler: POST ClusterRequirementsRequest (hostedControlPlane?)
  APIHandler->>Mapper: map request -> form (propagate HostedControlPlane)
  Mapper->>SizerSvc: CalculateClusterRequirements(form)
  alt hostedControlPlane == true
    SizerSvc->>SizerSvc: effectiveCP = 0\nincludeControlPlane = false
  else
    SizerSvc->>SizerSvc: effectiveCP = ControlPlaneNodeCount\nincludeControlPlane = true
  end
  SizerSvc->>External: POST /api/v1/size/custom (payload uses effectiveCP)
  External-->>SizerSvc: sizer response
  SizerSvc-->>APIHandler: transformed sizing result (controlPlaneNodes, workerNodes, total)
  APIHandler-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • machacekondra
  • tupyy
  • bardielle
  • nirarg

Poem

🐇 I hopped a flag into the tree,
If CP is hosted, zero it be,
Mappers whisper what I say,
Sizer counts the worker way,
Tiny hops — more room to play!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main feature: adding support for hosted control plane nodes in the sizer, which aligns with all the changes made across API definitions, handlers, and tests.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/handlers/v1alpha1/sizer_test.go`:
- Around line 357-391: The test currently leaves ControlPlaneNodeCount nil so it
doesn't exercise the new "bypass on HostedControlPlane" validation branch;
modify the test's request to set ControlPlaneNodeCount to an otherwise-invalid
value (e.g., cp := -1; request.ControlPlaneNodeCount = &cp) while keeping
hostedTrue := true so that handlers.CalculateAssessmentClusterRequirements (and
the ClusterRequirementsRequest) demonstrates that hostedControlPlane=true skips
the handler-side check and still returns 200.

In `@internal/service/sizer_test.go`:
- Around line 299-315: The test currently only asserts the transformed response;
add assertions that the outbound HTTP payload sent to the mock created by
createTestSizerServer (the request hitting /api/v1/size/custom) omits the
control-plane machine set and any control-plane workload when
request.HostedControlPlane is true — capture the incoming request body in the
test server handler, decode/inspect the JSON payload and assert the controlPlane
machineSet/workload fields are absent or empty before calling
sizerService.CalculateClusterRequirements (refer to the test's
request.HostedControlPlane, createTestSizerServer, and the /api/v1/size/custom
handler to locate where to capture and assert the outbound payload).

In `@internal/service/sizer.go`:
- Line 271: Normalize the effective control-plane node count once and reuse it
everywhere instead of recalculating or leaking the raw
req.ControlPlaneNodeCount: compute a single variable (e.g., effectiveCP :=
effectiveControlPlaneNodeCount(req)) before building the payload and before
calling transformSizerResponse, pass effectiveCP into buildSizerPayload (or
ensure buildSizerPayload uses the provided effectiveCP) and use effectiveCP in
any tracing/logging instead of req.ControlPlaneNodeCount; update references
around transformSizerResponse, buildSizerPayload, includeControlPlane and the
trace/span recording so they all use the same normalized effectiveCP value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0502a39d-40c7-4010-8ce6-fa84ad513aca

📥 Commits

Reviewing files that changed from the base of the PR and between 925b563 and 8b1f63b.

📒 Files selected for processing (9)
  • api/v1alpha1/openapi.yaml
  • api/v1alpha1/spec.gen.go
  • api/v1alpha1/types.gen.go
  • internal/handlers/v1alpha1/mappers/inbound.go
  • internal/handlers/v1alpha1/sizer.go
  • internal/handlers/v1alpha1/sizer_test.go
  • internal/service/mappers/inbound.go
  • internal/service/sizer.go
  • internal/service/sizer_test.go

@ronenav ronenav force-pushed the 3992-hosted-control-plane branch from 8b1f63b to a095a95 Compare March 10, 2026 11:56
@AvielSegev
Copy link
Copy Markdown
Collaborator

/retest

@ronenav ronenav force-pushed the 3992-hosted-control-plane branch from a095a95 to 57d5719 Compare March 15, 2026 10:46
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/service/sizer_test.go`:
- Around line 333-336: The test currently checks shapes but not scheduling;
update the assertions to verify the workload is scheduled only on the "worker"
machineset by asserting sizerPayload.Workloads[0].MachineSetNames (or the field
that lists target machinesets on the Workload struct) has length 1 and contains
exactly "worker" (e.g. Expect(...).To(HaveLen(1)) and
Expect(...).To(ContainElement("worker")) or an equality assertion to
[]string{"worker"}), so the hosted-control-plane contract is locked to the
worker-only placement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e132fe04-d815-412e-89c1-cd4aa914778a

📥 Commits

Reviewing files that changed from the base of the PR and between a095a95 and 57d5719.

📒 Files selected for processing (9)
  • api/v1alpha1/openapi.yaml
  • api/v1alpha1/spec.gen.go
  • api/v1alpha1/types.gen.go
  • internal/handlers/v1alpha1/mappers/inbound.go
  • internal/handlers/v1alpha1/sizer.go
  • internal/handlers/v1alpha1/sizer_test.go
  • internal/service/mappers/inbound.go
  • internal/service/sizer.go
  • internal/service/sizer_test.go

@ronenav ronenav force-pushed the 3992-hosted-control-plane branch from 57d5719 to cad296c Compare March 15, 2026 13:20
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
internal/service/sizer_test.go (1)

333-337: ⚠️ Potential issue | 🟡 Minor

Add explicit assertion for worker-only workload placement.

The test validates shape, but not that the workload targets only the worker machineset.

🔧 Suggested assertion
 				Expect(sizerPayload.Workloads).To(HaveLen(1))
 				Expect(sizerPayload.Workloads[0].Name).To(Equal("vm-workload"))
+				Expect(sizerPayload.Workloads[0].UsesMachines).To(Equal([]string{"worker"}))

As per coding guidelines, "Coverage: Strive for high test coverage on critical logic paths."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/service/sizer_test.go` around lines 333 - 337, The test currently
checks sizerPayload.MachineSets and sizerPayload.Workloads but doesn't assert
that the workload is targeted only at the "worker" machineset; update the test
to explicitly assert the workload's placement/target references the worker set
by examining sizerPayload.Workloads[0] (for example its Placement,
TargetMachineSet, or AllowedMachineSets field) and asserting it equals or
contains only "worker" (and assert no other targets are present).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/service/sizer_test.go`:
- Around line 333-337: The test currently checks sizerPayload.MachineSets and
sizerPayload.Workloads but doesn't assert that the workload is targeted only at
the "worker" machineset; update the test to explicitly assert the workload's
placement/target references the worker set by examining
sizerPayload.Workloads[0] (for example its Placement, TargetMachineSet, or
AllowedMachineSets field) and asserting it equals or contains only "worker" (and
assert no other targets are present).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ff5853ed-6371-4024-89b4-18b843ad559d

📥 Commits

Reviewing files that changed from the base of the PR and between 57d5719 and cad296c.

📒 Files selected for processing (9)
  • api/v1alpha1/openapi.yaml
  • api/v1alpha1/spec.gen.go
  • api/v1alpha1/types.gen.go
  • internal/handlers/v1alpha1/mappers/inbound.go
  • internal/handlers/v1alpha1/sizer.go
  • internal/handlers/v1alpha1/sizer_test.go
  • internal/service/mappers/inbound.go
  • internal/service/sizer.go
  • internal/service/sizer_test.go

@ronenav ronenav force-pushed the 3992-hosted-control-plane branch from cad296c to f45ff4d Compare March 16, 2026 12:04
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
internal/service/sizer_test.go (1)

333-337: 🧹 Nitpick | 🔵 Trivial

Strengthen payload assertion for worker-only scheduling.

The test verifies the payload structure but should also assert that the workload is scheduled only on "worker" machines to fully lock the hosted-control-plane contract.

🔧 Suggested assertion
 				Expect(sizerPayload.MachineSets).To(HaveLen(1))
 				Expect(sizerPayload.MachineSets[0].Name).To(Equal("worker"))
 				Expect(sizerPayload.Workloads).To(HaveLen(1))
 				Expect(sizerPayload.Workloads[0].Name).To(Equal("vm-workload"))
+				Expect(sizerPayload.Workloads[0].UsesMachines).To(Equal([]string{"worker"}))

As per coding guidelines, "Coverage: Strive for high test coverage on critical logic paths."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/service/sizer_test.go` around lines 333 - 337, Add an assertion that
the workload is scheduled exclusively to the "worker" machines by checking the
workload scheduling field used in the payload (e.g.,
sizerPayload.Workloads[0].MachineSetNames or the workload's
NodeSelector/Affinity field) and assert it equals/contains only "worker" (for
example expect the slice to equal []string{"worker"} or the NodeSelector to be
map[string]string{"node-role.kubernetes.io/worker": "true"}); update the test
around sizerPayload.Workloads[0] where you already assert the name to include
this exclusive-scheduling check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/service/sizer_test.go`:
- Around line 333-337: Add an assertion that the workload is scheduled
exclusively to the "worker" machines by checking the workload scheduling field
used in the payload (e.g., sizerPayload.Workloads[0].MachineSetNames or the
workload's NodeSelector/Affinity field) and assert it equals/contains only
"worker" (for example expect the slice to equal []string{"worker"} or the
NodeSelector to be map[string]string{"node-role.kubernetes.io/worker": "true"});
update the test around sizerPayload.Workloads[0] where you already assert the
name to include this exclusive-scheduling check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6f2778be-3c26-459c-bcff-3e0842b4f701

📥 Commits

Reviewing files that changed from the base of the PR and between cad296c and f45ff4d.

📒 Files selected for processing (9)
  • api/v1alpha1/openapi.yaml
  • api/v1alpha1/spec.gen.go
  • api/v1alpha1/types.gen.go
  • internal/handlers/v1alpha1/mappers/inbound.go
  • internal/handlers/v1alpha1/sizer.go
  • internal/handlers/v1alpha1/sizer_test.go
  • internal/service/mappers/inbound.go
  • internal/service/sizer.go
  • internal/service/sizer_test.go

Signed-off-by: Ronen Avraham <ravraham@redhat.com>
@ronenav ronenav force-pushed the 3992-hosted-control-plane branch from f45ff4d to c5e87ce Compare March 17, 2026 09:45
@ronenav ronenav requested a review from a team as a code owner March 17, 2026 09:45
@ronenav ronenav requested a review from AvielSegev March 17, 2026 09:45
@openshift-ci
Copy link
Copy Markdown

openshift-ci bot commented Mar 19, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: nirarg

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 37720cc into kubev2v:main Mar 19, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants