feat: rebuild #106-110 on current main (list_namespaces/list_service_accounts, expected_principal, console provider picker+wiring, fleetsK8sToml.ts) - #115
Merged
Conversation
…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.
…et_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.
…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.
…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.
… 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.
This was referenced Aug 28, 2026
Closed
This was referenced Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rebuilds #106-110 as one clean PR against current
main, after merging #105 broke the stack: squash-merging #105 with--delete-branchdeleted the branch #106 was based on, which GitHub responded to by auto-closing #106 (its base ref no longer resolved) — and #106 can't be reopened once its base branch is gone.The 5 original commits (from #106, #107, #108, #109, #110) cherry-picked cleanly onto current
main, no conflicts, unchanged content. Each is preserved as its own commit here for the same per-PR review context — see the individual PRs below for full descriptions; summarizing:list_namespaces+list_service_accountsMCP tools, plus factoringobserve_k8s_identity's client-setup boilerplate intok8s_client_for().K8sFleetBinding.expected_principalschema field +k8s_fleet_config_writeMCP tool.list_service_accountswiring (service-account<select>, silent-fallback-on-failure).console/src/fleetsK8sToml.ts, the client-side write helper forfleets-k8s.toml.Superseding: #106 (closed, can't reopen), #107, #108, #109, #110 (still open, will close in favor of this once merged — same content, this is not new work).
Verification
npm run typecheckclean,npm test106/106 passing (matches what #110 reported). Rust side hits the same pre-existingaws-sdk-ec2OOM constraint as every PR in this chain — content is byte-identical to the already-described individual PRs, no new Rust changes beyond what #106/#107 already had reviewed-and-described.Ref #104.