feat(cozystack-basics): let tenant admins order and release GPUClaims

Tenant admins and above can now create, change and delete GPUClaim
objects in their namespaces where GPUs are on. The order webhook judges
each order against its class, capacity, budgets and the vGPU
entitlement, and the claim tamper policy keeps tenants off a claim's
finalizers and template mark, so the write is granted only where that
policy renders. Status, which carries the binding and the attachment,
stays the controller's.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
Aleksei Sviridkin committed 2026-10-26 13:16:21 +03:00
1 parent 352f726196
commit 67fc342e79
3 files changed
+54 -6

No files matched your search

@@ -293,6 +293,15 @@ rules:
- update
- patch
- delete
{{- if and (eq (toString ((.Values.gpu).enabled | default false)) "true") (.Capabilities.APIVersions.Has "admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy") }}
{{/* Rendered under the same conditions as cozystack-gpu-claim-tamper, which
keeps tenants off a claim's finalizers and template mark. Status, which
carries the binding and attachment, is a subresource and stays the
controller's. */}}
- apiGroups: ["gpu.cozystack.io"]
resources: ["gpuclaims"]
verbs: ["create", "update", "patch", "delete"]
{{- end }}
---
# == super admin cluster role ==
# Aggregates admin + all roles labeled for super-admin access
@@ -0,0 +1,21 @@
suite: cozystack-basics GPUClaim write follows the policies that guard it
templates:
- templates/clusterroles.yaml
release:
name: cozystack-basics
namespace: cozy-system
# The GPU policies render only where the API server serves
# ValidatingAdmissionPolicy, so the write they guard does too.
capabilities:
apiVersions: []
tests:
- it: grants no GPUClaim write without the ValidatingAdmissionPolicy API
set:
gpu:
enabled: true
documentSelector:
path: metadata.name
value: cozy:tenant:admin:base
asserts:
- notExists:
path: rules[?(@.apiGroups[*] == "gpu.cozystack.io")]
@@ -7,11 +7,12 @@ release:
name: cozystack-basics
namespace: cozy-system
# Tenant roles read GPUClaim orders in their namespaces; no tenant role writes
# them yet, since nothing checks an order's budget or entitlement here.
# GPUClass is cluster-scoped, and tenant roles are bound per namespace, so its
# read goes to every authenticated user through a ClusterRoleBinding. GPUNode,
# which names physical devices, is granted to no tenant role.
# Tenants with admin or above manage GPUClaim orders in their namespaces
# where GPUs are on, and lower roles read them. GPUClass is cluster-scoped,
# and tenant roles are bound per namespace, so its read goes to every
# authenticated user through a ClusterRoleBinding. Neither the status of a
# claim, which holds its binding, nor GPUNode, which names physical devices,
# is granted to any tenant role.
tests:
# The filter returns the rule itself when exactly one rule names the group
# and a list otherwise, so equal pins both the rule and that it is the only
@@ -28,10 +29,27 @@ tests:
resources: ["gpuclaims"]
verbs: ["get", "list", "watch"]
- it: cozy:tenant:admin:base grants nothing in gpu.cozystack.io
- it: cozy:tenant:admin:base grants only write on gpuclaims when GPUs are on
set:
gpu:
enabled: true
capabilities:
apiVersions:
- admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy
documentSelector:
path: metadata.name
value: cozy:tenant:admin:base
asserts:
- equal:
path: rules[?(@.apiGroups[*] == "gpu.cozystack.io")]
value:
apiGroups: ["gpu.cozystack.io"]
resources: ["gpuclaims"]
verbs: ["create", "update", "patch", "delete"]
# The write ships with the GPU policies that guard it, which render only
# where GPUs are on.
- it: cozy:tenant:admin:base grants nothing in gpu.cozystack.io while GPUs are off
documentSelector:
path: metadata.name
value: cozy:tenant:admin:base