Skip to content

feat(FR-1436): limit deployment model folder selector to mountable hosts#8083

Draft
agatha197 wants to merge 1 commit into
mainfrom
fr-1436
Draft

feat(FR-1436): limit deployment model folder selector to mountable hosts#8083
agatha197 wants to merge 1 commit into
mainfrom
fr-1436

Conversation

@agatha197

@agatha197 agatha197 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Resolves #4234 (FR-1436)

Problem

The model folder picker on the deployment "add revision" modal (DeploymentAddRevisionModal, both the preset and custom forms) listed every usage_mode == "model" vfolder, regardless of whether the folder's storage host is mountable by the user. Picking a non-mountable-host folder (e.g. a left-over model-store project folder) reproduces the customer report: the request fails.

In the deployment flow this is actually worse than the legacy model-service flow. The backend defers the storage-host mount-permission check (MOUNT_IN_SESSION) to session start / replica spawn, not deployment creation:

repositories/deployment/db_source/db_source.py"Intentionally minimal: only the permission column is read, without RBAC / host-permission / cross-project checks — those apply at session creation (prepare_vfolder_mounts), not at revision write."

So a deployment built from a non-mountable-host model folder is accepted, looks created, and then its replicas silently fail to spawn with InsufficientStoragePermission (HTTP 400). Front-end filtering is the right place to prevent this up front.

Change

Filter both model-folder selectors to folders whose host the current user may actually mount:

  • New hook useMountableVFolderHosts reads the myStorageHostPermissions GraphQL field (Strawberry V2) and returns the host names carrying MOUNT_IN_SESSION.
  • The host names are composed into the existing GraphQL filter string as usage_mode == "model" & (host == "h1" | host == "h2" | …). host is server-side filterable on vfolder_nodes, so this keeps the selector's pagination/count intact. When the user has no mountable host, the filter matches nothing (rather than every folder).

Why myStorageHostPermissions (no waterfall)

The legacy client-side pattern (VFolderTable, useMergedAllowedStorageHostPermission) first resolves the keypair's resource-policy name, then issues a second query for domain/group/keypair allowed_vfolder_hosts, then merges three JSON blobs client-side — a request waterfall. myStorageHostPermissions returns the union of domain + the user's projects + keypair permissions in a single query, already intersected with the registered volumes, server-side. No keypair lookup, no client merge.

No @since gate is needed: this modal is already gated to managers ≥ 26.4.3, and myStorageHostPermissions was added in 26.4.2.

Scope notes

  • Host filter only. Group/project (ownership_type == "group") folders are intentionally not excluded here. The backend explicitly accepts group-owned MODEL folders at deployment creation, so excluding them would contradict backend intent; the customer-reported folders are correctly handled by the host filter (their host is simply not mountable). (The original FR-1435 group-exclusion lives in the now-refactored ServiceLauncherPageContent; whether to reinstate it under the new modal is tracked separately.)
  • Known nuance. myStorageHostPermissions is scoped to the user (the union across all of the user's projects), so it is a slight superset of the hosts mountable in one specific project. This is strictly better than today (no filter) and acceptable; strict per-project scoping is out of scope here.
  • Follow-up (separate stack). Migrating the existing waterfall consumers (VFolderTable, useMergedAllowedStorageHostPermission + its file-browser / SFTP / folder-explorer consumers) onto myStorageHostPermissions is tracked as a separate Jira issue and will be stacked on top of this PR.

Verification

bash scripts/verify.sh → Relay / Lint / Format / TypeScript PASS.

Test plan

  • As a user whose project has a model folder on a non-mountable host, open the deployment "add revision" modal → that folder is not offered in the model folder selector (preset and custom forms).
  • A model folder on a mountable host is still offered and a deployment created from it spawns replicas successfully.
  • A user with no mountable hosts sees an empty model folder selector (not the full list).

🤖 Generated with Claude Code

@github-actions github-actions Bot added the size:L 100~500 LoC label Jun 30, 2026

agatha197 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • flow:merge-queue - adds this PR to the back of the merge queue
  • flow:hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for react-coverage (./react)

Status Category Percentage Covered / Total
🔵 Lines 6.72% 1947 / 28945
🔵 Statements 5.49% 2194 / 39928
🔵 Functions 5.67% 322 / 5679
🔵 Branches 3.68% 1387 / 37649
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
react/src/components/DeploymentAddRevisionModal.tsx 0% 0% 0% 0% 169-233
react/src/hooks/useMountableVFolderHosts.ts 0% 0% 0% 0% 23-42
Generated in workflow #2407 for commit 31a8c97 by the Vitest Coverage Report Action

@agatha197 agatha197 marked this pull request as ready for review June 30, 2026 06:36
Copilot AI review requested due to automatic review settings June 30, 2026 06:36

Copilot AI 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.

Pull request overview

This PR prevents users from selecting model vfolders on storage hosts they cannot mount, avoiding deployments that are accepted at creation time but later fail during replica spawn due to missing MOUNT_IN_SESSION permission.

Changes:

  • Added useMountableVFolderHosts hook that queries myStorageHostPermissions and derives the set of mountable storage hosts for the current user.
  • Updated DeploymentAddRevisionModal’s model folder selectors (preset + custom forms) to apply a server-side filter combining usage_mode == "model" with an OR-list of mountable host == ... clauses.
  • Added the Relay generated query artifact for the new hook.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.

File Description
react/src/hooks/useMountableVFolderHosts.ts New Relay-backed hook to fetch mountable storage hosts via myStorageHostPermissions.
react/src/components/DeploymentAddRevisionModal.tsx Applies host-based filtering to both model folder pickers to exclude non-mountable hosts.
react/src/generated/useMountableVFolderHostsQuery.graphql.ts Relay generated query/types for useMountableVFolderHosts.
Files not reviewed (1)
  • react/src/generated/useMountableVFolderHostsQuery.graphql.ts: Generated file

Comment thread react/src/components/DeploymentAddRevisionModal.tsx Outdated
Comment thread react/src/components/DeploymentAddRevisionModal.tsx Outdated
The model folder picker in the deployment "add revision" modal previously
listed every `usage_mode == "model"` vfolder, including folders on storage
hosts the user cannot mount. The backend defers the host mount-permission
check (MOUNT_IN_SESSION) to session start / replica spawn rather than
deployment creation, so picking such a folder lets the deployment be created
but its replicas later fail with a host-permission error (HTTP 400) — a worse,
deferred failure than the legacy model-service flow.

Filter both selectors (preset and custom forms) to folders whose host the
current user is allowed to mount. The mountable host set is read from the
`myStorageHostPermissions` GraphQL field (Strawberry V2), which resolves the
union of `allowed_vfolder_hosts` across the domain, the user's projects, and
the keypair resource policy server-side — avoiding the legacy keypair →
allowed_vfolder_hosts request waterfall — and a new `useMountableVFolderHosts`
hook keeps the MOUNT_IN_SESSION host names. The host clause is composed into
the existing GraphQL `filter` string (`host` is server-side filterable on
`vfolder_nodes`), so pagination is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@agatha197 agatha197 marked this pull request as draft July 1, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100~500 LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Limit model folder selector to mountable folders per allowed_vfolder_hosts

2 participants