Files
Oliver BählerandGitHub 2252c530f4 feat(performance): removed duplicate client calls from all admission paths (#2054)
* chore

* perfromance improvements

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* perfromance improvements

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

* feat(performance): removed duplicate client calls from all admission paths

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>

---------

Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
2026-07-28 14:32:42 +02:00

315 lines
7.8 KiB
Go

// Copyright 2020-2026 Project Capsule Authors
// SPDX-License-Identifier: Apache-2.0
package customquotas
import (
"context"
"errors"
"fmt"
"sort"
"github.com/go-logr/logr"
k8smeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
"github.com/projectcapsule/capsule/internal/cache"
"github.com/projectcapsule/capsule/pkg/api/meta"
"github.com/projectcapsule/capsule/pkg/runtime/quota"
)
type quotaUsageReconcileInput struct {
Log logr.Logger
Client client.Client
Mapper k8smeta.RESTMapper
JSONPathCache *cache.JSONPathCache
CELCache *cache.CELCache
Sources []capsulev1beta2.CustomQuotaSpecSource
ScopeSelectors []metav1.LabelSelector
// For namespaced CustomQuota:
// []string{instance.Namespace}
//
// For GlobalCustomQuota:
// resolved namespaces or []string{"*"}
Namespaces []string
// Namespaced CustomQuota should only accept namespaced targets.
RequireNamespacedTargets bool
// Used for compiled target cache.
CacheKey string
TargetsCache *cache.CompiledTargetsCache[string]
}
type quotaUsageReconcileResult struct {
Targets []capsulev1beta2.CustomQuotaStatusTarget
Usage capsulev1beta2.CustomQuotaStatusUsage
Claims []capsulev1beta2.CustomQuotaClaimItem
}
type quotaClaimKey struct {
UID types.UID
Group string
Version string
Kind string
Namespace string
Name string
}
func reconcileQuotaUsage(
ctx context.Context,
in quotaUsageReconcileInput,
limit resource.Quantity,
) (quotaUsageReconcileResult, error) {
out := quotaUsageReconcileResult{
Targets: []capsulev1beta2.CustomQuotaStatusTarget{},
Usage: capsulev1beta2.CustomQuotaStatusUsage{},
Claims: nil,
}
for _, src := range in.Sources {
kind := src.GroupVersionKind()
mapping, err := in.Mapper.RESTMapping(kind.GroupKind(), kind.Version)
if err != nil {
return out, fmt.Errorf("failed to resolve REST mapping for %s: %w", kind.String(), err)
}
if in.RequireNamespacedTargets && mapping.Scope.Name() != k8smeta.RESTScopeNameNamespace {
return out, fmt.Errorf("GVK %s is not namespaced", kind.String())
}
out.Targets = append(out.Targets, capsulev1beta2.CustomQuotaStatusTarget{
GroupVersionKind: metav1.GroupVersionKind(kind),
CustomQuotaSpecSourceConfig: src.CustomQuotaSpecSourceConfig,
Scope: mapping.Scope.Name(),
})
}
targets, err := CompileTargets(in.JSONPathCache, in.CELCache, out.Targets)
if err != nil {
return out, err
}
if in.TargetsCache != nil && in.CacheKey != "" {
in.TargetsCache.Set(in.CacheKey, targets)
}
var errs []error
itemsByGVK := make(map[schema.GroupVersionKind][]unstructured.Unstructured, len(out.Targets))
claimsByKey := make(map[quotaClaimKey]capsulev1beta2.CustomQuotaClaimItem)
for _, target := range targets {
gvk := schema.GroupVersionKind{
Group: target.Group,
Version: target.Version,
Kind: target.Kind,
}
items, ok := itemsByGVK[gvk]
if !ok {
items, err = getResourcesByGVK(
ctx,
gvk,
in.Client,
target.Scope == k8smeta.RESTScopeNameNamespace,
in.ScopeSelectors,
in.Namespaces...,
)
if err != nil {
errs = append(errs, fmt.Errorf("list resources for %s: %w", gvk.String(), err))
continue
}
itemsByGVK[gvk] = items
}
in.Log.V(5).Info("listed resources for target",
"gvk", gvk.String(),
"count", len(items),
"namespaces", in.Namespaces,
"scopeSelectors", in.ScopeSelectors,
)
for _, item := range items {
matches, err := MatchesCompiledSelectorsWithFields(ctx, item, target.CompiledSelectors)
if err != nil {
errs = append(errs, fmt.Errorf(
"evaluate selectors for %s/%s (%s): %w",
item.GetNamespace(),
item.GetName(),
item.GetObjectKind().GroupVersionKind().String(),
err,
))
continue
}
if !matches {
continue
}
rawUsage, err := usageForTarget(ctx, item, target)
if err != nil {
errs = append(errs, err)
continue
}
accountingUsage := rawUsage.DeepCopy()
switch target.Operation {
case quota.OpSub:
accountingUsage.Neg()
out.Usage.Used.Add(accountingUsage)
quota.ClampQuantityToZero(&out.Usage.Used)
case quota.OpAdd, quota.OpCount:
out.Usage.Used.Add(accountingUsage)
default:
errs = append(errs, fmt.Errorf(
"unsupported operation %q for %s/%s (%s)",
target.Operation,
item.GetNamespace(),
item.GetName(),
item.GetObjectKind().GroupVersionKind().String(),
))
continue
}
key := quotaClaimKey{
UID: item.GetUID(),
Group: target.Group,
Version: target.Version,
Kind: target.Kind,
Namespace: item.GetNamespace(),
Name: item.GetName(),
}
claim, exists := claimsByKey[key]
if !exists {
claim = capsulev1beta2.CustomQuotaClaimItem{
GroupVersionKind: metav1.GroupVersionKind{
Group: target.Group,
Version: target.Version,
Kind: target.Kind,
},
NamespacedObjectWithUIDReference: meta.NamespacedObjectWithUIDReference{
Name: item.GetName(),
Namespace: meta.RFC1123SubdomainName(item.GetNamespace()),
UID: item.GetUID(),
},
Usage: resource.MustParse("0"),
}
}
// Claims must mirror the same net per-object contribution used by admission reservations.
// This is required so reservationMaterializedLedger(res, claims) can clear reservations.
//
// For pure subtraction sources, the claim remains present but clamps to zero.
// Example:
// claim usage = max(0 - 2Gi, 0) = 0
//
// For mixed sources, e.g. count + sub cpu:
// claim usage = max(1 - 500m, 0) = 500m
claim.Usage.Add(accountingUsage)
quota.ClampQuantityToZero(&claim.Usage)
claimsByKey[key] = claim
}
}
quota.ClampQuantityToZero(&out.Usage.Used)
out.Usage.Available = limit.DeepCopy()
out.Usage.Available.Sub(out.Usage.Used)
quota.ClampQuantityToZero(&out.Usage.Available)
out.Claims = make([]capsulev1beta2.CustomQuotaClaimItem, 0, len(claimsByKey))
for _, claim := range claimsByKey {
out.Claims = append(out.Claims, claim)
}
sort.SliceStable(out.Claims, func(i, j int) bool {
if out.Claims[i].Namespace != out.Claims[j].Namespace {
return out.Claims[i].Namespace < out.Claims[j].Namespace
}
if out.Claims[i].Kind != out.Claims[j].Kind {
return out.Claims[i].Kind < out.Claims[j].Kind
}
return out.Claims[i].Name < out.Claims[j].Name
})
if len(errs) > 0 {
return out, errors.Join(errs...)
}
return out, nil
}
func usageForTarget(
ctx context.Context,
item unstructured.Unstructured,
target cache.CompiledTarget,
) (resource.Quantity, error) {
switch target.Operation {
case quota.OpCount:
return *resource.NewQuantity(1, resource.DecimalSI), nil
case quota.OpAdd, quota.OpSub:
var (
usage resource.Quantity
err error
)
switch {
case target.CompiledCEL != nil:
usage, err = target.CompiledCEL.EvaluateQuantity(ctx, item)
case target.CompiledPath != nil:
usage, err = quota.ParseQuantityFromUnstructured(item, target.CompiledPath)
default:
err = fmt.Errorf("compiled usage expression is missing")
}
if err != nil {
return resource.Quantity{}, fmt.Errorf(
"get usage from %s/%s (%s) path %q cel %q op %q: %w",
item.GetNamespace(),
item.GetName(),
item.GetObjectKind().GroupVersionKind().String(),
target.Path,
target.CEL,
target.Operation,
err,
)
}
return usage, nil
default:
return resource.Quantity{}, fmt.Errorf(
"unsupported operation %q for %s/%s (%s)",
target.Operation,
item.GetNamespace(),
item.GetName(),
item.GetObjectKind().GroupVersionKind().String(),
)
}
}