Skip to content

feat(rest-api): Tenant TargetedInstanceCreation capability implementation#2134

Open
hwadekar-nv wants to merge 12 commits into
NVIDIA:mainfrom
hwadekar-nv:feat/tenant-capabilities-update
Open

feat(rest-api): Tenant TargetedInstanceCreation capability implementation#2134
hwadekar-nv wants to merge 12 commits into
NVIDIA:mainfrom
hwadekar-nv:feat/tenant-capabilities-update

Conversation

@hwadekar-nv

@hwadekar-nv hwadekar-nv commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Description

Tenant TargetedInstanceCreation capability

  • Currently We allow Tenant to config capability via Tenant config. As it is a global config here which problematic. We can use this for flags that apply to all Providers/Sites. Likely not going to be used unless there is such use case.

  • Today the API Tenant object returns a capabilities object with a single field targetedInstanceCreation. Because this value can vary per Site

  • This PR contain Tenant to configure capabilities for custom sites as well as providers, also we will deprecate field config from Tenant and use newly approach to validate and configure capabilities (TenantAccount)

Type of Change

  • Add - New feature or capability

Related Issues (Optional)

#2104

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated

Additional Notes

@hwadekar-nv hwadekar-nv self-assigned this Jun 2, 2026
@hwadekar-nv hwadekar-nv requested a review from a team as a code owner June 2, 2026 21:51
@hwadekar-nv hwadekar-nv marked this pull request as draft June 2, 2026 21:51
@copy-pr-bot

copy-pr-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-06-02 21:53:17 UTC | Commit: 1feb71c

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🔍 Container Scan Summary

Service Total Critical High Medium Low Other
nico-flow 15 1 3 3 0 8
nico-nsm 7 0 1 5 0 1
nico-psm 15 1 3 3 0 8
nico-rest-api 17 1 4 4 0 8
nico-rest-cert-manager 14 1 3 3 0 7
nico-rest-db 15 1 3 3 0 8
nico-rest-site-agent 15 1 3 3 0 8
nico-rest-site-manager 14 1 3 3 0 7
nico-rest-workflow 15 1 3 3 0 8
TOTAL 127 8 26 30 0 63

Per-CVE detail lives in the per-service grype-* artifacts (JSON + SARIF). Severity counts only — no CVE IDs published here.

@hwadekar-nv hwadekar-nv changed the title feat: Tenant TargetedInstanceCreation capability implementation feat(rest-api): Tenant TargetedInstanceCreation capability implementation Jun 3, 2026
@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from 2fff422 to 7d6bcc6 Compare June 3, 2026 22:10
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

This PR moves targeted instance creation from tenant-level config to tenant-account and tenant-site scoped JSONB config, adds capability-resolution helpers, updates authorization checks across handlers, and documents the new siteCapabilities contract and deprecation path.

Changes

Targeted Instance Creation Capability Rework

