Files
cozystack/internal/gpu/model/tenant.go
Aleksei Sviridkin 964bbcaf95 fix(gpu): share the tenant, quota, coverage and capacity readers
The binder, the admission webhooks, the catalog option source and the
tenant quota gate carried copies of the tenant release name mapping,
the resourceQuotas reader, the permittedHostDevices reader and the
per-VMI device coverage, and the copies had started to drift: the
catalog skipped a budget it could not read, where the others failed.
The VMI webhook still counted one unit per healthy GPU function when a
node had lost capacity, so it refused a VM whose devices an mdev
parent's remaining slots still served while the binder would have kept
them; the units a node serves are now counted once, for both.

They now live in the shared model, next to the tenant walk they depend
on. Coverage keeps two named readers on purpose: for budgets every
attached device covers its pod, since a Lost device still counts
itself, while for node capacity only a device holding units does.
Merging them would count a Lost device twice or not at all.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-10-21 13:31:31 +03:00

72 lines
2.0 KiB
Go

/*
Copyright 2026 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package model
import (
"strings"
"github.com/cozystack/cozystack/internal/gpu"
)
const (
rootTenantNamespace = "tenant-root"
TenantPrefix = "tenant-"
)
// ParentNamespace returns the namespace of the parent of the tenant that
// owns ns, or "" for the root tenant and non-tenant namespaces. The tenant
// chart encodes the hierarchy in the name (a child x of namespace P owns
// P-x, or tenant-x directly under root), so the parent is ns without its
// last "-" segment.
func ParentNamespace(ns string) string {
if ns == rootTenantNamespace || !strings.HasPrefix(ns, TenantPrefix) {
return ""
}
segments := strings.Split(ns, "-")
if len(segments) == 2 {
return rootTenantNamespace
}
return strings.Join(segments[:len(segments)-1], "-")
}
// Ancestors returns ns followed by each ancestor tenant namespace up to
// the root.
func Ancestors(ns string) []string {
var out []string
for cur := ns; cur != ""; cur = ParentNamespace(cur) {
out = append(out, cur)
}
return out
}
// InSubtree reports whether ns is root or a descendant of it.
func InSubtree(ns, root string) bool {
for cur := ns; cur != ""; cur = ParentNamespace(cur) {
if cur == root {
return true
}
}
return false
}
// Entitled reports whether a tenant namespace's labels grant vGPU. The
// tenant chart writes the effective value, parent included, so the label
// alone decides.
func Entitled(namespaceLabels map[string]string) bool {
return namespaceLabels[gpu.LabelNamespaceVGPU] == gpu.LabelValueTrue
}