Skip to content

feat(studio-cp,oab-mcp): list_namespaces / list_service_accounts tools (studio#104) - #106

Closed
brettchien wants to merge 1 commit into
feat/k8s-onboarding-list-toolsfrom
feat/k8s-onboarding-namespace-sa-tools
Closed

feat(studio-cp,oab-mcp): list_namespaces / list_service_accounts tools (studio#104)#106
brettchien wants to merge 1 commit into
feat/k8s-onboarding-list-toolsfrom
feat/k8s-onboarding-namespace-sa-tools

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

Summary

Second sub-item of #104, stacked on #105 (list_aws_profiles/list_k8s_contexts — same tools() vec, same design). Two context-scoped k8s discovery tools:

  • list_namespaces(context?)Api<Namespace>::list() against the given (or current-context) kubeconfig context. Backs the New Fleet wizard's namespace <select>. A manual-entry fallback still covers a namespace that doesn't exist yet — this can only list what's already there.
  • list_service_accounts(context?, namespace)Api<ServiceAccount>::list(namespace). Backs the optional service-account <select>. Per the design decided in the K8s fleet onboarding — console UX for provider selection ('+ New fleet') #104 thread discussion: any failure here (including an RBAC-denied list, which is common against a scoped-down cluster identity) should read to the caller as "leave it unset" — the namespace's default service account applies — not surfaced as an error. So unlike list_aws_profiles/list_k8s_contexts, this one doesn't split exists/error; it just errors normally and the console is expected to swallow it.

Also factored the from_kubeconfig + Client::try_from boilerplate observe_k8s_identity had inlined into a shared k8s_client_for() helper, used by both new functions and refactored into observe_k8s_identity too — same logic, no behavior change there.

Verification

Same situation as #105: local cargo check -p studio-cp -p oab-mcp OOM-kills on aws-sdk-ec2 on this machine regardless of retries (5 attempts total across both PRs today, all the same failure) — pre-existing environment constraint, not something this change causes. Hand-verified the k8s-openapi 0.24/kube-client 0.99 API surface directly against vendored crate source: Namespace/ServiceAccount shape (metadata: ObjectMeta, .metadata.name: Option<String>), Api::namespaced(client, ns: &str), Api::list(&ListParams) -> Result<ObjectList<K>> with ObjectList.items: Vec<K>, ListParams: Default. CI is the real gate — same disclosure as #105.

Scope note

Console-side wiring (<select> population, provider picker) and the remaining backend items (k8s_fleet_config_write, deploy_provision provider param, K8sFleetBinding.expected_principal) are tracked separately in #104.

Ref #104. Stacks on #105 — please merge #105 first.

…s (studio#104)

Second sub-item of #104 (stacked on #105's list_aws_profiles/list_k8s_contexts,
same tools() vec / same design). Both are context-scoped k8s discovery:

- list_namespaces(context?): Api<Namespace>::list() for the given/current
  kubeconfig context. Backs the New Fleet wizard's namespace <select> — a
  manual-entry fallback covers a brand-new namespace that doesn't exist yet.
- list_service_accounts(context?, namespace): Api<ServiceAccount>::
  list(namespace). Backs the optional service-account <select>. Per the
  design, any failure here (including an RBAC-denied list, which is common
  against a restricted-scope cluster identity) should read to the caller as
  "leave it unset" (the namespace's default service account applies), not
  as an error to surface — so unlike list_aws_profiles/list_k8s_contexts
  this one doesn't split exists/error, it just errors normally.

Same k8s_client_for() helper factors out the from_kubeconfig+Client::try_from
boilerplate observe_k8s_identity already had inlined.

Ref: studio#104.
@brettchien
brettchien deleted the branch feat/k8s-onboarding-list-tools August 28, 2026 02:35
@brettchien brettchien closed this Aug 28, 2026
@brettchien
brettchien deleted the feat/k8s-onboarding-namespace-sa-tools branch August 28, 2026 02:57
brettchien added a commit that referenced this pull request Aug 28, 2026
…accounts, expected_principal, console provider picker+wiring, fleetsK8sToml.ts) (#115)

* feat(studio-cp,oab-mcp): list_namespaces / list_service_accounts tools (studio#104)

Second sub-item of #104 (stacked on #105's list_aws_profiles/list_k8s_contexts,
same tools() vec / same design). Both are context-scoped k8s discovery:

- list_namespaces(context?): Api<Namespace>::list() for the given/current
  kubeconfig context. Backs the New Fleet wizard's namespace <select> — a
  manual-entry fallback covers a brand-new namespace that doesn't exist yet.
- list_service_accounts(context?, namespace): Api<ServiceAccount>::
  list(namespace). Backs the optional service-account <select>. Per the
  design, any failure here (including an RBAC-denied list, which is common
  against a restricted-scope cluster identity) should read to the caller as
  "leave it unset" (the namespace's default service account applies), not
  as an error to surface — so unlike list_aws_profiles/list_k8s_contexts
  this one doesn't split exists/error, it just errors normally.

Same k8s_client_for() helper factors out the from_kubeconfig+Client::try_from
boilerplate observe_k8s_identity already had inlined.

Ref: studio#104.

* feat(studio-cp,oab-mcp): K8sFleetBinding.expected_principal + k8s_fleet_config_write (studio#104)

Third sub-item of #104, stacked on #106.

- K8sFleetBinding gets expected_principal: Option<String>, deliberately
  named to match FleetBinding's AWS-side field — the verify machinery
  already exists symmetrically (observe_k8s_identity's SelfSubjectReview-
  derived principal + k8s_principal_kind, same shape as observe_identity/
  identity_matches for AWS), this just wires the config schema to it.
  Typically a system:serviceaccount:<ns>:<name> string, or a plain
  username; unset = no identity check for that fleet.
- k8s_fleet_config_write: new MCP tool mirroring fleet_config_write's
  AWS-side write path (validate text parses, write bytes verbatim so
  comments/layout survive, return the parsed fleets + raw text).
  save_k8s_bindings_text (studio-cp/lib.rs) already existed and needed
  zero changes — it's a generic toml::from_str + verbatim write, so it
  picked up the new field automatically once added to the schema structs.
  Unlike AWS bindings, k8s bindings aren't cached anywhere in OabMcp yet
  (nothing dispatches provisioning to K8sDriver yet either — separate
  item), so this is a plain validate-then-write, no in-memory state to
  invalidate.

Ref: studio#104.

* feat(console): provider picker + k8s context/namespace fields in New Fleet wizard (studio#104)

Fourth sub-item of #104, stacked on #107. Adds the console-side UI half of
the k8s onboarding design — the "+ New fleet" identity form (previously
AWS-only: Region/Credential profile/Principal) now starts with a Provider
select (AWS / Kubernetes), toggling between the existing AWS field group and
a new k8s group:

- Context: <select>, populated live from the new list_k8s_contexts tool
  (#105), current-context flagged in the label.
- Namespace: text input + <datalist> from list_namespaces (#106), scoped to
  whichever context is selected — deliberately NOT a plain <select>, since a
  brand-new namespace is a valid choice per #104's design and a select can't
  express "not in this list yet".
- Service account (optional): plain text input for now (list_service_accounts
  wiring is a smaller follow-up; per #104's design any failure there should
  silently fall back to the namespace's default SA anyway, so a live <select>
  buys less than it does for context/namespace).

**k8s submission is intentionally blocked, not wired through**: the identity
form's submit handler shows "Kubernetes provisioning isn't available yet —
tracked in #104" and refuses to advance to the Compose step
when provider=k8s. This is deliberate, not a placeholder oversight — traced
deploy_provision's call chain this same session and found it requires an
already-stored manifest (K8sDriver::apply doesn't persist one the way ECS's
apply_manifests does), so wiring k8s all the way to a live deploy_provision
call would either silently fail or need the driver-dispatch design question
resolved first (posted to #104, unresolved as of this commit). Shipping the
picker/discovery UI now is still real progress — AWS path is 100% unchanged,
and this is testable/mergeable independent of how the deploy_provision
question resolves.

Verified locally (no OOM constraint here — TS/vite, not cargo): `npm run
typecheck` clean, `npm test` 102/102 passing, `npm run build` succeeds.

Ref: studio#104.

* feat(console): wire list_service_accounts into New Fleet's k8s field group (studio#104)

Fifth sub-item of #104, stacked on #108. Service account (optional) becomes
a <select> (was plain text) populated from list_service_accounts(context,
namespace) — unlike namespace, a service account must already exist for k8s
to accept it as a pod's serviceAccountName, so (unlike namespace's <input>+
<datalist>) a plain select with no free-text escape hatch is the right shape
here, matching context's treatment.

Reload triggers: context change (cascades into namespace + service-account
reload) and namespace field's "change" event (fires on blur/commit, not per
keystroke — avoids a tool call per character typed).

Per #104's design this tool's failures are deliberately silent — unlike
list_k8s_contexts/list_namespaces (which show a status message on failure),
any error here, including an RBAC-denied list (common against a scoped-down
cluster identity), just falls back to the "namespace default" option with no
status shown. The field is optional and the whole point of default-SA
fallback is that it's fine not to have a definitive answer here.

Verified locally: npm run typecheck clean, npm test 102/102, npm run build
succeeds.

Ref: studio#104.

* feat(console): add fleetsK8sToml.ts, the client-side write helper for fleets-k8s.toml (studio#104)

Sixth sub-item of #104, stacked on #109. Mirrors fleetToml.ts for
fleets-k8s.toml, same rationale: k8s_fleet_config_write (#107) has no
partial/append primitive, so the client computes the new/edited TOML text
and calls it with the whole file.

`findFleetBlock`/`appendMember` are identical between the two files (same
`[fleet.<name>]` table shape, same `members = [...]` array, neither
references any AWS-specific field) — reused via import from fleetToml.ts
rather than duplicated. Exported `quote()` from fleetToml.ts (was a private
helper) so both modules share the one implementation.

Only `appendK8sFleetBlock` (creating a brand-new fleet block) is new code —
required/optional fields differ from AWS's appendFleetBlock: `context`
(optional) + `namespace` (required, always written) instead of
region+profile, same `expected_principal` (optional) as AWS.

**Not wired into deploy.ts's submit flow yet** — the k8s identity form still
can't submit (see #108: deploy_provision has no k8s dispatch, architecture
question still open in #104). This PR is the write-path helper only, same
"build the piece, wire it once its dependency resolves" pattern as #107's
k8s_fleet_config_write tool (also not yet called by anything).

Verified locally: npm run typecheck clean, npm test 106/106 (4 new tests),
npm run build succeeds.

Ref: studio#104.
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.

1 participant