Layer / File(s) Summary
Storage and migration changes
rest-api/db/pkg/db/model/tenant.go, tenantaccount.go, tenantsite.go, testing.go, tenant_test.go, tenantsite_test.go, rest-api/db/pkg/migrations/*, rest-api/api/pkg/api/model/operatingsystem_test.go, sshkeygroup_test.go
Adds typed TenantAccountConfig/TenantSiteConfig JSONB fields, removes the legacy Tenant.Config field, updates DAO create/update behavior for partial config merges and key removal, adds a migration backfilling tenant_account.config before dropping tenant.config, and updates fixtures/tests to the new config shapes.
Capability resolution helpers
rest-api/api/pkg/api/handler/util/common/common.go, common_test.go
Adds TenantHasTargetedInstanceCreation and GetPrivilegedAccessSiteIDsForTenant, and wires existing privilege checks to use them.
Tenant and tenant-account API models
rest-api/api/pkg/api/model/tenant.go, tenantaccount.go, tenantaccountsitecapability.go, *_test.go
Adds tenant capability deprecation metadata, introduces tenant-account site-capabilities request and response shapes, and extends tenant-account rendering to include tenant sites.
Handler authorization updates
instance.go, machine.go, expectedmachine.go, site.go, sku.go, vpc.go, tenant.go, serviceaccount.go, tenantaccount.go, and associated *_test.go
Switches targeted-instance checks from tenant-level flags to site-scoped effective or aggregate capability checks, and refactors tenant-account updates to handle provider-admin site-capabilities changes and tenant-admin invite acceptance.
TenantAccount update flow
rest-api/api/pkg/api/handler/tenantaccount.go
Moves capability assignment to tenant accounts on service-account creation and refactors tenant-account updates to route between provider-admin site-capabilities changes and tenant-admin invite acceptance.
OpenAPI contract updates
rest-api/openapi/spec.yaml, oasdiff-breaking-changes-ignore.txt
Documents the deprecated tenant capability, new siteCapabilities schemas, and updated update-request contract.
Workflow tenant builder update
rest-api/workflow/pkg/util/testing.go, rest-api/workflow/pkg/activity/*
Removes the config parameter from the workflow tenant builder and updates workflow activity tests to the simplified signature.

Estimated code review effort: 5 (Critical) | ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: implementing Tenant TargetedInstanceCreation capability.
Description check ✅ Passed The description is aligned with the change set and explains the move from tenant config to TenantAccount-based capability handling.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch 2 times, most recently from ec337d8 to eeb42e0 Compare June 4, 2026 20:23
@hwadekar-nv

Copy link
Copy Markdown
Contributor Author

Will start working on this PR after 2.0. We have new strategies to allow tenants to update capabilities via TenantAccount and TenantSite across all Sites or specific Site.

@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch 2 times, most recently from 685f8f1 to c3402d9 Compare July 2, 2026 18:32
@hwadekar-nv hwadekar-nv added this to the v2.1 milestone Jul 2, 2026
@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch 2 times, most recently from 482fc88 to d20920e Compare July 7, 2026 21:31
@hwadekar-nv hwadekar-nv marked this pull request as ready for review July 7, 2026 21:37
@hwadekar-nv

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from d20920e to 3c8f601 Compare July 7, 2026 21:46

@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

Caution

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

⚠️ Outside diff range comments (1)
rest-api/db/pkg/db/model/tenantaccount.go (1)

546-564: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make mixed TenantAccount updates atomic.

Update can run the scalar-column update and the config merge as separate statements. If a caller passes tx == nil with both normal fields and Config, the first update can commit before the config merge fails. Prefer building one UPDATE, or require/wrap mixed updates in cdb.WithTx.

🤖 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 `@rest-api/db/pkg/db/model/tenantaccount.go` around lines 546 - 564, The
TenantAccount update path in the update method currently splits scalar field
changes and Config merging into separate UPDATE statements, which can leave
mixed updates partially applied when tx is nil. Refactor the logic around
updatedFields/input.Config in TenantAccount.Update to perform a single atomic
update when both kinds of changes are present, or ensure mixed updates are
always executed inside cdb.WithTx by wrapping/requiring a transaction before
running db.GetIDB(...).NewUpdate().Exec.

Source: Path instructions

🧹 Nitpick comments (3)
rest-api/api/pkg/api/model/tenantaccountsitecapability.go (1)

199-206: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer a receiver method over a free function on the named type.

GlobalTargetedInstanceCreationFromRequest operates entirely on APITenantAccountSiteCapabilitiesUpdateRequest; expressing it as a method (e.g. caps.GlobalTargetedInstanceCreation()) keeps behavior colocated with the type and reads more naturally at the call site.

As per path instructions: "discourage scattered independent functions when a receiver method would make ownership and responsibilities clearer."

♻️ Proposed conversion to a receiver method
-func GlobalTargetedInstanceCreationFromRequest(caps APITenantAccountSiteCapabilitiesUpdateRequest) *bool {
-	for _, cap := range caps {
+func (caps APITenantAccountSiteCapabilitiesUpdateRequest) GlobalTargetedInstanceCreation() *bool {
+	for _, cap := range caps {
 		if cap.Scope == TenantAccountSiteCapabilityScopeGlobal {
 			return cutil.GetPtr(cap.TargetedInstanceCreation)
 		}
 	}
 	return nil
 }
🤖 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 `@rest-api/api/pkg/api/model/tenantaccountsitecapability.go` around lines 199 -
206, Convert GlobalTargetedInstanceCreationFromRequest into a receiver method on
APITenantAccountSiteCapabilitiesUpdateRequest so the behavior lives with the
type and can be called as caps.GlobalTargetedInstanceCreation(). Keep the same
logic that scans for TenantAccountSiteCapabilityScopeGlobal and returns the
targeted instance creation value, but move it onto the named type and update any
call sites accordingly.

Source: Path instructions

rest-api/db/pkg/db/model/tenantsite_test.go (2)

287-293: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use range-over-integer for this counting loop.

-	for i := 0; i < siteCount; i++ {
+	for i := range siteCount {

As per coding guidelines, “Counting loop → range-over-integer.” As per path instructions, prefer range-based iteration over C-style for loops.

🤖 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 `@rest-api/db/pkg/db/model/tenantsite_test.go` around lines 287 - 293, The
counting loop in the tenant site test should use range-over-integer instead of a
C-style for loop. Update the iteration in the site-building block within the
test that uses TestBuildSite and TestBuildTenantSite so it iterates with a
range-based integer loop over siteCount, while preserving the existing indexing
and even/odd behavior.

Sources: Coding guidelines, Path instructions


602-701: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test case for RemoveTargetedInstanceCreation.

The DAO now has a clear-override branch, but this table only verifies config merge. Add a case that starts with targetedInstanceCreation=true, updates with RemoveTargetedInstanceCreation: true, and asserts the pointer is cleared.

🤖 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 `@rest-api/db/pkg/db/model/tenantsite_test.go` around lines 602 - 701, Add a
missing Update test for the clear-override path in TenantSiteSQLDAO.Update: the
current TestTenantSiteSQLDAO_Update only covers config merge, so add a case that
seeds TenantSiteConfig.TargetedInstanceCreation as true, calls Update with
TenantSiteConfigUpdateInput.RemoveTargetedInstanceCreation set to true, and
asserts the resulting TenantSite.Config.TargetedInstanceCreation is cleared/nil.
Use the existing TenantSiteSQLDAO.Update, TenantSiteUpdateInput, and
TenantSiteConfigUpdateInput symbols to locate the right spot in the table-driven
test.

Source: Path instructions

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

Inline comments:
In `@rest-api/api/pkg/api/handler/machine.go`:
- Line 245: Update the privileged machine read authorization to use the
site-effective targeted-instance capability instead of the legacy tenant
flag/provider-account check. In the machine read paths around the
TenantHasTargetedInstanceCreation logic, switch single-machine reads to
EffectiveTargetedInstanceCreation(ctx, nil, ..., tenant, machine.SiteID), and
for list reads apply the same site-aware rule when siteId is present so
disabled-site machines are not returned in tenant-privileged results. Make sure
the behavior in the machine handler keeps honoring TenantAccount.config, Ready
status, and TenantSite.config overrides.
- Around line 762-768: The site-scoped capability check in IsProviderOrTenant is
being bypassed by the early requirePrivilegedTenant gate, which can reject
legacy TenantAccount tenants before machine.SiteID is evaluated. Update the
IsProviderOrTenant flow to defer the privileged-tenant rejection until after the
EffectiveTargetedInstanceCreation check runs, keeping the machine.SiteID-based
logic as the authoritative permission decision and removing the premature
pre-gate.

In `@rest-api/api/pkg/api/handler/util/common/common.go`:
- Around line 1467-1483: The privileged pre-check is still using the deprecated
tenant-level flag in TenantHasTargetedInstanceCreation, which can incorrectly
reject tenants enabled via the new TenantAccount.config path. Update the
requirePrivileged / requirePrivilegedTenant flow in common.go so non-site-scoped
privilege is resolved from the ready TenantAccount config, or remove this early
gate for site-aware callers and let EffectiveTargetedInstanceCreation be the
authoritative check. Keep the fix localized around
TenantHasTargetedInstanceCreation and the caller(s) that enforce the privileged
gate.

In `@rest-api/db/pkg/db/model/tenantsite.go`:
- Around line 314-334: The TenantSite update path currently applies Config
merging and RemoveTargetedInstanceCreation as separate updates, which can leave
partial state and makes conflicting input ambiguous. Update the logic in the
TenantSite model update flow to validate that both inputs are not used together,
then build a single NewUpdate/Exec path that applies either the JSONB merge or
the targetedInstanceCreation removal, not both. Use the existing update block
around input.Config and input.RemoveTargetedInstanceCreation to centralize the
decision and preserve one atomic update per request.

In
`@rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go`:
- Around line 26-33: The backfill in the migration only applies to Ready
tenant_account rows, so invited or pending accounts that later become Ready will
miss the legacy targetedInstanceCreation capability. Update the migration logic
in the tenant_account/tenant backfill to copy the capability for all matching
tenant_account rows tied to tenants with
tenant.config.targetedInstanceCreation=true, and if the status filter must
remain elsewhere, ensure the status transition path in tenant account handling
also propagates this config field when moving to Ready.

---

Outside diff comments:
In `@rest-api/db/pkg/db/model/tenantaccount.go`:
- Around line 546-564: The TenantAccount update path in the update method
currently splits scalar field changes and Config merging into separate UPDATE
statements, which can leave mixed updates partially applied when tx is nil.
Refactor the logic around updatedFields/input.Config in TenantAccount.Update to
perform a single atomic update when both kinds of changes are present, or ensure
mixed updates are always executed inside cdb.WithTx by wrapping/requiring a
transaction before running db.GetIDB(...).NewUpdate().Exec.

---

Nitpick comments:
In `@rest-api/api/pkg/api/model/tenantaccountsitecapability.go`:
- Around line 199-206: Convert GlobalTargetedInstanceCreationFromRequest into a
receiver method on APITenantAccountSiteCapabilitiesUpdateRequest so the behavior
lives with the type and can be called as caps.GlobalTargetedInstanceCreation().
Keep the same logic that scans for TenantAccountSiteCapabilityScopeGlobal and
returns the targeted instance creation value, but move it onto the named type
and update any call sites accordingly.

In `@rest-api/db/pkg/db/model/tenantsite_test.go`:
- Around line 287-293: The counting loop in the tenant site test should use
range-over-integer instead of a C-style for loop. Update the iteration in the
site-building block within the test that uses TestBuildSite and
TestBuildTenantSite so it iterates with a range-based integer loop over
siteCount, while preserving the existing indexing and even/odd behavior.
- Around line 602-701: Add a missing Update test for the clear-override path in
TenantSiteSQLDAO.Update: the current TestTenantSiteSQLDAO_Update only covers
config merge, so add a case that seeds TenantSiteConfig.TargetedInstanceCreation
as true, calls Update with
TenantSiteConfigUpdateInput.RemoveTargetedInstanceCreation set to true, and
asserts the resulting TenantSite.Config.TargetedInstanceCreation is cleared/nil.
Use the existing TenantSiteSQLDAO.Update, TenantSiteUpdateInput, and
TenantSiteConfigUpdateInput symbols to locate the right spot in the table-driven
test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: aef13451-a675-454a-9c90-57bbe59434be

📥 Commits

Reviewing files that changed from the base of the PR and between 1c22f4d and d20920e.

⛔ Files ignored due to path filters (40)
  • rest-api/sdk/standard/api_tenant_account.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/client.go is excluded by !rest-api/sdk/standard/client.go
  • rest-api/sdk/standard/model_batch_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability_scope.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_capabilities.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
📒 Files selected for processing (21)
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/db/pkg/db/model/tenantsite_test.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/openapi/spec.yaml

Comment thread rest-api/api/pkg/api/handler/machine.go Outdated
Comment thread rest-api/api/pkg/api/handler/machine.go Outdated
Comment thread rest-api/api/pkg/api/handler/util/common/common.go Outdated
Comment thread rest-api/db/pkg/db/model/tenantsite.go Outdated
Comment thread rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go Outdated
@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from 4a12952 to b0ee8a6 Compare July 8, 2026 00:01

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

🧹 Nitpick comments (2)
rest-api/db/pkg/db/model/tenantsite_test.go (1)

287-297: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer range-over-integer for this counting loop.

The index is used solely as a counter (i%2, Sprintf), so this is a textbook candidate for Go's range-over-integer form.

♻️ Suggested refactor
-	sites := []*Site{}
-	siteCount := 30
-	for i := 0; i < siteCount; i++ {
+	sites := []*Site{}
+	siteCount := 30
+	for i := range siteCount {

As per path instructions: "Counting loop → range-over-integer. When the bound is a count ... use for i := range n".

🤖 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 `@rest-api/db/pkg/db/model/tenantsite_test.go` around lines 287 - 297, The
counting loop in the tenant site test uses an index only as a counter for naming
and parity checks, so refactor the loop to use Go’s range-over-integer form
instead of a classic three-part loop. Update the loop in the test setup that
builds sites and calls TestBuildSite/TestBuildTenantSite so the iteration
variable still supports the Sprintf name generation and i%2 branch, but the loop
itself follows the preferred range-over-integer pattern.

Source: Path instructions

rest-api/api/pkg/api/handler/machine.go (1)

305-331: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Redundant per-site resolution creates an N+1 on the machine-list hot path.

EffectiveTargetedInstanceCreation internally re-fetches the Site by ID and re-queries Ready TenantAccounts per provider, yet this loop already holds each site from siteDAO.GetAll and the provider is fixed for the inner iteration. The cost is O(providers × sites) with three DB round-trips per site. Consider resolving the provider's Ready TenantAccount once outside the inner loop and evaluating the per-site TenantSite override directly against the already-loaded site.

🤖 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 `@rest-api/api/pkg/api/handler/machine.go` around lines 305 - 331, The machine
list path has an N+1 problem because the loop in the handler around
siteDAO.GetAll and common.EffectiveTargetedInstanceCreation re-fetches each Site
and re-queries TenantAccount data for every site/provider pair. Refactor this
block so the provider’s Ready TenantAccount is resolved once per provider
outside the inner site loop, then use the already-loaded site data to check the
per-site TenantSite override directly instead of calling
EffectiveTargetedInstanceCreation for every site. Keep the existing error
handling/logging structure in the same handler flow.
🤖 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 `@rest-api/api/pkg/api/handler/machine.go`:
- Around line 294-337: The machine list filtering in the tenant branch is using
provider IDs even though EffectiveTargetedInstanceCreation is evaluated per
site, which can widen results to disabled sites. Update the logic in machine.go
around the tenant/filterSite handling to collect and append enabled site IDs
instead of provider IDs, and apply them through MachineFilterInput.SiteIDs
rather than InfrastructureProviderIDs. Keep the existing
EffectiveTargetedInstanceCreation and SiteDAO.GetAll flow, but change the
aggregation variable and final filter assignment so the list stays scoped to
sites where targeted instance creation is enabled.

---

Nitpick comments:
In `@rest-api/api/pkg/api/handler/machine.go`:
- Around line 305-331: The machine list path has an N+1 problem because the loop
in the handler around siteDAO.GetAll and
common.EffectiveTargetedInstanceCreation re-fetches each Site and re-queries
TenantAccount data for every site/provider pair. Refactor this block so the
provider’s Ready TenantAccount is resolved once per provider outside the inner
site loop, then use the already-loaded site data to check the per-site
TenantSite override directly instead of calling
EffectiveTargetedInstanceCreation for every site. Keep the existing error
handling/logging structure in the same handler flow.

In `@rest-api/db/pkg/db/model/tenantsite_test.go`:
- Around line 287-297: The counting loop in the tenant site test uses an index
only as a counter for naming and parity checks, so refactor the loop to use Go’s
range-over-integer form instead of a classic three-part loop. Update the loop in
the test setup that builds sites and calls TestBuildSite/TestBuildTenantSite so
the iteration variable still supports the Sprintf name generation and i%2
branch, but the loop itself follows the preferred range-over-integer pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8bd15369-c9f6-45e7-b4dd-b1d7c80569ca

📥 Commits

Reviewing files that changed from the base of the PR and between 3c8f601 and b0ee8a6.

⛔ Files ignored due to path filters (40)
  • rest-api/sdk/standard/api_tenant_account.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/client.go is excluded by !rest-api/sdk/standard/client.go
  • rest-api/sdk/standard/model_batch_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability_scope.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_capabilities.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
📒 Files selected for processing (23)
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantaccount_test.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/db/pkg/db/model/tenantsite_test.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/openapi/spec.yaml
✅ Files skipped from review due to trivial changes (1)
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
🚧 Files skipped from review as they are similar to previous changes (14)
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/openapi/spec.yaml
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/api/pkg/api/handler/tenantaccount.go

Comment thread rest-api/api/pkg/api/handler/machine.go Outdated
@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from fdc9a2d to ffd8c7b Compare July 8, 2026 16:38

@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

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

⚠️ Outside diff range comments (3)
rest-api/api/pkg/api/handler/tenantaccount.go (2)

397-464: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

GetAllTenantAccountHandler silently drops per-site capability overrides.

Every list-response APITenantAccount is built with tenantSites=nil (Line 447), while GetTenantAccountHandler (Line 585) and the new PUT response (Line 932) now fetch and pass the actual TenantSite slice. Since NewAPITenantAccount derives SiteCapabilities from tenantSites, the list endpoint will only ever report the global scope and silently omit any limited per-site overrides — producing an inconsistent contract between GET /tenant-account (list) and GET /tenant-account/{id} for the same brand-new field.

Consider batch-fetching TenantSite rows grouped by tenant ID (mirroring the existing allocation-count batching pattern already in this handler) so list responses stay consistent with the detail view.

🤖 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 `@rest-api/api/pkg/api/handler/tenantaccount.go` around lines 397 - 464,
GetAllTenantAccountHandler is building APITenantAccount list items with
tenantSites left nil, so NewAPITenantAccount cannot compute SiteCapabilities and
per-site limited overrides are lost. Update the list path in tenantaccount.go to
batch-load TenantSite rows for the tenants in tas, grouped by tenant ID similar
to the existing allocation-count fetch, and pass the matching tenantSites into
model.NewAPITenantAccount just like GetTenantAccountHandler and the PUT response
do.

907-937: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Hardcoded allocationCount: 0 in the siteCapabilities update response.

model.NewAPITenantAccount(ta, ssds, 0, tenantSites) always reports zero allocations, even though GetTenantAccountHandler computes the real value via aDAO.GetCount(...) for the exact same entity. A Provider Admin updating siteCapabilities on an active TenantAccount with existing allocations will get back a response claiming zero allocations.

🐛 Proposed fix
+	aDAO := cdbm.NewAllocationDAO(utah.dbSession)
+	total := 0
+	if ta.TenantID != nil {
+		cnt, cerr := aDAO.GetCount(ctx, nil, cdbm.AllocationFilterInput{
+			InfrastructureProviderIDs: []uuid.UUID{ta.InfrastructureProviderID},
+			TenantIDs:                 []uuid.UUID{*ta.TenantID},
+		})
+		if cerr != nil {
+			logger.Error().Err(cerr).Msg("error retrieving allocation count for Tenant Account from DB")
+			return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Allocations to determine total allocation for tenant account", nil)
+		}
+		total = cnt
+	}
+
 	sdDAO := cdbm.NewStatusDetailDAO(utah.dbSession)
 	ssds, _, err := sdDAO.GetAll(ctx, nil, cdbm.StatusDetailFilterInput{EntityIDs: []string{ta.ID.String()}}, cdbp.PageInput{Limit: cutil.GetPtr(pagination.MaxPageSize)})
 	if err != nil {
 		logger.Error().Err(err).Msg("error retrieving Status Details for TenantAccount from DB")
 		return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Status Details for TenantAccount", nil)
 	}
 
-	apiInstance := model.NewAPITenantAccount(ta, ssds, 0, tenantSites)
+	apiInstance := model.NewAPITenantAccount(ta, ssds, total, tenantSites)
🤖 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 `@rest-api/api/pkg/api/handler/tenantaccount.go` around lines 907 - 937, The
update response is hardcoding allocationCount to 0 in the call to
model.NewAPITenantAccount, so the returned TenantAccount can incorrectly show no
allocations. Reuse the same allocation-count logic already used in
GetTenantAccountHandler by fetching the real count for ta.ID before building the
response, then pass that value instead of 0 when constructing apiInstance.
rest-api/api/pkg/api/handler/expectedmachine.go (1)

351-365: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mirror the TenantAccount fallback in the ExpectedMachine list filter.

GetAllExpectedMachineHandler only adds TenantSite.SiteID values to filterInput.SiteIDs. A privileged tenant that is authorized through a ready TenantAccount but has no explicit TenantSite row for a provider site will get an empty result set, and a siteId query for that site will be rejected, even though ValidateProviderOrTenantSiteAccess allows the same access path. Reuse the same access resolution here or extract a shared helper.

🤖 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 `@rest-api/api/pkg/api/handler/expectedmachine.go` around lines 351 - 365, The
ExpectedMachine list filter only uses TenantSite rows, so privileged tenants can
miss access granted through a ready TenantAccount. Update
GetAllExpectedMachineHandler to resolve site access the same way
ValidateProviderOrTenantSiteAccess does, or extract and reuse a shared helper,
so filterInput.SiteIDs includes both TenantSite.SiteID values and any provider
sites reachable via the TenantAccount fallback before applying the list filter.

Source: Path instructions

🧹 Nitpick comments (1)
rest-api/api/pkg/api/handler/tenantaccount.go (1)

785-932: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Add handler-level test coverage for the new provider siteCapabilities flow.

This introduces a non-trivial, security-sensitive authorization path (advisory-locked transaction, global vs. limited scope resolution, stale-override cleanup, cross-provider site scoping) with no accompanying tenantaccount_test.go changes in this layer. Given the privilege-granting nature of this endpoint, targeted tests (ownership rejection, stale-override cleanup, limited-site-not-owned-by-provider rejection) would materially reduce regression risk.

Based on path instructions: "Review Go code for correctness, clean control flow, error handling, context propagation, test coverage, performance, and cohesive organization..."

🤖 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 `@rest-api/api/pkg/api/handler/tenantaccount.go` around lines 785 - 932, Add
handler-level tests for handleProviderSiteCapabilitiesUpdate in tenantaccount.go
to cover the new provider siteCapabilities flow. Exercise the security-sensitive
branches by verifying ownership/provider rejection, limited-site validation when
a Site is not owned by the Tenant Account Infrastructure Provider, and stale
TenantSite override cleanup when a site is removed from the limited set. Use the
existing UpdateTenantAccountHandler, TenantAccountDAO/TenantSiteDAO behavior,
and the advisory-locked transaction path to locate the right handler logic.

Source: Path instructions

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

Outside diff comments:
In `@rest-api/api/pkg/api/handler/expectedmachine.go`:
- Around line 351-365: The ExpectedMachine list filter only uses TenantSite
rows, so privileged tenants can miss access granted through a ready
TenantAccount. Update GetAllExpectedMachineHandler to resolve site access the
same way ValidateProviderOrTenantSiteAccess does, or extract and reuse a shared
helper, so filterInput.SiteIDs includes both TenantSite.SiteID values and any
provider sites reachable via the TenantAccount fallback before applying the list
filter.

In `@rest-api/api/pkg/api/handler/tenantaccount.go`:
- Around line 397-464: GetAllTenantAccountHandler is building APITenantAccount
list items with tenantSites left nil, so NewAPITenantAccount cannot compute
SiteCapabilities and per-site limited overrides are lost. Update the list path
in tenantaccount.go to batch-load TenantSite rows for the tenants in tas,
grouped by tenant ID similar to the existing allocation-count fetch, and pass
the matching tenantSites into model.NewAPITenantAccount just like
GetTenantAccountHandler and the PUT response do.
- Around line 907-937: The update response is hardcoding allocationCount to 0 in
the call to model.NewAPITenantAccount, so the returned TenantAccount can
incorrectly show no allocations. Reuse the same allocation-count logic already
used in GetTenantAccountHandler by fetching the real count for ta.ID before
building the response, then pass that value instead of 0 when constructing
apiInstance.

---

Nitpick comments:
In `@rest-api/api/pkg/api/handler/tenantaccount.go`:
- Around line 785-932: Add handler-level tests for
handleProviderSiteCapabilitiesUpdate in tenantaccount.go to cover the new
provider siteCapabilities flow. Exercise the security-sensitive branches by
verifying ownership/provider rejection, limited-site validation when a Site is
not owned by the Tenant Account Infrastructure Provider, and stale TenantSite
override cleanup when a site is removed from the limited set. Use the existing
UpdateTenantAccountHandler, TenantAccountDAO/TenantSiteDAO behavior, and the
advisory-locked transaction path to locate the right handler logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 337a24c6-0bfa-4a62-ab56-b6e48d7222c7

📥 Commits

Reviewing files that changed from the base of the PR and between b0ee8a6 and ffd8c7b.

⛔ Files ignored due to path filters (40)
  • rest-api/sdk/standard/api_tenant_account.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/client.go is excluded by !rest-api/sdk/standard/client.go
  • rest-api/sdk/standard/model_batch_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability_scope.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_capabilities.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
📒 Files selected for processing (31)
  • rest-api/api/pkg/api/handler/dpureprovision_test.go
  • rest-api/api/pkg/api/handler/expectedmachine.go
  • rest-api/api/pkg/api/handler/expectedmachine_test.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/instance_test.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/handler/machinehealth_test.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/sku_test.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/util/common/testing.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/handler/vpc_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantaccount_test.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/db/pkg/db/model/tenantsite_test.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/openapi/spec.yaml
✅ Files skipped from review due to trivial changes (2)
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
🚧 Files skipped from review as they are similar to previous changes (20)
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/openapi/spec.yaml
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/db/pkg/db/model/tenantaccount_test.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantsite_test.go

@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from 1375da6 to 04ce4d9 Compare July 8, 2026 17:09
@hwadekar-nv

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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

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

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

⚠️ Outside diff range comments (1)
rest-api/api/pkg/api/handler/sku_test.go (1)

202-220: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

"Without capability" fixture doesn't actually test capability-disabled — it tests missing-account.

In both TestGetAllSkuHandler_Handle (lines 202-220) and TestGetSkuHandler_Handle (lines 477-495), tenantWithoutCapability is inserted with no corresponding TenantAccount at all — identical in setup to tenantWithoutAccount. Since EffectiveTargetedInstanceCreation returns false, nil whenever no Ready TenantAccount row exists, the "tenant without TargetedInstanceCreation capability is denied" test case exercises the exact same code path (len(tas) == 0) as "tenant without TenantAccount with Provider is denied", rather than the intended path where a Ready TenantAccount exists with Config.TargetedInstanceCreation: false.

To genuinely cover the capability-disabled branch, give tenantWithoutCapability a Ready TenantAccount with an explicit Config: cdbm.TenantAccountConfig{TargetedInstanceCreation: false} (mirroring how tenantAccount is built for tenantWithCapability).

Based on learnings, "ensure each failure-path test intentionally targets a single validation branch... construct fixtures so they satisfy all prerequisite conditions except the one being tested."

🧪 Proposed fix to isolate the capability-disabled branch
 	// Create tenant WITHOUT TargetedInstanceCreation capability
 	tenantOrgNoCapability := "test-tenant-org-no-capability"
 	tenantWithoutCapability := &cdbm.Tenant{
 		ID:   uuid.New(),
 		Name: "test-tenant-no-capability",
 		Org:  tenantOrgNoCapability,
 	}
 	_, err = dbSession.DB.NewInsert().Model(tenantWithoutCapability).Exec(ctx)
 	assert.Nil(t, err)
+
+	// Ready TenantAccount that explicitly disables the capability, so this
+	// case is distinct from "no TenantAccount at all".
+	tenantAccountNoCapability := &cdbm.TenantAccount{
+		ID:                       uuid.New(),
+		AccountNumber:            "test-account-no-capability",
+		TenantID:                 &tenantWithoutCapability.ID,
+		TenantOrg:                tenantOrgNoCapability,
+		InfrastructureProviderID: infraProv.ID,
+		Status:                   cdbm.TenantAccountStatusReady,
+		Config:                   cdbm.TenantAccountConfig{TargetedInstanceCreation: false},
+	}
+	_, err = dbSession.DB.NewInsert().Model(tenantAccountNoCapability).Exec(ctx)
+	assert.Nil(t, err)

Also applies to: 477-495

🤖 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 `@rest-api/api/pkg/api/handler/sku_test.go` around lines 202 - 220, The
“without capability” setup in the SKU handler tests is currently
indistinguishable from the “without tenant account” case because
`tenantWithoutCapability` never gets a Ready `TenantAccount`, so
`EffectiveTargetedInstanceCreation` exits on the missing-account branch instead
of the capability-disabled branch. Update the fixtures in
`TestGetAllSkuHandler_Handle` and `TestGetSkuHandler_Handle` so the
`tenantWithoutCapability` tenant has a Ready `TenantAccount` with
`Config.TargetedInstanceCreation` explicitly set to false, while keeping
`tenantWithoutAccount` free of any account rows. Use the existing
`tenantAccount`/`EffectiveTargetedInstanceCreation` setup as the reference point
so each test isolates a single validation path.

Source: Learnings

🧹 Nitpick comments (1)
rest-api/api/pkg/api/handler/site.go (1)

889-926: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Collapse the duplicate TenantAccount lookup into a single query.

common.TenantHasTargetedInstanceCreation already fetches the tenant's Ready TenantAccount rows and scans their Config.TargetedInstanceCreation. Immediately afterwards, the privileged branch issues a second taDAO.GetAll with the identical TenantIDs + Ready filter and re-scans the same flag. On a list endpoint this is an avoidable extra round-trip per Tenant request. Fetch the accounts once here and derive providerIDs directly, dropping the separate capability probe.

♻️ Proposed single-query shape
		taDAO := cdbm.NewTenantAccountDAO(gash.dbSession)
		tas, _, serr := taDAO.GetAll(ctx, nil, cdbm.TenantAccountFilterInput{
			TenantIDs: []uuid.UUID{tenant.ID},
			Statuses:  []string{cdbm.TenantAccountStatusReady},
		}, cdbp.PageInput{Limit: cutil.GetPtr(cdbp.TotalLimit)}, nil)
		if serr != nil {
			logger.Error().Err(serr).Msg("error retrieving Tenant Accounts for Tenant")
			return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Tenant Accounts", nil)
		}

		providerIDs := make([]uuid.UUID, 0, len(tas))
		for _, ta := range tas {
			if ta.Config.TargetedInstanceCreation {
				providerIDs = append(providerIDs, ta.InfrastructureProviderID)
			}
		}

		if len(providerIDs) > 0 {
			// ...fetch providerSites as before
		}
🤖 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 `@rest-api/api/pkg/api/handler/site.go` around lines 889 - 926, The privileged
Tenant path is doing a duplicate Ready TenantAccount fetch after
common.TenantHasTargetedInstanceCreation already checked the same capability,
causing an extra query. In site.go’s Tenant site lookup flow, remove the
separate capability probe and use the existing TenantAccountDAO/GetAll result
once to derive providerIDs from ta.Config.TargetedInstanceCreation. Keep the
providerSites fetch in the same branch, and update the error handling/messages
around the single TenantAccount lookup in the relevant site handler logic.
🤖 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.

Outside diff comments:
In `@rest-api/api/pkg/api/handler/sku_test.go`:
- Around line 202-220: The “without capability” setup in the SKU handler tests
is currently indistinguishable from the “without tenant account” case because
`tenantWithoutCapability` never gets a Ready `TenantAccount`, so
`EffectiveTargetedInstanceCreation` exits on the missing-account branch instead
of the capability-disabled branch. Update the fixtures in
`TestGetAllSkuHandler_Handle` and `TestGetSkuHandler_Handle` so the
`tenantWithoutCapability` tenant has a Ready `TenantAccount` with
`Config.TargetedInstanceCreation` explicitly set to false, while keeping
`tenantWithoutAccount` free of any account rows. Use the existing
`tenantAccount`/`EffectiveTargetedInstanceCreation` setup as the reference point
so each test isolates a single validation path.

---

Nitpick comments:
In `@rest-api/api/pkg/api/handler/site.go`:
- Around line 889-926: The privileged Tenant path is doing a duplicate Ready
TenantAccount fetch after common.TenantHasTargetedInstanceCreation already
checked the same capability, causing an extra query. In site.go’s Tenant site
lookup flow, remove the separate capability probe and use the existing
TenantAccountDAO/GetAll result once to derive providerIDs from
ta.Config.TargetedInstanceCreation. Keep the providerSites fetch in the same
branch, and update the error handling/messages around the single TenantAccount
lookup in the relevant site handler logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7da1fca7-0ffc-484c-b108-5ebb970e2e6b

📥 Commits

Reviewing files that changed from the base of the PR and between ffd8c7b and df699c6.

⛔ Files ignored due to path filters (42)
  • rest-api/sdk/standard/api_tenant.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/api_tenant_account.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/client.go is excluded by !rest-api/sdk/standard/client.go
  • rest-api/sdk/standard/model_batch_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability_scope.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_capabilities.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_summary.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
📒 Files selected for processing (50)
  • rest-api/api/pkg/api/handler/dpureprovision_test.go
  • rest-api/api/pkg/api/handler/expectedmachine.go
  • rest-api/api/pkg/api/handler/expectedmachine_test.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/instance_test.go
  • rest-api/api/pkg/api/handler/instancebatch_test.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/handler/machinehealth_test.go
  • rest-api/api/pkg/api/handler/rack_test.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/site.go
  • rest-api/api/pkg/api/handler/site_test.go
  • rest-api/api/pkg/api/handler/sku.go
  • rest-api/api/pkg/api/handler/sku_test.go
  • rest-api/api/pkg/api/handler/tenant.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/tray_test.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/util/common/testing.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/handler/vpc_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/db/pkg/db/model/tenant.go
  • rest-api/db/pkg/db/model/tenant_test.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/db/pkg/db/model/tenantsite_test.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/docs/index.html
  • rest-api/openapi/oasdiff-breaking-changes-ignore.txt
  • rest-api/openapi/spec.yaml
  • rest-api/workflow/pkg/activity/dpuextensionservice/dpuextensionservice_test.go
  • rest-api/workflow/pkg/activity/infinibandpartition/infinibandpartition_test.go
  • rest-api/workflow/pkg/activity/instance/instance_test.go
  • rest-api/workflow/pkg/activity/nvlinklogicalpartition/nvlinklogicalpartition_test.go
  • rest-api/workflow/pkg/activity/operatingsystem/operatingsystem_test.go
  • rest-api/workflow/pkg/activity/site/site_test.go
  • rest-api/workflow/pkg/activity/sshkeygroup/sshkeygroup_test.go
  • rest-api/workflow/pkg/activity/tenant/tenant_test.go
  • rest-api/workflow/pkg/util/testing.go
🚧 Files skipped from review as they are similar to previous changes (19)
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/api/pkg/api/handler/vpc_test.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/handler/dpureprovision_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/openapi/oasdiff-breaking-changes-ignore.txt
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/handler/util/common/testing.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/db/pkg/db/model/tenantsite_test.go

@hwadekar-nv

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks !

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

@hwadekar-nv Sounds good — kicking off a full review of all changes in this PR now.

ᕕ( ᐛ )ᕗ

✅ Action performed

Full review finished.

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

Caution

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

⚠️ Outside diff range comments (2)
rest-api/db/pkg/db/model/tenantaccount.go (1)

504-574: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Make this update atomic when tx is nil. TenantAccountSQLDAO.Update performs two independent writes when Config is set, and rest-api/api/pkg/api/handler/serviceaccount.go:162 calls it with tx == nil. A failure in the second UPDATE can leave the row partially applied; wrap this path in a transaction or collapse it into one statement.

🤖 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 `@rest-api/db/pkg/db/model/tenantaccount.go` around lines 504 - 574,
TenantAccountSQLDAO.Update currently performs separate write operations when
input.Config is set, so a nil tx call from the service account handler can leave
the tenant account partially updated if the second write fails. Update the
Update method to make the no-transaction path atomic by either wrapping the full
update flow in a transaction when tx is nil or combining the field and config
changes into a single database statement; keep the logic centered in
TenantAccountSQLDAO.Update and preserve the final GetByID fetch after the atomic
write completes.
rest-api/api/pkg/api/handler/sku_test.go (1)

202-210: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

tenantWithoutCapability no longer exercises the "capability disabled" branch — it's now a duplicate of tenantWithoutAccount.

Both tenantWithoutCapability (Lines 202-210 / 477-485) and tenantWithoutAccount (Lines 212-220 / 487-495) create a bare Tenant with no TenantAccount row. EffectiveTargetedInstanceCreation treats "no Ready TenantAccount" and "TenantAccount exists but Config.TargetedInstanceCreation == false" as distinct branches — this test previously covered the latter via Tenant.Config.TargetedInstanceCreation: false, but that signal was dropped without an equivalent TenantAccount fixture, so the two "denied" subtests now assert the exact same code path under different names.

Based on learnings, failure-path tests should target a single, correctly-named validation branch with fixtures that isolate it.

🧪 Proposed fix to restore branch coverage
 	// Create tenant WITHOUT TargetedInstanceCreation capability
 	tenantOrgNoCapability := "test-tenant-org-no-capability"
 	tenantWithoutCapability := &cdbm.Tenant{
 		ID:   uuid.New(),
 		Name: "test-tenant-no-capability",
 		Org:  tenantOrgNoCapability,
 	}
 	_, err = dbSession.DB.NewInsert().Model(tenantWithoutCapability).Exec(ctx)
 	assert.Nil(t, err)
+
+	// Ready TenantAccount that explicitly disables the capability, so this
+	// test isolates the "capability disabled" branch, not "no account".
+	tenantAccountNoCapability := &cdbm.TenantAccount{
+		ID:                       uuid.New(),
+		AccountNumber:            "test-account-no-capability",
+		TenantID:                 &tenantWithoutCapability.ID,
+		TenantOrg:                tenantOrgNoCapability,
+		InfrastructureProviderID: infraProv.ID,
+		Status:                   cdbm.TenantAccountStatusReady,
+		Config:                   cdbm.TenantAccountConfig{TargetedInstanceCreation: false},
+	}
+	_, err = dbSession.DB.NewInsert().Model(tenantAccountNoCapability).Exec(ctx)
+	assert.Nil(t, err)

Apply the analogous change in TestGetSkuHandler_Handle (Lines 477-485).

Also applies to: 477-485

🤖 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 `@rest-api/api/pkg/api/handler/sku_test.go` around lines 202 - 210,
`tenantWithoutCapability` is no longer testing the disabled-capability branch
and now duplicates `tenantWithoutAccount`; update the fixtures in
`TestEffectiveTargetedInstanceCreation` and the matching
`TestGetSkuHandler_Handle` case so each subtest isolates a distinct
`EffectiveTargetedInstanceCreation` path. Keep `tenantWithoutCapability` backed
by a ready `TenantAccount` with `Config.TargetedInstanceCreation` set to false,
and leave `tenantWithoutAccount` as the no-`TenantAccount` case, so the two
denied scenarios exercise different branches again.

Source: Learnings

♻️ Duplicate comments (1)
rest-api/db/pkg/db/model/tenantsite.go (1)

285-339: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate before writing — the mutual-exclusion check still runs after a DB mutation.

The Config/RemoveTargetedInstanceCreation mutual-exclusion check (lines 315-317) executes after the EnableSerialConsole update (lines 300-313) has already been sent to the database. If a caller passes EnableSerialConsole plus a conflicting Config+RemoveTargetedInstanceCreation combination, the EnableSerialConsole change is committed even though Update ultimately returns an error — a partial, silently-persisted write on an "invalid input" error path.

Move the validation to the top of the function, before any write occurs.

🛡️ Proposed fix — validate before any write
 func (tssd TenantSiteSQLDAO) Update(ctx context.Context, tx *db.Tx, input TenantSiteUpdateInput) (*TenantSite, error) {
 	// Create a child span and set the attributes for current request
 	ctx, tnsDAOSpan := tssd.tracerSpan.CreateChildInCurrentContext(ctx, "TenantSiteDAO.Update")
 	if tnsDAOSpan != nil {
 		defer tnsDAOSpan.End()
 
 		tssd.tracerSpan.SetAttribute(tnsDAOSpan, "id", input.TenantSiteID.String())
 	}
 
+	if input.Config != nil && input.RemoveTargetedInstanceCreation {
+		return nil, errors.Wrap(db.ErrInvalidParams, "Config and RemoveTargetedInstanceCreation are mutually exclusive")
+	}
+
 	ts := &TenantSite{
 		ID: input.TenantSiteID,
 	}
 
 	updatedFields := []string{}
 
 	if input.EnableSerialConsole != nil {
 		ts.EnableSerialConsole = *input.EnableSerialConsole
 		updatedFields = append(updatedFields, "enable_serial_console")
 		tssd.tracerSpan.SetAttribute(tnsDAOSpan, "enable_serial_console", *input.EnableSerialConsole)
 	}
 
 	if len(updatedFields) > 0 {
 		updatedFields = append(updatedFields, "updated")
 
 		_, err := db.GetIDB(tx, tssd.dbSession).NewUpdate().Model(ts).Column(updatedFields...).Where("id = ?", input.TenantSiteID).Exec(ctx)
 		if err != nil {
 			return nil, err
 		}
 	}
 
-	if input.Config != nil && input.RemoveTargetedInstanceCreation {
-		return nil, errors.Wrap(db.ErrInvalidParams, "Config and RemoveTargetedInstanceCreation are mutually exclusive")
-	}
-
 	switch {
🤖 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 `@rest-api/db/pkg/db/model/tenantsite.go` around lines 285 - 339, Move the
Config and RemoveTargetedInstanceCreation mutual-exclusion validation in
TenantSiteSQLDAO.Update to the top of the method, before any database write or
EnableSerialConsole update occurs. Keep the existing error handling with
errors.Wrap(db.ErrInvalidParams, ...) but run it before building ts or calling
db.GetIDB(...).NewUpdate().Exec so invalid input cannot partially persist
changes.
🧹 Nitpick comments (7)
rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go (1)

15-59: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add a dedicated test for this migration.

This migration moves capability data and drops a column irreversibly, yet — unlike the historical tenantConfigUpMigration (covered by Test_tenantConfigUpMigration in migrations_test.go) — it ships without any unit test verifying the backfill predicates or the column drop.

As per coding guidelines, "Add or update focused tests for bug fixes, shared behavior, API contracts, migrations, and cross-module changes."

🤖 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
`@rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go`
around lines 15 - 59, Add a focused migration test for the new tenant account
site capabilities migration in the same test suite that covers
tenantConfigUpMigration. The test should exercise the migration function
registered in init, verify the backfill predicates for targetedInstanceCreation
and enableSshAccess on tenant_account.config, and confirm that tenant.config is
dropped after the up migration. Use the migration registration and the existing
migration test helpers as the entry points so the test stays aligned with the
actual apply path.

Source: Coding guidelines

rest-api/workflow/pkg/util/testing.go (1)

227-238: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Config removal is correct, but the identically-named sibling helper is a landmine.

Dropping config *cdbm.TenantConfig here tracks cleanly with cdbm.Tenant no longer carrying a Config field. No objection to the removal itself.

However, rest-api/db/pkg/db/model/testing.go exposes another TestBuildTenant with the reversed first two string parameters — (name, org, user) vs. this one's (org, orgDisplayName, user). That collision is exactly what produced the argument-swap bugs flagged in operatingsystem_test.go and site_test.go in this same PR (callers reached for the wrong ordering because two same-named helpers disagree on it). Consider aligning both helpers' parameter order (or renaming one, e.g. TestBuildTenantWithDisplayName) to eliminate this trap going forward.

♻️ Illustrative alignment (pick one helper's order and mirror it)
-func TestBuildTenant(t *testing.T, dbSession *cdb.Session, org string, orgDisplayName string, user *cdbm.User) *cdbm.Tenant {
+// Parameter order matches db/pkg/db/model.TestBuildTenant: (name, org, user).
+func TestBuildTenant(t *testing.T, dbSession *cdb.Session, orgDisplayName string, org string, user *cdbm.User) *cdbm.Tenant {
🤖 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 `@rest-api/workflow/pkg/util/testing.go` around lines 227 - 238, The two
TestBuildTenant helpers are inconsistent: this util.TestBuildTenant takes (org,
orgDisplayName, user) while the sibling db/pkg/db/model.TestBuildTenant uses a
different string order, which caused callers to swap arguments accidentally.
Align the parameter order across both TestBuildTenant functions, or rename one
of them (for example, TestBuildTenantWithDisplayName) so the intended ordering
is unambiguous. Update any call sites to match the chosen signature, especially
the helper references used in tenant-related tests.
rest-api/api/pkg/api/handler/instance.go (1)

4881-4917: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hoist the IsRepairTenant authorization read out of the transaction.

The EffectiveTargetedInstanceCreation capability check at Line 4908 is a pure authorization read that depends only on instance.Tenant and instance.SiteID, both resolved before the transaction begins. As written, the closure first mutates state (Update to Terminating at Line 4883 and a StatusDetail insert) and only then verifies the caller is permitted to set IsRepairTenant. While the rollback preserves correctness, performing writes ahead of an authorization gate is an inversion of control flow that pins a DB connection for the duration of the check.

Relocate the check above cdb.WithTx (passing nil for the transaction), returning cutil.NewAPIErrorResponse on the forbidden path.

As per path instructions: "Pure input-validation reads ... hold the tx open and pin a DB connection for no benefit. Move them above the cdb.WithTx(...) call and start the tx only when writes begin."

🤖 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 `@rest-api/api/pkg/api/handler/instance.go` around lines 4881 - 4917, The
`IsRepairTenant` authorization check in `instance.go` is happening inside
`cdb.WithTx` after state-changing writes have already started; move the
`common.EffectiveTargetedInstanceCreation` call above the transaction, using the
already-loaded `instance.Tenant` and `instance.SiteID` and passing a nil
transaction if needed. If the capability is disabled, return
`cutil.NewAPIErrorResponse` (forbidden) before opening the transaction, and keep
`cdb.WithTx`, `instanceDAO.Update`, and `StatusDetailDAO.Create` limited to the
actual write path only.

Source: Path instructions

rest-api/api/pkg/api/handler/tenantaccount.go (1)

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

Unused dbUser parameter.

handleProviderSiteCapabilitiesUpdate takes dbUser *cdbm.User but never references it in the body, unlike its sibling handleTenantInviteAcceptance which uses it for the contact-identity check. If it isn't needed for this provider-admin flow (e.g. for audit trail on the capability change), drop it; otherwise this is a revive/golangci-lint unused-parameter target worth verifying against CI config.

🤖 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 `@rest-api/api/pkg/api/handler/tenantaccount.go` at line 781, The
handleProviderSiteCapabilitiesUpdate method currently takes dbUser *cdbm.User
but never uses it, so update the method signature and all call sites to remove
the unused parameter if it is not needed for this provider-admin flow; if it is
intended for audit or authorization, reference it inside the method similarly to
handleTenantInviteAcceptance so the parameter is no longer unused and lint
checks pass.
rest-api/api/pkg/api/model/tenantaccountsitecapability.go (3)

105-107: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Misleading error message when zero global entries are supplied.

validationErrorDuplicateGlobalScope ("only one global siteCapabilities entry is allowed") is returned both when globalCount > 1 and when globalCount == 0. In the zero case the message tells the caller the opposite of the actual problem. The handler already uses a clearer phrase for this exact concept ("siteCapabilities must include exactly one global entry", tenantaccount.go:812) — reuse it here.

🐛 Proposed fix
-	if globalCount != 1 {
-		return validation.Errors{"siteCapabilities": fmt.Errorf(validationErrorDuplicateGlobalScope)}
-	}
+	if globalCount != 1 {
+		return validation.Errors{"siteCapabilities": fmt.Errorf("siteCapabilities must include exactly one global entry")}
+	}
🤖 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 `@rest-api/api/pkg/api/model/tenantaccountsitecapability.go` around lines 105 -
107, The validation in tenant account site capability handling uses
validationErrorDuplicateGlobalScope for every non-1 globalCount, which makes the
zero-global case return a misleading message. Update the check around the
siteCapabilities validation so it distinguishes between globalCount == 0 and
globalCount > 1, and reuse the clearer “siteCapabilities must include exactly
one global entry” wording already used in tenantaccount.go rather than the
duplicate-global message for both cases.

182-197: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

parseSiteCapabilitySiteIDs appears unused and duplicates handler logic.

This helper isn't called anywhere in the reviewed handler; handleProviderSiteCapabilitiesUpdate (tenantaccount.go:834-841) re-implements the identical uuid.Parse loop inline instead of reusing it. Either wire the handler up to call this helper (removing the duplication) or delete the dead code.

🤖 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 `@rest-api/api/pkg/api/model/tenantaccountsitecapability.go` around lines 182 -
197, The parseSiteCapabilitySiteIDs helper is currently dead code and duplicates
the UUID parsing loop already in handleProviderSiteCapabilitiesUpdate. Update
the handler to call parseSiteCapabilitySiteIDs so the parsing logic is
centralized, or remove parseSiteCapabilitySiteIDs entirely if it is not meant to
be reused; use the existing function name and the handler name to locate the
duplicated logic.

52-110: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Reinvented validation instead of the canonical typed-slice pattern.

Validate() hand-rolls per-entry iteration, ad-hoc fmt.Errorf errors, and inline anonymous validation.By(func(value interface{}) error {...}) closures for the global/limited scope checks. The established convention here is a typed-slice Validate() that delegates per-element checks via validation.Validate(s, validation.Each()) and pulls cross-field checks into named methods referenced with validation.By(t.methodName) (see APIMachineCapabilities.Validate as the canonical shape). As written, this is harder to unit-test per-rule and diverges from the pattern used elsewhere for the same problem shape (list + per-element + list-level constraints).

♻️ Suggested direction
-func (caps APITenantAccountSiteCapabilitiesUpdateRequest) Validate() error {
-	...
-			validation.Field(&cap.SiteIDs,
-				validation.When(cap.Scope == TenantAccountSiteCapabilityScopeGlobal,
-					validation.By(func(value interface{}) error { ... })),
-				validation.When(cap.Scope == TenantAccountSiteCapabilityScopeLimited,
-					validation.By(func(value interface{}) error { ... }),
-					validation.Each(validationis.UUID.Error(validationErrorInvalidUUID)),
-				),
-			),
-	...
-}
+func (cap APITenantAccountSiteCapability) Validate() error {
+	return validation.ValidateStruct(&cap,
+		validation.Field(&cap.Scope, validation.Required, validation.In(tenantAccountSiteCapabilityScopes...)),
+		validation.Field(&cap.SiteIDs, validation.By(cap.validateSiteIDsForScope)),
+	)
+}
+
+func (cap APITenantAccountSiteCapability) validateSiteIDsForScope(value interface{}) error { ... }
+
+func (caps APITenantAccountSiteCapabilitiesUpdateRequest) Validate() error {
+	if err := validation.Validate([]APITenantAccountSiteCapability(caps), validation.Each()); err != nil {
+		return err
+	}
+	return caps.validateListLevelRules() // global-count-exactly-one, duplicate siteIds
+}
🤖 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 `@rest-api/api/pkg/api/model/tenantaccountsitecapability.go` around lines 52 -
110, `APITenantAccountSiteCapabilitiesUpdateRequest.Validate` is hand-rolled
instead of following the typed-slice validation pattern used elsewhere. Refactor
it to use `validation.Validate(..., validation.Each(...))` for per-item checks
and move the scope/siteID cross-field rules into named methods on
`APITenantAccountSiteCapabilitiesUpdateRequest` so they can be referenced with
`validation.By(...)` like `APIMachineCapabilities.Validate`. Keep the existing
rules (global/limited scope, UUID validation, duplicate site IDs, single global
entry), but express them through named validators rather than inline anonymous
closures and ad-hoc iteration.

Source: Path instructions

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

Inline comments:
In `@rest-api/api/pkg/api/handler/tenantaccount.go`:
- Around line 781-820: The `handleProviderSiteCapabilitiesUpdate` flow reads
`ta` before entering `cdb.WithTx`, then uses that stale snapshot to build the
advisory lock key and perform ownership checks inside the transaction. Re-fetch
the `TenantAccount` with `taDAO.GetByID` after acquiring the lock inside the
`cdb.WithTx` closure, and use that refreshed entity for the
`TenantID`/`InfrastructureProviderID` validation and subsequent update so the
check-and-write happen against the same transactional snapshot.

In `@rest-api/api/pkg/api/handler/util/common/common.go`:
- Around line 1525-1568: The EffectiveTargetedInstanceCreation helper currently
assumes only one Ready TenantAccount exists and uses tas[0], which makes the
result order-dependent if duplicates slip in. Update the logic in
EffectiveTargetedInstanceCreation to fail closed or otherwise handle the case
where taDAO.GetAll returns more than one Ready account instead of trusting the
first row, and align the tenantaccount creation path in
api/pkg/api/handler/tenantaccount.go with a database-enforced uniqueness
guarantee for tenant/infrastructure provider.

In `@rest-api/api/pkg/api/model/tenantaccount_test.go`:
- Line 232: Add test coverage in NewAPITenantAccount tests for the new
SiteCapabilities derivation, since all current cases pass nil tenantSites and
never exercise tenantAccountSiteCapabilitiesToAPI or
filterTenantSitesForAccount. Update the test setup to include at least one
non-empty tenantSites case with mixed provider IDs and overrides, then assert
the resulting got.SiteCapabilities for global values, per-site overrides,
provider filtering, and enabled/disabled ordering.

In `@rest-api/openapi/oasdiff-breaking-changes-ignore.txt`:
- Around line 13-17: The ignore list still suppresses the removed POST
/v2/org/{org}/nico/machine/{machineId}/bmc-reset path, which hides a real
client-facing breaking change. Update oasdiff-breaking-changes-ignore.txt to
remove that specific ignore entry, and only keep it if the old route is
explicitly documented as deprecated or renamed; use the nearby
/v2/org/{org}/nico/machine/{machineId}/bmc/reset path change as the reference
when adjusting the ignore rules.

In `@rest-api/openapi/spec.yaml`:
- Around line 14481-14498: Document the server-enforced siteCapabilities rules
so the OpenAPI contract matches Validate() in
APITenantAccountSiteCapabilitiesUpdateRequest: the
TenantAccountUpdateRequest.siteCapabilities array must be non-empty, must
contain exactly one global entry, and must not repeat any siteId across entries.
Update the TenantAccountSiteCapability / siteCapabilities descriptions to spell
out these constraints and add minItems: 1 where the array is required so clients
cannot build payloads that pass schema validation but are rejected by the
server.

In `@rest-api/workflow/pkg/activity/operatingsystem/operatingsystem_test.go`:
- Line 48: `util.TestBuildTenant` is being called with the tenant org and
display name in the wrong order, so `Tenant.Org` and `OrgDisplayName` are
swapped. Update the `TestBuildTenant` call in `operatingsystem_test.go` to pass
`tnOrg` as the org argument and use a human-readable tenant name for the
display-name argument, matching the `(org, orgDisplayName)` signature.

In `@rest-api/workflow/pkg/activity/site/site_test.go`:
- Line 91: The `util.TestBuildTenant` call in `site_test.go` is passing the
organization arguments in the wrong order, so swap the `"test-tenant"` and
`tnOrg` values to match `TestBuildTenant(t, dbSession, org, orgDisplayName,
user)`. Check the other call site in this test file as well and make sure any
fixture setup that depends on `tenant.Org` uses the corrected organization
value, including the SSH key group fixture.

---

Outside diff comments:
In `@rest-api/api/pkg/api/handler/sku_test.go`:
- Around line 202-210: `tenantWithoutCapability` is no longer testing the
disabled-capability branch and now duplicates `tenantWithoutAccount`; update the
fixtures in `TestEffectiveTargetedInstanceCreation` and the matching
`TestGetSkuHandler_Handle` case so each subtest isolates a distinct
`EffectiveTargetedInstanceCreation` path. Keep `tenantWithoutCapability` backed
by a ready `TenantAccount` with `Config.TargetedInstanceCreation` set to false,
and leave `tenantWithoutAccount` as the no-`TenantAccount` case, so the two
denied scenarios exercise different branches again.

In `@rest-api/db/pkg/db/model/tenantaccount.go`:
- Around line 504-574: TenantAccountSQLDAO.Update currently performs separate
write operations when input.Config is set, so a nil tx call from the service
account handler can leave the tenant account partially updated if the second
write fails. Update the Update method to make the no-transaction path atomic by
either wrapping the full update flow in a transaction when tx is nil or
combining the field and config changes into a single database statement; keep
the logic centered in TenantAccountSQLDAO.Update and preserve the final GetByID
fetch after the atomic write completes.

---

Duplicate comments:
In `@rest-api/db/pkg/db/model/tenantsite.go`:
- Around line 285-339: Move the Config and RemoveTargetedInstanceCreation
mutual-exclusion validation in TenantSiteSQLDAO.Update to the top of the method,
before any database write or EnableSerialConsole update occurs. Keep the
existing error handling with errors.Wrap(db.ErrInvalidParams, ...) but run it
before building ts or calling db.GetIDB(...).NewUpdate().Exec so invalid input
cannot partially persist changes.

---

Nitpick comments:
In `@rest-api/api/pkg/api/handler/instance.go`:
- Around line 4881-4917: The `IsRepairTenant` authorization check in
`instance.go` is happening inside `cdb.WithTx` after state-changing writes have
already started; move the `common.EffectiveTargetedInstanceCreation` call above
the transaction, using the already-loaded `instance.Tenant` and
`instance.SiteID` and passing a nil transaction if needed. If the capability is
disabled, return `cutil.NewAPIErrorResponse` (forbidden) before opening the
transaction, and keep `cdb.WithTx`, `instanceDAO.Update`, and
`StatusDetailDAO.Create` limited to the actual write path only.

In `@rest-api/api/pkg/api/handler/tenantaccount.go`:
- Line 781: The handleProviderSiteCapabilitiesUpdate method currently takes
dbUser *cdbm.User but never uses it, so update the method signature and all call
sites to remove the unused parameter if it is not needed for this provider-admin
flow; if it is intended for audit or authorization, reference it inside the
method similarly to handleTenantInviteAcceptance so the parameter is no longer
unused and lint checks pass.

In `@rest-api/api/pkg/api/model/tenantaccountsitecapability.go`:
- Around line 105-107: The validation in tenant account site capability handling
uses validationErrorDuplicateGlobalScope for every non-1 globalCount, which
makes the zero-global case return a misleading message. Update the check around
the siteCapabilities validation so it distinguishes between globalCount == 0 and
globalCount > 1, and reuse the clearer “siteCapabilities must include exactly
one global entry” wording already used in tenantaccount.go rather than the
duplicate-global message for both cases.
- Around line 182-197: The parseSiteCapabilitySiteIDs helper is currently dead
code and duplicates the UUID parsing loop already in
handleProviderSiteCapabilitiesUpdate. Update the handler to call
parseSiteCapabilitySiteIDs so the parsing logic is centralized, or remove
parseSiteCapabilitySiteIDs entirely if it is not meant to be reused; use the
existing function name and the handler name to locate the duplicated logic.
- Around line 52-110: `APITenantAccountSiteCapabilitiesUpdateRequest.Validate`
is hand-rolled instead of following the typed-slice validation pattern used
elsewhere. Refactor it to use `validation.Validate(..., validation.Each(...))`
for per-item checks and move the scope/siteID cross-field rules into named
methods on `APITenantAccountSiteCapabilitiesUpdateRequest` so they can be
referenced with `validation.By(...)` like `APIMachineCapabilities.Validate`.
Keep the existing rules (global/limited scope, UUID validation, duplicate site
IDs, single global entry), but express them through named validators rather than
inline anonymous closures and ad-hoc iteration.

In
`@rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go`:
- Around line 15-59: Add a focused migration test for the new tenant account
site capabilities migration in the same test suite that covers
tenantConfigUpMigration. The test should exercise the migration function
registered in init, verify the backfill predicates for targetedInstanceCreation
and enableSshAccess on tenant_account.config, and confirm that tenant.config is
dropped after the up migration. Use the migration registration and the existing
migration test helpers as the entry points so the test stays aligned with the
actual apply path.

In `@rest-api/workflow/pkg/util/testing.go`:
- Around line 227-238: The two TestBuildTenant helpers are inconsistent: this
util.TestBuildTenant takes (org, orgDisplayName, user) while the sibling
db/pkg/db/model.TestBuildTenant uses a different string order, which caused
callers to swap arguments accidentally. Align the parameter order across both
TestBuildTenant functions, or rename one of them (for example,
TestBuildTenantWithDisplayName) so the intended ordering is unambiguous. Update
any call sites to match the chosen signature, especially the helper references
used in tenant-related tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 793aae45-fe4f-4e93-af35-1e408b260506

📥 Commits

Reviewing files that changed from the base of the PR and between 6890629 and 7021d75.

⛔ Files ignored due to path filters (42)
  • rest-api/sdk/standard/api_tenant.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/api_tenant_account.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/client.go is excluded by !rest-api/sdk/standard/client.go
  • rest-api/sdk/standard/model_batch_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability_scope.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_capabilities.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_summary.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
📒 Files selected for processing (51)
  • rest-api/api/pkg/api/handler/dpureprovision_test.go
  • rest-api/api/pkg/api/handler/expectedmachine.go
  • rest-api/api/pkg/api/handler/expectedmachine_test.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/instance_test.go
  • rest-api/api/pkg/api/handler/instancebatch_test.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/handler/machinehealth_test.go
  • rest-api/api/pkg/api/handler/rack_test.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/site.go
  • rest-api/api/pkg/api/handler/site_test.go
  • rest-api/api/pkg/api/handler/sku.go
  • rest-api/api/pkg/api/handler/sku_test.go
  • rest-api/api/pkg/api/handler/tenant.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/tray_test.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/util/common/testing.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/handler/vpc_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/db/pkg/db/model/tenant.go
  • rest-api/db/pkg/db/model/tenant_test.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/db/pkg/db/model/tenantsite_test.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/db/pkg/migrations/migrations_test.go
  • rest-api/docs/index.html
  • rest-api/openapi/oasdiff-breaking-changes-ignore.txt
  • rest-api/openapi/spec.yaml
  • rest-api/workflow/pkg/activity/dpuextensionservice/dpuextensionservice_test.go
  • rest-api/workflow/pkg/activity/infinibandpartition/infinibandpartition_test.go
  • rest-api/workflow/pkg/activity/instance/instance_test.go
  • rest-api/workflow/pkg/activity/nvlinklogicalpartition/nvlinklogicalpartition_test.go
  • rest-api/workflow/pkg/activity/operatingsystem/operatingsystem_test.go
  • rest-api/workflow/pkg/activity/site/site_test.go
  • rest-api/workflow/pkg/activity/sshkeygroup/sshkeygroup_test.go
  • rest-api/workflow/pkg/activity/tenant/tenant_test.go
  • rest-api/workflow/pkg/util/testing.go

Comment thread rest-api/api/pkg/api/handler/tenantaccount.go
Comment thread rest-api/api/pkg/api/handler/util/common/common.go Outdated
Comment thread rest-api/api/pkg/api/model/tenantaccount_test.go Outdated
Comment on lines +13 to +17
POST /v2/org/{org}/nico/machine/{machineId}/bmc-reset api path removed without deprecation
GET /v2/org/{org}/nico/tenant/current the `capabilities/targetedInstanceCreation` response's property type/format changed from `boolean`/`uuid` to `boolean`/`` for status `200`
GET /v2/org/{org}/nico/vpc-peering the `items/tenant/capabilities/targetedInstanceCreation` response's property type/format changed from `boolean`/`uuid` to `boolean`/`` for status `200`
POST /v2/org/{org}/nico/vpc-peering the `tenant/capabilities/targetedInstanceCreation` response's property type/format changed from `boolean`/`uuid` to `boolean`/`` for status `201`
GET /v2/org/{org}/nico/vpc-peering/{id} the `tenant/capabilities/targetedInstanceCreation` response's property type/format changed from `boolean`/`uuid` to `boolean`/`` for status `200` No newline at end of file

@coderabbitai coderabbitai Bot Jul 8, 2026

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate bmc-reset references =="
rg -n --hidden --no-ignore-vcs 'bmc-reset|bmc reset|BMC reset|/machine/\{machineId\}/bmc-reset' rest-api openapi .github docs book || true

echo
echo "== inspect openapi ignore file context =="
cat -n rest-api/openapi/oasdiff-breaking-changes-ignore.txt | sed -n '1,80p'

echo
echo "== inspect openapi spec for the endpoint =="
rg -n --hidden --no-ignore-vcs 'machine/.+\breset\b|bmc-reset|BMCReset|bmcReset' rest-api/openapi/spec.yaml rest-api/openapi/**/*.yaml rest-api/openapi/**/*.yml || true

