Files
cozystack/packages
Andrei Kvapil c725a41f3a feat(tenant): recursive (hierarchical) resource quotas for sub-tenants (#3086)
## What this PR does

Adds **hierarchical (recursive) resource quotas** for tenants so a
tenant can no longer escalate its allocation by creating sub-tenants.
Today `tenant.spec.resourceQuotas` becomes a plain per-namespace
`ResourceQuota`, which ignores the tenant tree: a super-admin inside a
quota'd tenant can create a sub-tenant with a larger quota — or no quota
at all — and consume more than was allocated.

A tenant's declared quota is now treated as the budget for its **whole
sub-tree**:

- A child that declares its own quota **carves a fixed slice** out of
the parent's remaining budget (parent 10 CPU → child 4 CPU → parent has
6 left).
- A child **without** a quota **shares** the parent's remaining pool
rather than being unbounded.
- A child quota that exceeds the parent's remaining budget is
**rejected**.

Two cooperating layers, mirroring OpenShift's ClusterResourceQuota split
(admission + reconciliation):

1. **Declaration-time gate** in the aggregated apiserver
(`apps.cozystack.io` Tenant create/update): deterministically rejects a
sub-tenant whose declared quota exceeds the parent's remaining budget.
2. **Runtime enforcer** in `cozystack-controller`: maintains a
per-namespace `tenant-quota-allocated` ResourceQuota that clamps each
pool member to its share of the parent budget, aggregating sub-tree
usage so members collectively stay within the budget. Kubernetes
enforces the most restrictive ResourceQuota in a namespace, so this
binds without fighting Flux over the chart-rendered `tenant-quota`.
Overcommitted pools (a parent quota lowered below the sum of its
children) emit a `QuotaOvercommitted` event. A
`--tenant-quota-buffer-percent` flag temporarily inflates pool budgets
during rollout so workloads already over a freshly-introduced quota keep
running.

The model and the selector-spans-namespaces / aggregated-usage approach
are adapted from OpenShift's ClusterResourceQuota controllers
(`openshift/api`, `library-go`, `cluster-policy-controller`,
`apiserver-library-go`), Apache-2.0, Copyright Red Hat, Inc. and the
OpenShift Authors; attribution is preserved in the source. The
per-namespace quota arithmetic reuses the upstream Kubernetes quota
helpers (`k8s.io/apiserver/pkg/quota/v1`).

No API types or the tenant chart change; the feature works with the
existing `tenant.spec.resourceQuotas` field.

### Screenshots

N/A — no UI changes.

### Release note

```release-note
feat(tenant): tenant resource quotas are now hierarchical — a sub-tenant's quota is carved out of its parent's remaining budget and can no longer exceed it; sub-tenants without a quota share the parent's pool. Set `--tenant-quota-buffer-percent` on cozystack-controller for a temporary grace buffer during upgrades.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added hierarchical tenant quota enforcement across tenant namespaces,
including shared pool handling and configurable quota buffer support.
* Tenant quota changes are now validated on create and update to prevent
over-allocation.

* **Bug Fixes**
* Improved quota calculations so usage, carve-outs, and nested tenant
limits are applied more consistently.
* Stale quota allocations are now cleaned up when namespaces no longer
belong to an active tenant pool.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 17:56:24 +02:00
..