feat(quota): Kubernetes quota-enforcement — M1 (tenancy + hard quotas + scoped kubeconfig) - #471
Closed
powderluv wants to merge 4 commits into
Closed
feat(quota): Kubernetes quota-enforcement — M1 (tenancy + hard quotas + scoped kubeconfig)#471powderluv wants to merge 4 commits into
powderluv wants to merge 4 commits into
Conversation
…a/LimitRange/RBAC) Pure, unit-tested mapping from a SPUR account allocation (grp_tres) to the native k8s objects that enforce it. 6 tests green. Foundation for the quota policy reconciler; DB/proto persistence + controller + `kubeconfig --user` follow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire an account-level resource allocation (grp_tres: cpu/mem/amd.com/gpu) through the accounting stack so the quota reconciler can project it to a per-account ResourceQuota. SPUR previously persisted TRES caps only on QoS, not accounts. - proto: CreateAccountRequest + AccountInfo gain a `grp_tres` TRES string. - db: accounts table gets a grp_tres column (+ ADD COLUMN migration for existing DBs); upsert_account/list_accounts/AccountRecord carry it; round-trip test. - grpc: create_account persists grp_tres; list_accounts returns it. - cli: `sacctmgr add/modify account grptres=cpu=16,mem=32768,gres/gpu=8`. Verified: build + spurctld/spur-cli/spur-k8s tests + fmt + clippy -D warnings (the DB round-trip test is #[ignore], runs in CI's Postgres). Also folds a rustfmt/doc-lint cleanup of increment 1's quota.rs + the CP-fix formatting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Makes the quota layer live: an opt-in reconcile loop that projects every SPUR account into its native k8s objects and drift-corrects them. - quota_controller::run: connects a SlurmAccountingClient to spurctld, then every 30s lists accounts (with grp_tres) + members and server-side-applies (force, so an admin hand-edit is reverted) the Namespace + ResourceQuota + LimitRange + Role + RoleBinding from crate::quota. - build_account_quota: pure AccountInfo + members -> AccountQuota (unit-tested). - apply<K>: generic SSA helper (injects apiVersion/kind, which k8s-openapi types don't serialize but server-side apply requires). - Wired into main.rs behind --enable-quota (default off; opt-in policy plane), spawned with the same run_with_retry backoff as the other controllers. Verified: build + spur-k8s tests (146) + fmt + clippy -D warnings. The apply/RPC path is integration-level (needs a cluster); the mapping + build logic are unit-tested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…onfig (M1 increment 4) A SPUR user gets a namespace-scoped credential into their account's namespace, without an admin ever handing out the cluster-admin kubeconfig. - spur-core::quota_names: the per-account namespace + per-user ServiceAccount naming, shared so the operator (which creates them) and spurctld (which mints the kubeconfig) agree. Moved out of spur-k8s::quota. - proto: ClusterKubeconfigRequest.user; GetAdminKubeconfigRequest gains user/namespace/service_account (reuses the existing RPC — no new trait method). - spurd: K0sAgent::user_kubeconfig ensures the SA exists + mints a bound token via `k0s kubectl create token`, then templates a scoped kubeconfig from the admin cluster CA/server (pure parse + template helpers, unit-tested). - spurctld: cluster_kubeconfig resolves the user's account (association cache) -> namespace + SA and forwards to the control-plane agent. - cli: `spur k8s kubeconfig --user <u>`. Verified (no cluster run, per request): build + tests + fmt + clippy -D warnings across all crates incl. spurd on Linux (shark-a). The SA/token minting is integration-level; the naming, CA/server parse, and kubeconfig template are unit-tested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Member
|
cherry-picked to PR #475 to keep working on this feature |
Member
|
close this PR since the commits has been cherry-picked to PR #475 , and the commits have been merged. |
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.
What
Implements M1 of the Kubernetes quota-enforcement design (#444 RFC): the tenancy + hard-quota layer that projects SPUR accounts onto native Kubernetes objects, plus per-user scoped kubeconfigs — so a cluster is a completely normal Kubernetes deployment to its users while SPUR is the invisible policy plane.
End-to-end flow this lands
Increments (each independently reviewable)
account → Namespace/ResourceQuota/LimitRange/RBACmapping (spur-k8s/quota.rs), fully unit-testedgrp_tressacctmgrquota_controllerin the operator: lists accounts+members over gRPC, server-side-applies withforce(drift-correcting), opt-in--enable-quotakubeconfig --userspur-core, scoped kubeconfig minted by the control-plane agent (k0s kubectl create token), CLI flagDesign decision
Per the RFC, SPUR persists resource caps on QoS, not accounts. M1 wires account-level allocations (
grp_treson theAccount) so each account/namespace has a definite ResourceQuota — the cleanest tenancy model, and it gives the GPU cluster the GPU quota it lacks today.Status / not in this PR
--enable-quota(off by default) — zero effect on existing deployments.build_account_quota) is unit-tested.Testing
Build + tests +
fmt+clippy -D warningsgreen across every crate —spur-core/spurctld/spur-cli/spur-k8son macOS andspurdon Linux (cargo test -p spurd, clippy clean). DB round-trip tests are#[ignore](run in CI's Postgres).🤖 Generated with Claude Code