Repository: NVIDIA/infra-controller

Length of output: 50380


Remove the /bmc-reset ignore entry unless the rename is documented.

rest-api/openapi/spec.yaml now exposes POST /v2/org/{org}/nico/machine/{machineId}/bmc/reset; the old hyphenated path is gone. Suppressing that removal here hides a client-facing break. If the change is intentional, add a deprecation/removal note for the old URL instead of ignoring it.

🤖 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 `@rest-api/openapi/oasdiff-breaking-changes-ignore.txt` around lines 13 - 17,
The ignore list still suppresses the removed POST
/v2/org/{org}/nico/machine/{machineId}/bmc-reset path, which hides a real
client-facing breaking change. Update oasdiff-breaking-changes-ignore.txt to
remove that specific ignore entry, and only keep it if the old route is
explicitly documented as deprecated or renamed; use the nearby
/v2/org/{org}/nico/machine/{machineId}/bmc/reset path change as the reference
when adjusting the ignore rules.

Source: Path instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "== current ignore file =="
cat -n rest-api/openapi/oasdiff-breaking-changes-ignore.txt
echo
echo "== search spec for bmc-reset and bmc/reset =="
rg -n 'bmc-reset|bmc/reset|bmcReset' rest-api/openapi/spec.yaml || true
echo
echo "== last commits touching ignore file =="
git log --oneline -n 5 -- rest-api/openapi/oasdiff-breaking-changes-ignore.txt

