Skip to content

OSAC-2244: resolve fabric_interface from HostType for CaaS node sets - #294

Open
ori-amizur wants to merge 1 commit into
osac-project:mainfrom
ori-amizur:OSAC-2244
Open

OSAC-2244: resolve fabric_interface from HostType for CaaS node sets#294
ori-amizur wants to merge 1 commit into
osac-project:mainfrom
ori-amizur:OSAC-2244

Conversation

@ori-amizur

@ori-amizur ori-amizur commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Add fabric_interface field (field 3) to the private ClusterNodeSet proto. During cluster creation, when network_attachment is set, the server resolves each node set's HostType and selects the first interface with role "fabric". If no fabric interface exists, creation fails with FailedPrecondition.

The resolved value flows through the feedback controller to the ClusterOrder CRD's NodeSetStatus.FabricInterface (added by OSAC-2346), which the operator uses during reconcileNetworking to configure the correct physical NIC on each bare-metal worker.

Assisted-by: Claude Code noreply@anthropic.com

Summary by CodeRabbit

  • New Features
    • Cluster node sets now automatically identify the appropriate fabric network interface when a network attachment is configured.
    • The selected interface is populated for tenant fabric traffic and cannot be set manually.
  • Bug Fixes
    • Cluster creation now reports a clear precondition error when no suitable fabric interface is available.

@openshift-ci-robot

openshift-ci-robot commented Aug 12, 2026

Copy link
Copy Markdown

@ori-amizur: This pull request references OSAC-2244 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 task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Add fabric_interface field (field 3) to the private ClusterNodeSet proto. During cluster creation, when network_attachment is set, the server resolves each node set's HostType and selects the first interface with role "fabric". If no fabric interface exists, creation fails with FailedPrecondition.

The resolved value flows through the feedback controller to the ClusterOrder CRD's NodeSetStatus.FabricInterface (added by OSAC-2346), which the operator uses during reconcileNetworking to configure the correct physical NIC on each bare-metal worker.

Assisted-by: Claude Code noreply@anthropic.com

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 commented Aug 12, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ori-amizur

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

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:01 PM UTC · Completed 4:22 PM UTC

Commit: 453e25f · View workflow run →

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ori-amizur, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 112 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bad98993-23f4-420d-9cc7-a1a5e0e83cd1

📥 Commits

Reviewing files that changed from the base of the PR and between 931bcea and b4aeced.

⛔ Files ignored due to path filters (2)
  • fulfillment-service/internal/api/osac/private/v1/cluster_type.pb.go is excluded by !**/*.pb.go
  • fulfillment-service/internal/api/osac/private/v1/cluster_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (3)
  • fulfillment-service/internal/servers/private_clusters_server.go
  • fulfillment-service/internal/servers/private_clusters_server_test.go
  • fulfillment-service/proto/private/osac/private/v1/cluster_type.proto

Walkthrough

Cluster creation now resolves each attached node set’s fabric interface from its referenced host type. The selected interface name is system-populated in ClusterNodeSet.fabric_interface. Missing fabric interfaces return FailedPrecondition.

Changes

Fabric interface resolution

Layer / File(s) Summary
Node-set contract and fixtures
fulfillment-service/proto/.../cluster_type.proto, fulfillment-service/internal/servers/private_clusters_server_test.go
ClusterNodeSet.fabric_interface records the selected interface. Test host types now include a shared data-0 fabric interface.
Creation-time interface resolution
fulfillment-service/internal/servers/private_clusters_server.go
Cluster creation resolves attached node sets by looking up each host type and selecting its first case-insensitive fabric interface. Missing interfaces return FailedPrecondition.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CreateCluster
  participant resolveFabricInterfaces
  participant HostType
  participant ClusterNodeSet
  CreateCluster->>resolveFabricInterfaces: resolve attached node-set interfaces
  resolveFabricInterfaces->>HostType: look up referenced host type
  HostType-->>resolveFabricInterfaces: return host type interfaces
  resolveFabricInterfaces->>ClusterNodeSet: set fabric interface name
  resolveFabricInterfaces-->>CreateCluster: return success or FailedPrecondition
Loading

Suggested reviewers: sk-ilya, omer-vishlitzky

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes resolving fabric_interface from HostType for CaaS node sets, which is the main change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No-Hardcoded-Secrets ✅ Passed The PR diff adds only interface metadata, protobuf descriptors, and resolver code; searches found no API keys, tokens, passwords, private keys, credential URLs, or long encoded literals.
No-Weak-Crypto ✅ Passed The HEAD^→HEAD diff adds only interface resolution and a protobuf field; scans found no MD5, SHA1, DES, RC4, Blowfish, ECB, custom crypto, or secret comparison.
No-Injection-Vectors ✅ Passed The diff adds interface selection and a protobuf field; it adds no shell, eval/exec, YAML, pickle, or HTML sinks, and host lookup uses quoted CEL input with SQL escaping.
Container-Privileges ✅ Passed The diff changes only Go, protobuf, and test files; it adds no container/Kubernetes manifests or privilege settings such as privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEs...
No-Sensitive-Data-In-Logs ✅ Passed The diff adds no logging. The resolver only reads HostType interfaces, sets fabric_interface, or returns an error; no passwords, tokens, PII, hostnames, or customer data are logged.
Ai-Attribution ✅ Passed AI use is declared in the PR tip commit with Assisted-by: Claude Code; it also has a Red Hat Signed-off-by, and no AI Co-Authored-By trailer was introduced.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

coderabbitai[bot]
coderabbitai Bot previously requested changes Aug 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
fulfillment-service/internal/servers/private_clusters_server_test.go (1)

133-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for the new fabric_interface contract.

The fixture now provides data-0, but the test file does not assert that a network-attached cluster stores GetFabricInterface() == "data-0". It also does not cover first-match ordering, no attachment, or a host type without a fabric interface returning FailedPrecondition. Add focused tests for these cases.

🤖 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 `@fulfillment-service/internal/servers/private_clusters_server_test.go` around
lines 133 - 137, Add focused tests in the private cluster server test suite
covering the new fabric_interface contract: assert a network-attached cluster
stores GetFabricInterface() as "data-0", verify the first matching fabric
interface is selected when multiple interfaces exist, verify no attachment
leaves the field unset, and verify a host type without a fabric interface
returns FailedPrecondition. Reuse the existing fixture and server/test helpers
rather than changing production code.
fulfillment-service/internal/servers/private_clusters_server.go (1)

705-735: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reuse resolved HostType records.

Create already looks up request node sets at Lines [190-200]. Both transformation paths also load host types before this helper. resolveFabricInterfaces then performs another DAO lookup for every final node set. Pass the resolved records into this step or resolve the interface during the existing transformation to avoid redundant DAO I/O.

🤖 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 `@fulfillment-service/internal/servers/private_clusters_server.go` around lines
705 - 735, Update resolveFabricInterfaces and its callers to reuse HostType
records already loaded during Create and the node-set transformation paths,
rather than calling lookupHostType for each final node set. Pass the resolved
records into the helper or assign fabric interfaces during transformation while
preserving the existing missing-host-type and missing-fabric-interface behavior.
🤖 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 `@fulfillment-service/internal/servers/private_clusters_server.go`:
- Around line 230-237: Update the Update flow around GenericServer.Update to
retain each stored node set’s system-populated fabric_interface when
spec.node_sets is masked and the request omits it. Validate any client-supplied
fabric_interface against the persisted value and reject changes rather than
accepting them. Add regression tests covering both preservation of omitted
values and rejection of attempted modifications.
- Around line 230-237: Before the network-attachment check in the server
handler, apply the tenant default network_attachment to spec when the request
omits one, using the existing defaulting/transformation mechanism. Ensure this
occurs before resolveFabricInterfaces is called so the defaulted attachment
triggers fabric interface resolution and is persisted; retain the existing
error-return behavior.

---

Nitpick comments:
In `@fulfillment-service/internal/servers/private_clusters_server_test.go`:
- Around line 133-137: Add focused tests in the private cluster server test
suite covering the new fabric_interface contract: assert a network-attached
cluster stores GetFabricInterface() as "data-0", verify the first matching
fabric interface is selected when multiple interfaces exist, verify no
attachment leaves the field unset, and verify a host type without a fabric
interface returns FailedPrecondition. Reuse the existing fixture and server/test
helpers rather than changing production code.

In `@fulfillment-service/internal/servers/private_clusters_server.go`:
- Around line 705-735: Update resolveFabricInterfaces and its callers to reuse
HostType records already loaded during Create and the node-set transformation
paths, rather than calling lookupHostType for each final node set. Pass the
resolved records into the helper or assign fabric interfaces during
transformation while preserving the existing missing-host-type and
missing-fabric-interface behavior.
🪄 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: osac-project/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a0d1a553-d96f-4585-a901-ae4ba439c392

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc8491 and 453e25f.

⛔ Files ignored due to path filters (2)
  • fulfillment-service/internal/api/osac/private/v1/cluster_type.pb.go is excluded by !**/*.pb.go
  • fulfillment-service/internal/api/osac/private/v1/cluster_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (3)
  • fulfillment-service/internal/servers/private_clusters_server.go
  • fulfillment-service/internal/servers/private_clusters_server_test.go
  • fulfillment-service/proto/private/osac/private/v1/cluster_type.proto

Comment on lines +230 to +237
// Resolve fabric_interface for each node set when the cluster has a
// network attachment. The HostType's interfaces list is searched for
// the first interface with role "fabric".
if spec.GetNetworkAttachment() != nil {
if err = s.resolveFabricInterfaces(ctx, spec); err != nil {
return
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 \
  'fabric_interface|FabricInterface|SetFabricInterface|Update|spec\.node_sets' \
  fulfillment-service/internal/servers/private_clusters_server.go \
  fulfillment-service/internal/servers/private_clusters_server_test.go

Repository: osac-project/osac

Length of output: 50374


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Create/update and fabric resolution ---'
sed -n '200,280p' fulfillment-service/internal/servers/private_clusters_server.go
sed -n '700,760p' fulfillment-service/internal/servers/private_clusters_server.go

printf '%s\n' '--- Node-set validation and transformation ---'
sed -n '403,440p' fulfillment-service/internal/servers/private_clusters_server.go
sed -n '737,860p' fulfillment-service/internal/servers/private_clusters_server.go

printf '%s\n' '--- Generic update implementation and field-mask semantics ---'
rg -n -C 5 'func .*Update|proto\.Merge|UpdateMask|FieldMask|Merge' fulfillment-service/internal -g '*.go' | head -n 240

printf '%s\n' '--- Fabric-interface declarations and update tests ---'
rg -n -C 6 'fabric_interface|FabricInterface|resolveFabricInterfaces|network_attachment' fulfillment-service -g '*.go' -g '*.proto' | head -n 320

Repository: osac-project/osac

Length of output: 50373


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- GenericServer definition ---'
rg -l 'type GenericServer|func \(.*GenericServer.*\) Update' fulfillment-service
rg -n -C 12 'type GenericServer|func \(.*GenericServer.*\) Update' fulfillment-service -g '*.go' | head -n 220

printf '%s\n' '--- Exact cluster field declarations ---'
rg -l 'fabric_interface|message ClusterNodeSet|ClusterNodeSet' fulfillment-service/proto fulfillment-service/internal | head -n 80
rg -n -C 10 'fabric_interface|message ClusterNodeSet' fulfillment-service/proto -g '*.proto'

printf '%s\n' '--- Cluster test cases containing fabric or network attachment ---'
rg -n -C 12 'fabric_interface|FabricInterface|network_attachment|NetworkAttachment' \
  fulfillment-service/internal/servers/private_clusters_server_test.go

printf '%s\n' '--- Update mask helper and merge behavior ---'
rg -n -C 10 'Apply.*Update|apply.*Update|update mask|updateMask|proto.Merge|MergeFrom|FieldMask' \
  fulfillment-service/internal fulfillment-service/pkg fulfillment-service -g '*.go' \
  | rg -v 'mock|_test.go' | head -n 300

Repository: osac-project/osac

Length of output: 50375


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Complete GenericServer.Update merge and persistence flow ---'
sed -n '608,790p' fulfillment-service/internal/servers/generic_server.go

printf '%s\n' '--- Field-path implementation ---'
rg -n -C 8 'func .*compilePaths|type .*FieldPath|func .*Set|func .*Clear|func .*Get' \
  fulfillment-service/internal/masks fulfillment-service/internal/servers -g '*.go' \
  | head -n 260

printf '%s\n' '--- All cluster update validators ---'
sed -n '560,710p' fulfillment-service/internal/servers/private_clusters_server.go
sed -n '1000,1095p' fulfillment-service/internal/servers/private_clusters_server_test.go

printf '%s\n' '--- Relevant source invariant probe ---'
python3 - <<'PY'
from pathlib import Path

server = Path("fulfillment-service/internal/servers/private_clusters_server.go").read_text()
generic = Path("fulfillment-service/internal/servers/generic_server.go").read_text()
proto = Path("fulfillment-service/proto/private/osac/private/v1/cluster_type.proto").read_text()

checks = {
    "create_resolves_fabric_only": (
        "resolveFabricInterfaces(ctx, spec)" in server
        and "func (s *PrivateClustersServer) Update" in server
        and server.index("resolveFabricInterfaces(ctx, spec)")
        < server.index("func (s *PrivateClustersServer) Update")
    ),
    "generic_mask_replaces_masked_value": (
        "fieldPath.Set(tmpObject, value)" in generic
        and 'tmpObject = requestObject' in generic
    ),
    "fabric_is_not_user_settable_by_contract_only": (
        "Not user-settable." in proto
        and "string fabric_interface = 3;" in proto
    ),
    "no_update_fabric_validation": "fabric_interface" not in server[
        server.index("func (s *PrivateClustersServer) Update"):
        server.index("func (s *PrivateClustersServer) Delete")
    ],
}

for name, result in checks.items():
    print(f"{name}: {'PASS' if result else 'FAIL'}")
PY

Repository: osac-project/osac

Length of output: 39446


Preserve and protect fabric_interface during Update.

When spec.node_sets is masked, GenericServer.Update replaces the node-set map with the request map. Existing fabric_interface values are therefore cleared because update requests omit this system-populated field. The update path also accepts client-supplied fabric_interface values. Preserve stored values and reject client changes, with regression tests for both cases.

🤖 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 `@fulfillment-service/internal/servers/private_clusters_server.go` around lines
230 - 237, Update the Update flow around GenericServer.Update to retain each
stored node set’s system-populated fabric_interface when spec.node_sets is
masked and the request omits it. Validate any client-supplied fabric_interface
against the persisted value and reject changes rather than accepting them. Add
regression tests covering both preservation of omitted values and rejection of
attempted modifications.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 \
  'setDefaults|injectDefaultNetworkAttachment|GetNetworkAttachment|resolveFabricInterfaces' \
  fulfillment-service/internal/servers/private_clusters_server.go

Repository: osac-project/osac

Length of output: 4043


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Create and defaulting flow ---'
sed -n '180,245p' fulfillment-service/internal/servers/private_clusters_server.go
sed -n '280,300p' fulfillment-service/internal/servers/private_clusters_server.go

printf '%s\n' '--- Fabric resolution implementation ---'
sed -n '705,755p' fulfillment-service/internal/servers/private_clusters_server.go

printf '%s\n' '--- Relevant symbols across the repository ---'
rg -n -C 4 \
  'injectDefaultNetworkAttachment|DefaultNetworkingReady|network_attachment|fabric_interface' \
  fulfillment-service/internal fulfillment-service/proto \
  -g '*.go' -g '*.proto' | head -n 400

Repository: osac-project/osac

Length of output: 50373


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Cluster transformation paths ---'
rg -n -C 12 \
  'func \(s \*PrivateClustersServer\) validateAndTransform(CatalogItem|Cluster)|SetNetworkAttachment|NetworkAttachment_builder|networkAttachment' \
  fulfillment-service/internal/servers/private_clusters_server.go

printf '%s\n' '--- Cluster-specific default-network symbols ---'
rg -n -C 5 \
  'PrivateClustersServer|findDefaultSubnet|findDefaultSecurity|default subnet|default security|ClusterNetworkAttachment_builder|SetNetworkAttachment' \
  fulfillment-service/internal/servers \
  -g '*.go' | grep -E 'private_clusters|Cluster|cluster|default|NetworkAttachment|network_attachment' | head -n 300

printf '%s\n' '--- All direct writes to ClusterSpec.network_attachment ---'
rg -n -C 3 \
  'SetNetworkAttachment|NetworkAttachment_builder|network_attachment' \
  fulfillment-service/internal/servers/private_clusters_server.go \
  fulfillment-service/internal/controllers \
  -g '*.go' 2>/dev/null | head -n 300

Repository: osac-project/osac

Length of output: 33559


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- validateAndTransformCluster ---'
sed -n '737,947p' fulfillment-service/internal/servers/private_clusters_server.go

printf '%s\n' '--- validateAndTransformCatalogItem ---'
sed -n '948,1085p' fulfillment-service/internal/servers/private_clusters_server.go

Repository: osac-project/osac

Length of output: 12232


Inject the tenant default network_attachment before resolving fabric_interface. setDefaults only initializes spec and status, and neither transformation path injects tenant networking. A request without network_attachment therefore skips resolveFabricInterfaces and persists no fabric_interface, despite the API contract.

🤖 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 `@fulfillment-service/internal/servers/private_clusters_server.go` around lines
230 - 237, Before the network-attachment check in the server handler, apply the
tenant default network_attachment to spec when the request omits one, using the
existing defaulting/transformation mechanism. Ensure this occurs before
resolveFabricInterfaces is called so the defaulted attachment triggers fabric
interface resolution and is persisted; retain the existing error-return
behavior.

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review

Findings

High

  • [missing-test] fulfillment-service/internal/servers/private_clusters_server_test.go — No dedicated tests for resolveFabricInterfaces. The PR adds Interfaces: fabricInterfaces to host type fixtures so existing tests don't break, but no test verifies that fabric_interface is populated correctly on resulting node sets. Missing coverage: (1) happy path — create cluster with network_attachment, assert fabric_interface equals expected interface name; (2) error path — HostType without fabric interface, assert FailedPrecondition error; (3) skip path — no network_attachment, assert empty fabric_interface.
    Remediation: Add tests covering all three paths.

Low

  • [logic-error] fulfillment-service/internal/servers/private_clusters_server.go:713resolveFabricInterfaces has two defensive guards that are effectively unreachable: (1) continue on empty hostTypeKeymergeNodeSetsWithTemplate already populates every node set's HostType from the template; (2) hostType == nil guard — lookupHostType returns NotFound error, not nil, so the preceding err != nil check already returns.

  • [edge-case] fulfillment-service/proto/private/osac/private/v1/cluster_type.proto:150fabric_interface is documented as "Not user-settable" but validateNodeSetsUpdate does not check its immutability on Update. Create path is fine (always overwrites via resolveFabricInterfaces).

  • [pattern-inconsistency] fulfillment-service/internal/servers/private_clusters_server.go:730 — Error message uses node_sets[%s]: map-index notation; all other node set errors in this file use natural language node set '%s' phrasing.

  • [pattern-inconsistency] fulfillment-service/proto/private/osac/private/v1/cluster_type.proto:149 — Comment uses "System-populated" / "Not user-settable" instead of the established (system-provided, read-only) idiom used elsewhere in proto files.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run

Review

Findings

Medium

  • [test adequacy] fulfillment-service/internal/servers/private_clusters_server_test.go — No test cases added for the new resolveFabricInterfaces behavior. The PR modifies host type fixtures so existing tests continue to pass, but none of the critical code paths are exercised: (1) creating a cluster with network_attachment and verifying fabric_interface is populated on each node set, (2) creating a cluster when the host type has no interface with role "fabric" and expecting FailedPrecondition, (3) verifying fabric_interface is empty when no network_attachment is set. Adding these test cases would improve confidence in the resolution logic.
    Remediation: Add test cases that (a) create a cluster with a network_attachment and assert each node set's fabric_interface equals the expected interface name; (b) create a host type without a fabric interface and assert cluster creation returns FailedPrecondition; (c) verify fabric_interface is empty on clusters created without a network_attachment.

  • [API contract violation] fulfillment-service/internal/servers/private_clusters_server.go:234 — The Update method does not enforce immutability of fabric_interface. The proto documents fabric_interface as "Not user-settable" and it is derived from the immutable host_type, yet a caller could include spec.node_sets in the update mask and overwrite fabric_interface. Since host_type is already validated as immutable via validateNodeSetHostTypeImmutability, applying similar protection to fabric_interface would close this gap.
    Remediation: Add validation in validateNodeSetsUpdate that rejects changes to fabric_interface, or strip/re-resolve it on the Update path.

Low

  • [edge case] fulfillment-service/internal/servers/private_clusters_server.go:709 — In resolveFabricInterfaces, the if hostType == nil { continue } guard after lookupHostType is dead code. For a non-empty key, lookupHostType returns (result, nil) on success or (nil, error) on failure — it never returns (nil, nil). The empty-key case is already handled by the preceding if hostTypeKey == "" { continue } check.

  • [proto comment style] fulfillment-service/proto/private/osac/private/v1/cluster_type.proto:149 — The comment uses "System-populated ... Not user-settable" which has no precedent in the proto files. The established convention for system-managed fields is "system-provided, read-only" (used in nat_gateway, virtual_network, subnet, security_group protos) or "populated by the server" (used in secret_type.proto). Aligning with the existing vocabulary would improve consistency.

  • [spec-status-ownership] fulfillment-service/proto/private/osac/private/v1/cluster_type.proto:150 — The fabric_interface field has status-like semantics (system-controlled, not user-settable) but lives in ClusterNodeSet, a shared message used in both ClusterSpec.node_sets and ClusterStatus.node_sets. This follows the existing pattern — host_type and size are also shared — but adding a system-only field to the shared message further blurs the spec/status ownership boundary. Worth noting in the proto comment that the field is server-controlled and ignored if set by the caller in spec.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 12, 2026
@omer-vishlitzky
omer-vishlitzky dismissed coderabbitai[bot]’s stale review August 12, 2026 16:22

Auto-dismissed: only Prow labels gate merging

Add fabric_interface field (field 3) to the private ClusterNodeSet proto.
During cluster creation, when network_attachment is set, the server
resolves each node set's HostType and selects the first interface with
role "fabric". If no fabric interface exists, creation fails with
FailedPrecondition.

The resolved value flows through the feedback controller to the
ClusterOrder CRD's NodeSetStatus.FabricInterface (added by OSAC-2346),
which the operator uses during reconcileNetworking to configure the
correct physical NIC on each bare-metal worker.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Ori Amizur <oamizur@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 13, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:21 AM UTC · Completed 10:39 AM UTC

Commit: b4aeced · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.

if hostTypeKey == "" {
continue
}
hostType, err := s.lookupHostType(ctx, hostTypeKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] logic-error

resolveFabricInterfaces has two defensive guards that are effectively unreachable: (1) continue on empty hostTypeKey -- mergeNodeSetsWithTemplate already populates every node set's HostType from the template; (2) hostType == nil guard -- lookupHostType returns NotFound error, not nil, so the preceding err != nil check already returns.


// Name of the network interface on the HostType used for tenant fabric traffic.
//
// System-populated during cluster creation by selecting the first interface with

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] edge-case

fabric_interface is documented as 'Not user-settable' but validateNodeSetsUpdate does not check its immutability on Update. Create path is fine (always overwrites via resolveFabricInterfaces).

if fabricInterface == "" {
return grpcstatus.Errorf(grpccodes.FailedPrecondition,
"node_sets[%s]: host type '%s' has no interface with role 'fabric'",
name, hostTypeKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] pattern-inconsistency

Error message uses node_sets[%s]: map-index notation; all other node set errors in this file use natural language 'node set' phrasing.

Suggested fix: Reword to match established pattern, e.g.: "host type '%s' for node set '%s' has no interface with role 'fabric'".

int32 size = 2;

// Name of the network interface on the HostType used for tenant fabric traffic.
//

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] pattern-inconsistency

Comment uses 'System-populated' / 'Not user-settable' instead of the established (system-provided, read-only) idiom used elsewhere in proto files.

Suggested fix: Adopt the established (system-provided, read-only) phrasing.

@fullsend-ai-review fullsend-ai-review Bot removed the requires-manual-review Review requires human judgment label Aug 13, 2026
@omer-vishlitzky
omer-vishlitzky dismissed fullsend-ai-review[bot]’s stale review August 13, 2026 10:39

Auto-dismissed: only Prow labels gate merging

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants