Skip to content

Commit

Permalink
Support resource quota at namespace level (#325)
Browse files Browse the repository at this point in the history
Signed-off-by: Tamal Saha <[email protected]>
  • Loading branch information
tamalsaha authored Aug 28, 2024
1 parent c8366da commit 0048d74
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 45 deletions.
13 changes: 13 additions & 0 deletions artifacts/resourcequota/ns-quota.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: management.k8s.appscode.com/v1alpha1
kind: ProjectQuota
metadata:
name: demo
spec:
quotas:
- group: kubedb.com
kind: Postgres
hard:
limits.memory: 1Gi
- group: kubedb.com
hard:
limits.memory: 1Gi
File renamed without changes.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ require (
k8s.io/kube-state-metrics/v2 v2.12.0
kmodules.xyz/apiversion v0.2.0
kmodules.xyz/authorizer v0.29.1
kmodules.xyz/client-go v0.30.13-0.20240820121523-30dbfd82b4c6
kmodules.xyz/client-go v0.30.13
kmodules.xyz/custom-resources v0.30.0
kmodules.xyz/go-containerregistry v0.0.12
kmodules.xyz/monitoring-agent-api v0.29.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,8 @@ kmodules.xyz/apiversion v0.2.0 h1:vAQYqZFm4xu4pbB1cAdHbFEPES6EQkcR4wc06xdTOWk=
kmodules.xyz/apiversion v0.2.0/go.mod h1:oPX8g8LvlPdPX3Yc5YvCzJHQnw3YF/X4/jdW0b1am80=
kmodules.xyz/authorizer v0.29.1 h1:uByGGoryKbZcfiEAhjcK/Y345I9mygNQP7DVpkMbNQQ=
kmodules.xyz/authorizer v0.29.1/go.mod h1:kZRhclL8twzyt2bQuJQJbpYww2sc+qFr8I5PPoq/sWY=
kmodules.xyz/client-go v0.30.13-0.20240820121523-30dbfd82b4c6 h1:WsCrZxH8FD5WEiiHdFwXrjnURyUnQSLA7fa3VqzVu38=
kmodules.xyz/client-go v0.30.13-0.20240820121523-30dbfd82b4c6/go.mod h1:XL3PDQIXG4s3xNRL2SSxIvi8b2WyMGpn26dFnOBz0j4=
kmodules.xyz/client-go v0.30.13 h1:Tpvg7ZlSNlzjB0NZT9hUe9JrO7FN4PjUUF1hNUXSku0=
kmodules.xyz/client-go v0.30.13/go.mod h1:XL3PDQIXG4s3xNRL2SSxIvi8b2WyMGpn26dFnOBz0j4=
kmodules.xyz/crd-schema-fuzz v0.29.1 h1:zJTlWYOrT5dsVVHW8HGcnR/vaWfxQfNh11QwTtkYpcs=
kmodules.xyz/crd-schema-fuzz v0.29.1/go.mod h1:n708z9YQqLMP2KNLQVgBcRJw1QpSWLvpNCEi+KJDOYE=
kmodules.xyz/custom-resources v0.30.0 h1:vR3CbseHMLwR4GvtcJJuRuwIV8voKqFqNii27rMcm1o=
Expand Down
48 changes: 37 additions & 11 deletions pkg/controllers/projectquota/projectquota_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ func (r *ProjectQuotaReconciler) Reconcile(ctx context.Context, req ctrl.Request

var pj v1alpha1.ProjectQuota
if err := r.Get(ctx, req.NamespacedName, &pj); err != nil {
log.Error(err, "unable to fetch ProjectQuota")
return ctrl.Result{}, client.IgnoreNotFound(err)
}

Expand Down Expand Up @@ -164,10 +163,7 @@ type QuotaResult struct {
}

func (r *ProjectQuotaReconciler) CalculateStatus(pj *v1alpha1.ProjectQuota) error {
var nsList core.NamespaceList
err := r.List(context.TODO(), &nsList, client.MatchingLabels{
clustermeta.LabelKeyRancherFieldProjectId: pj.Name,
})
nsList, err := r.getNamespaceList(pj.Name)
if err != nil {
return err
}
Expand Down Expand Up @@ -266,6 +262,29 @@ func (r *ProjectQuotaReconciler) CalculateStatus(pj *v1alpha1.ProjectQuota) erro
return nil
}

func (r *ProjectQuotaReconciler) getNamespaceList(name string) (*core.NamespaceList, error) {
var nsList core.NamespaceList
if clustermeta.IsRancherManaged(r.RESTMapper()) {
err := r.List(context.TODO(), &nsList, client.MatchingLabels{
clustermeta.LabelKeyRancherFieldProjectId: name,
})
if err != nil {
return nil, err
}
if len(nsList.Items) > 0 {
return &nsList, nil
}
}

err := r.List(context.TODO(), &nsList, client.MatchingLabels{
core.LabelMetadataName: name,
})
if err != nil {
return nil, err
}
return &nsList, nil
}

func (r *ProjectQuotaReconciler) UsedQuota(ns string, typeInfo APIType) (core.ResourceList, *QuotaResult, error) {
gk := schema.GroupKind{
Group: typeInfo.Group,
Expand Down Expand Up @@ -394,12 +413,19 @@ func ProjectQuotaForObjects(kc client.Client) func(_ context.Context, _ client.O
return nil
}

projectId, found := ns.Labels[clustermeta.LabelKeyRancherFieldProjectId]
if !found {
return nil
}
return []reconcile.Request{
{NamespacedName: types.NamespacedName{Name: projectId}},
var req []reconcile.Request

if clustermeta.IsRancherManaged(kc.RESTMapper()) {
projectId, found := ns.Labels[clustermeta.LabelKeyRancherFieldProjectId]
if found {
req = append(req, reconcile.Request{
NamespacedName: types.NamespacedName{Name: projectId},
})
}
}
req = append(req, reconcile.Request{
NamespacedName: types.NamespacedName{Name: ns.Name},
})
return req
}
}
31 changes: 19 additions & 12 deletions pkg/registry/meta/resourcecalculator/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"strings"

core "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -101,20 +100,28 @@ func mergeRequestsLimits(requests, limits core.ResourceList) core.ResourceList {
return rl
}

func getProjectQuota(kc client.Client, ns string) (*v1alpha1.ProjectQuota, error) {
projectId, _, err := clustermeta.GetProjectId(kc, ns)
if err != nil {
return nil, err
func getApplicableQuotas(kc client.Client, ns string) ([]*v1alpha1.ProjectQuota, error) {
var names []string
if clustermeta.IsRancherManaged(kc.RESTMapper()) {
projectId, _, err := clustermeta.GetProjectId(kc, ns)
if err != nil {
return nil, err
}
names = append(names, projectId)
}
var pj v1alpha1.ProjectQuota
err = kc.Get(context.TODO(), client.ObjectKey{Name: projectId}, &pj)
if err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
names = append(names, ns)

var result []*v1alpha1.ProjectQuota
for _, name := range names {
var pj v1alpha1.ProjectQuota
err := kc.Get(context.TODO(), client.ObjectKey{Name: name}, &pj)
if client.IgnoreNotFound(err) != nil {
return nil, err
} else if err == nil {
result = append(result, &pj)
}
return nil, err
}
return &pj, nil
return result, nil
}

func getGVK(obj map[string]interface{}) schema.GroupVersionKind {
Expand Down
43 changes: 25 additions & 18 deletions pkg/registry/meta/resourcecalculator/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,26 +108,40 @@ func (r *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
}
rid := kmapi.NewResourceID(mapping)

pq, err := getProjectQuota(r.kc, u.GetNamespace())
if err != nil {
return nil, err
}
// Wrap referenced db resource with the OpsRequest object
if rid.Group == "ops.kubedb.com" {
if err = utils.ExpandReferencedAppInOpsObject(r.kc, &u); err != nil {
return nil, err
}
} else if in.Request.Edit && pq != nil {
if err := deductOldDbObjectResourceUsageFromProjectQuota(r.kc, u, pq); err != nil {
return nil, err
}
}

resp, err := ToGenericResource(&u, rid, pq)
resp, err := ToGenericResource(&u, rid)
if err != nil {
return nil, apierrors.NewInternalError(err)
}

quotas, err := getApplicableQuotas(r.kc, u.GetNamespace())
if err != nil {
return nil, err
}
// Wrap referenced db resource with the OpsRequest object
for i := range quotas {
pq := quotas[i]
if rid.Group != "ops.kubedb.com" && in.Request.Edit {
if err := deductOldDbObjectResourceUsageFromProjectQuota(r.kc, u, pq); err != nil {
return nil, err
}
}

rv, err := quota(u.UnstructuredContent(), pq)
if err != nil {
return nil, err
}
resp.Quota = *rv
if rv.Decision == rsapi.DecisionDeny {
break
}
}

in.Response = resp
return in, nil
}
Expand All @@ -136,7 +150,7 @@ func (r *Storage) ConvertToTable(ctx context.Context, object runtime.Object, tab
return r.convertor.ConvertToTable(ctx, object, tableOptions)
}

func ToGenericResource(item *unstructured.Unstructured, apiType *kmapi.ResourceID, pq *v1alpha1.ProjectQuota) (*rsapi.ResourceCalculatorResponse, error) {
func ToGenericResource(item *unstructured.Unstructured, apiType *kmapi.ResourceID) (*rsapi.ResourceCalculatorResponse, error) {
content := item.UnstructuredContent()

var genres rsapi.ResourceCalculatorResponse
Expand Down Expand Up @@ -210,13 +224,6 @@ func ToGenericResource(item *unstructured.Unstructured, apiType *kmapi.ResourceI
}
genres.RoleResourceLimits = rv
}
{
rv, err := quota(content, pq)
if err != nil {
return nil, err
}
genres.Quota = *rv
}
}
return &genres, nil
}
Expand Down
2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1951,7 +1951,7 @@ kmodules.xyz/authorizer/apiserver
kmodules.xyz/authorizer/rbac
kmodules.xyz/authorizer/rbac/helpers
kmodules.xyz/authorizer/rbac/validation
# kmodules.xyz/client-go v0.30.13-0.20240820121523-30dbfd82b4c6
# kmodules.xyz/client-go v0.30.13
## explicit; go 1.22.0
kmodules.xyz/client-go
kmodules.xyz/client-go/api/v1
Expand Down

0 comments on commit 0048d74

Please sign in to comment.