Length of output: 3748


@hwadekar-nv, thanks for the update. However, checking the current state of rest-api/openapi/oasdiff-breaking-changes-ignore.txt on this branch, Line 13 still contains:

POST /v2/org/{org}/nico/machine/{machineId}/bmc-reset api path removed without deprecation

and rest-api/openapi/spec.yaml confirms only the renamed /v2/org/{org}/nico/machine/{machineId}/bmc/reset path exists now. Could you confirm whether the fix has been pushed yet? If it's still pending, I'll hold off on marking this resolved until the ignore entry is removed (or the old path is properly documented as deprecated/renamed).

Comment thread rest-api/openapi/spec.yaml Outdated
Comment thread rest-api/workflow/pkg/activity/operatingsystem/operatingsystem_test.go Outdated
Comment thread rest-api/workflow/pkg/activity/site/site_test.go Outdated
// TenantAccount.config. Site-scoped capability actions must use
// EffectiveTargetedInstanceCreation, which additionally honors per-site
// TenantSite.config overrides.
func TenantHasTargetedInstanceCreation(ctx context.Context, tx *cdb.Tx, dbSession *cdb.Session, tenant *cdbm.Tenant) (bool, error) {

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.

Let's add optional site *cdbm.Site here so we don't need a separate EffectiveTargetedInstanceCreation

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

Need to look through more but added some initial comments.

// Begin validating Machine ID
if apiRequest.MachineID != nil {
if tenant.Config == nil || !tenant.Config.TargetedInstanceCreation {
enabledForSite, derr := common.EffectiveTargetedInstanceCreation(ctx, tx, cih.dbSession, tenant, site.ID)

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.

Let's use var privilegedAccess instead of enabledForSite

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment thread rest-api/api/pkg/api/handler/machine.go Outdated
logger.Error().Err(serr).Msg("error retrieving Tenant Accounts for privileged Tenant")
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Error retrieving Tenant Accounts for privileged Tenant", nil)
}
taDAO := cdbm.NewTenantAccountDAO(gamh.dbSession)

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.

Should we add a utility function GetPrivilegedAccessSitesForTenant that returns all Site IDs where Tenant has privileged access? That'll make the logic in this handler simpler. Instead of filtering by Provider IDs, we should filter by Site IDs here for privileged Tenant. If the returned Site ID count is 0 then we reject this request.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

}, cdbp.PageInput{Limit: cutil.GetPtr(cdbp.TotalLimit)}, []string{"Site"})
if err != nil {
logger.Error().Err(err).Msg("error retrieving Tenant Sites for Tenant Account")
return cutil.NewAPIErrorResponse(c, http.StatusInternalServerError, "Failed to retrieve Tenant Sites for Tenant Account", nil)

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.

Failed to retrieve Tenant Sites for Tenant should be ok.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch 2 times, most recently from 0080f33 to f9f5549 Compare July 9, 2026 18:12
@hwadekar-nv

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks !

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@hwadekar-nv Understood — kicking off another full review of all changes in this PR now.

ᕕ( ᐛ )ᕗ

✅ Action performed

Full review finished.

@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

Caution

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

⚠️ Outside diff range comments (1)
rest-api/db/pkg/db/model/tenantsite.go (1)

298-338: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Move the Config/RemoveTargetedInstanceCreation guard before any update

The current order can persist enable_serial_console before returning db.ErrInvalidParams when Update is called without a transaction, leaving the row partially updated. Check the mutually exclusive inputs first, then perform any writes.

🤖 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 `@rest-api/db/pkg/db/model/tenantsite.go` around lines 298 - 338, The Update
path in tenantsite.go applies the EnableSerialConsole change before validating
the mutually exclusive Config and RemoveTargetedInstanceCreation inputs, which
can leave the row partially updated when db.ErrInvalidParams is returned. Move
the guard check for input.Config and input.RemoveTargetedInstanceCreation in the
tenant site update flow ahead of any write logic in the Update method, then keep
the existing update branches for enable_serial_console, Config, and
RemoveTargetedInstanceCreation after validation.
🧹 Nitpick comments (1)
rest-api/db/pkg/db/model/tenantsite_test.go (1)

285-297: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reach for range-over-integer for this counting loop.

siteCount is a plain count and i is only read (for Sprintf and the modulo check), so this is exactly the "counting loop" case the repo's Go conventions call out for range.

♻️ Proposed refactor
-	sites := []*Site{}
 	siteCount := 30
-	for i := 0; i < siteCount; i++ {
+	sites := make([]*Site, 0, siteCount)
+	for i := range siteCount {
 		site := TestBuildSite(t, dbSession, ip, fmt.Sprintf("test-site-%d", i), ipu)
 		sites = append(sites, site)
 		if i%2 == 0 {
 			TestBuildTenantSite(t, dbSession, tn1, site, nil, tnu1)
 		} else {
 			TestBuildTenantSite(t, dbSession, tn2, site, config, tnu2)
 		}
 	}

As per path instructions, "Prefer range-based iteration over C-style for loops; use range/slices.Backward/range-over-integer where they express the loop more clearly."

🤖 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 `@rest-api/db/pkg/db/model/tenantsite_test.go` around lines 285 - 297, Refactor
the counting loop in the tenant site test to use range-over-integer instead of a
C-style `for` loop. In the `TestBuildTenantSite` setup block, replace the manual
`siteCount`/`i` iteration with a range-based integer loop, keeping the existing
`TestBuildSite` and `TestBuildTenantSite` calls, the
`fmt.Sprintf("test-site-%d", i)` naming, and the even/odd branch logic intact.

Source: Path instructions

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

Inline comments:
In `@rest-api/api/pkg/api/handler/util/common/common.go`:
- Around line 1551-1626: The site and expected-machine list authorization is
still using tenant-level privileged access expansion instead of the effective
per-site set. Update the list-path checks in site.go and expectedmachine.go to
call GetPrivilegedAccessSiteIDsForTenant, and use its returned Site IDs for
filtering/authorization so the list endpoints match the single-site behavior,
including TenantSite.config overrides.

In
`@rest-api/workflow/pkg/activity/dpuextensionservice/dpuextensionservice_test.go`:
- Around line 98-100: The test setup in dpuextensionservice_test.go still has
the org/orgDisplayName arguments swapped in the TestBuildTenant call, causing
the tenant org to mismatch the TestBuildUser org. Update the TestBuildTenant
invocation in this test to match the corrected pattern used in site_test.go,
operatingsystem_test.go, and nvlinklogicalpartition_test.go, with the org value
passed first and the display name second so tenant.Org aligns with the user’s
org.

---

Outside diff comments:
In `@rest-api/db/pkg/db/model/tenantsite.go`:
- Around line 298-338: The Update path in tenantsite.go applies the
EnableSerialConsole change before validating the mutually exclusive Config and
RemoveTargetedInstanceCreation inputs, which can leave the row partially updated
when db.ErrInvalidParams is returned. Move the guard check for input.Config and
input.RemoveTargetedInstanceCreation in the tenant site update flow ahead of any
write logic in the Update method, then keep the existing update branches for
enable_serial_console, Config, and RemoveTargetedInstanceCreation after
validation.

---

Nitpick comments:
In `@rest-api/db/pkg/db/model/tenantsite_test.go`:
- Around line 285-297: Refactor the counting loop in the tenant site test to use
range-over-integer instead of a C-style `for` loop. In the `TestBuildTenantSite`
setup block, replace the manual `siteCount`/`i` iteration with a range-based
integer loop, keeping the existing `TestBuildSite` and `TestBuildTenantSite`
calls, the `fmt.Sprintf("test-site-%d", i)` naming, and the even/odd branch
logic intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 282a661b-b6f5-4e29-8780-fa9f971905a9

📥 Commits

Reviewing files that changed from the base of the PR and between 356dd0b and f9f5549.

⛔ Files ignored due to path filters (42)
  • rest-api/sdk/standard/api_tenant.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/api_tenant_account.go is excluded by !rest-api/sdk/standard/api_*.go
  • rest-api/sdk/standard/client.go is excluded by !rest-api/sdk/standard/client.go
  • rest-api/sdk/standard/model_batch_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_power_shelf_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_rack_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_expected_switch_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_infini_band_partition_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_type_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_instance_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_interface_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_machine_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_network_security_group_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_site_capability_scope.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_account_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_capabilities.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_tenant_summary.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_create_request.go is excluded by !rest-api/sdk/standard/model_*.go
  • rest-api/sdk/standard/model_vpc_update_request.go is excluded by !rest-api/sdk/standard/model_*.go
📒 Files selected for processing (51)
  • rest-api/api/pkg/api/handler/dpureprovision_test.go
  • rest-api/api/pkg/api/handler/expectedmachine.go
  • rest-api/api/pkg/api/handler/expectedmachine_test.go
  • rest-api/api/pkg/api/handler/instance.go
  • rest-api/api/pkg/api/handler/instance_test.go
  • rest-api/api/pkg/api/handler/instancebatch_test.go
  • rest-api/api/pkg/api/handler/machine.go
  • rest-api/api/pkg/api/handler/machine_test.go
  • rest-api/api/pkg/api/handler/machinehealth_test.go
  • rest-api/api/pkg/api/handler/rack_test.go
  • rest-api/api/pkg/api/handler/serviceaccount.go
  • rest-api/api/pkg/api/handler/site.go
  • rest-api/api/pkg/api/handler/site_test.go
  • rest-api/api/pkg/api/handler/sku.go
  • rest-api/api/pkg/api/handler/sku_test.go
  • rest-api/api/pkg/api/handler/tenant.go
  • rest-api/api/pkg/api/handler/tenantaccount.go
  • rest-api/api/pkg/api/handler/tray_test.go
  • rest-api/api/pkg/api/handler/util/common/common.go
  • rest-api/api/pkg/api/handler/util/common/common_test.go
  • rest-api/api/pkg/api/handler/util/common/testing.go
  • rest-api/api/pkg/api/handler/vpc.go
  • rest-api/api/pkg/api/handler/vpc_test.go
  • rest-api/api/pkg/api/model/operatingsystem_test.go
  • rest-api/api/pkg/api/model/sshkeygroup_test.go
  • rest-api/api/pkg/api/model/tenant.go
  • rest-api/api/pkg/api/model/tenant_test.go
  • rest-api/api/pkg/api/model/tenantaccount.go
  • rest-api/api/pkg/api/model/tenantaccount_test.go
  • rest-api/api/pkg/api/model/tenantaccountsitecapability.go
  • rest-api/db/pkg/db/model/tenant.go
  • rest-api/db/pkg/db/model/tenant_test.go
  • rest-api/db/pkg/db/model/tenantaccount.go
  • rest-api/db/pkg/db/model/tenantsite.go
  • rest-api/db/pkg/db/model/tenantsite_test.go
  • rest-api/db/pkg/db/model/testing.go
  • rest-api/db/pkg/migrations/20230412224951_tenant_site.go
  • rest-api/db/pkg/migrations/20260701120000_tenant_account_site_capabilities.go
  • rest-api/db/pkg/migrations/migrations_test.go
  • rest-api/docs/index.html
  • rest-api/openapi/oasdiff-breaking-changes-ignore.txt
  • rest-api/openapi/spec.yaml
  • rest-api/workflow/pkg/activity/dpuextensionservice/dpuextensionservice_test.go
  • rest-api/workflow/pkg/activity/infinibandpartition/infinibandpartition_test.go
  • rest-api/workflow/pkg/activity/instance/instance_test.go
  • rest-api/workflow/pkg/activity/nvlinklogicalpartition/nvlinklogicalpartition_test.go
  • rest-api/workflow/pkg/activity/operatingsystem/operatingsystem_test.go
  • rest-api/workflow/pkg/activity/site/site_test.go
  • rest-api/workflow/pkg/activity/sshkeygroup/sshkeygroup_test.go
  • rest-api/workflow/pkg/activity/tenant/tenant_test.go
  • rest-api/workflow/pkg/util/testing.go

Comment thread rest-api/api/pkg/api/handler/util/common/common.go
Comment thread rest-api/workflow/pkg/activity/dpuextensionservice/dpuextensionservice_test.go Outdated
@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from 2bbf0ac to a8e6e5c Compare July 9, 2026 20:42
@hwadekar-nv hwadekar-nv force-pushed the feat/tenant-capabilities-update branch from a8e6e5c to a26937d Compare July 9, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants