mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-14 09:59:57 +00:00
* fix(controller): decode old object for delete requests Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: modernize golang Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * fix(config): remove usergroups default Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * fix(config): remove usergroups default Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * sec(ghsa-2ww6-hf35-mfjm): intercept namespace subresource Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * chore: conflicts Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> * feat(api): add rulestatus api Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com> --------- Signed-off-by: Oliver Bähler <oliverbaehler@hotmail.com>
136 lines
3.1 KiB
Go
136 lines
3.1 KiB
Go
// Copyright 2020-2026 Project Capsule Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package tenant
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
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"
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/util/retry"
|
|
|
|
capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2"
|
|
)
|
|
|
|
func (r *Manager) syncCustomResourceQuotaUsages(ctx context.Context, tenant *capsulev1beta2.Tenant) error {
|
|
type resource struct {
|
|
kind string
|
|
group string
|
|
version string
|
|
}
|
|
|
|
resourceList := make([]resource, 0, len(tenant.GetAnnotations()))
|
|
|
|
for k := range tenant.GetAnnotations() {
|
|
if !strings.HasPrefix(k, capsulev1beta2.ResourceQuotaAnnotationPrefix) {
|
|
continue
|
|
}
|
|
|
|
parts := strings.Split(k, "/")
|
|
if len(parts) != 2 {
|
|
r.Log.V(4).Info("non well-formed Resource Limit annotation", "key", k)
|
|
|
|
continue
|
|
}
|
|
|
|
parts = strings.Split(parts[1], "_")
|
|
|
|
if len(parts) != 2 {
|
|
r.Log.V(4).Info("non well-formed Resource Limit annotation, cannot retrieve version", "key", k)
|
|
|
|
continue
|
|
}
|
|
|
|
groupKindParts := strings.Split(parts[0], ".")
|
|
if len(groupKindParts) < 2 {
|
|
r.Log.V(4).Info("non well-formed Resource Limit annotation, cannot retrieve kind and group", "key", k)
|
|
|
|
continue
|
|
}
|
|
|
|
resourceList = append(resourceList, resource{
|
|
kind: groupKindParts[0],
|
|
group: strings.Join(groupKindParts[1:], "."),
|
|
version: parts[1],
|
|
})
|
|
}
|
|
|
|
errGroup := new(errgroup.Group)
|
|
|
|
usedMap := make(map[string]int)
|
|
|
|
defer func() {
|
|
for gvk, used := range usedMap {
|
|
err := retry.RetryOnConflict(retry.DefaultBackoff, func() (retryErr error) {
|
|
tnt := &capsulev1beta2.Tenant{}
|
|
if retryErr = r.Get(ctx, types.NamespacedName{Name: tenant.GetName()}, tnt); retryErr != nil {
|
|
return retryErr
|
|
}
|
|
|
|
if tnt.GetAnnotations() == nil {
|
|
tnt.Annotations = make(map[string]string)
|
|
}
|
|
|
|
tnt.Annotations[capsulev1beta2.UsedAnnotationForResource(gvk)] = fmt.Sprintf("%d", used)
|
|
|
|
return r.Update(ctx, tnt)
|
|
})
|
|
if err != nil {
|
|
r.Log.Error(err, "cannot update custom Resource Quota", "GVK", gvk)
|
|
}
|
|
}
|
|
}()
|
|
|
|
for _, item := range resourceList {
|
|
res := item
|
|
|
|
errGroup.Go(func() (scopeErr error) {
|
|
dynamicClient := dynamic.NewForConfigOrDie(r.RESTConfig)
|
|
|
|
for _, ns := range tenant.Status.Namespaces {
|
|
var list *unstructured.UnstructuredList
|
|
|
|
list, scopeErr = dynamicClient.Resource(schema.GroupVersionResource{Group: res.group, Version: res.version, Resource: res.kind}).List(ctx, metav1.ListOptions{
|
|
FieldSelector: fmt.Sprintf("metadata.namespace==%s", ns),
|
|
})
|
|
if scopeErr != nil {
|
|
return scopeErr
|
|
}
|
|
|
|
key := fmt.Sprintf("%s.%s_%s", res.kind, res.group, res.version)
|
|
|
|
if _, ok := usedMap[key]; !ok {
|
|
usedMap[key] = 0
|
|
}
|
|
|
|
var used int
|
|
|
|
for _, k := range list.Items {
|
|
if k.GetDeletionTimestamp() != nil {
|
|
continue
|
|
}
|
|
|
|
used++
|
|
}
|
|
|
|
usedMap[key] += used
|
|
}
|
|
|
|
return scopeErr
|
|
})
|
|
}
|
|
|
|
if err := errGroup.Wait(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|