Skip to content

Commit

Permalink
Add quota decision in resource calculator create api
Browse files Browse the repository at this point in the history
Signed-off-by: Imtiaz Uddin <[email protected]>
  • Loading branch information
Imtiaz246 committed Sep 29, 2023
1 parent b97e34a commit 35954cd
Showing 1 changed file with 119 additions and 2 deletions.
121 changes: 119 additions & 2 deletions pkg/registry/meta/resourcecalculator/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ package resourceCalculator

import (
"context"
"fmt"
"strings"

core "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand All @@ -28,6 +31,8 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
kmapi "kmodules.xyz/client-go/api/v1"
"kmodules.xyz/client-go/cluster"
"kmodules.xyz/resource-metadata/apis/management/v1alpha1"
rsapi "kmodules.xyz/resource-metadata/apis/meta/v1alpha1"
resourcemetrics "kmodules.xyz/resource-metrics"
"kmodules.xyz/resource-metrics/api"
Expand Down Expand Up @@ -96,8 +101,9 @@ func (r *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
return nil, apierrors.NewInternalError(err)
}
rid := kmapi.NewResourceID(mapping)
pq := getProjectQuota(r.kc, u)

resp, err := ToGenericResource(&u, rid)
resp, err := ToGenericResource(&u, rid, pq)
if err != nil {
return nil, apierrors.NewInternalError(err)
}
Expand All @@ -110,7 +116,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) (*rsapi.ResourceCalculatorResponse, error) {
func ToGenericResource(item *unstructured.Unstructured, apiType *kmapi.ResourceID, pq *v1alpha1.ProjectQuota) (*rsapi.ResourceCalculatorResponse, error) {
content := item.UnstructuredContent()

var genres rsapi.ResourceCalculatorResponse
Expand Down Expand Up @@ -184,6 +190,117 @@ func ToGenericResource(item *unstructured.Unstructured, apiType *kmapi.ResourceI
}
genres.RoleResourceLimits = rv
}
{
rv, err := quota(content, pq, apiType)
if err != nil {
return nil, err
}
genres.Quota = *rv
}
}
return &genres, nil
}

func quota(obj map[string]interface{}, pq *v1alpha1.ProjectQuota, apiType *kmapi.ResourceID) (*rsapi.QuotaDecision, error) {
qd := &rsapi.QuotaDecision{
Decision: rsapi.DecisionAllow,
Violations: make([]string, 0),
}
if pq == nil {
qd.Decision = rsapi.DecisionNoOpinion
return qd, nil
}
c, err := api.Load(obj)
if err != nil {
return nil, err
}

requests, err := c.AppResourceRequests(obj)
if err != nil {
return nil, err
}
limits, err := c.AppResourceLimits(obj)
if err != nil {
return nil, err
}

for _, quota := range pq.Status.Quotas {
if quota.Group == apiType.Group {
if quota.Kind != "" && quota.Kind != apiType.Kind {
continue
}
hardRequests, hardLimits := extractRequestsLimits(quota.Hard)
usedRequests, usedLimits := extractRequestsLimits(quota.Used)

totRequestsUsage := api.AddResourceList(requests, usedRequests)
for rn, usageQuan := range totRequestsUsage {
hr, found := hardRequests[rn]
if !found {
continue
}
if usageQuan.Cmp(hr) > 0 {
r := requests[rn]
u := usedRequests[rn]
l := hardRequests[rn]

qd.Decision = rsapi.DecisionDeny
qd.Violations = append(qd.Violations,
fmt.Sprintf("Project quota exceeded. Requested: requests.%s=%s, Used: requests.%s=%s, Limited: requests.%s=%s", rn, r.String(), rn, u.String(), rn, l.String()))
}
}

totLimitsUsage := api.AddResourceList(limits, usedLimits)
for rn, usageQuan := range totLimitsUsage {
hl, found := hardLimits[rn]
if !found {
continue
}
if usageQuan.Cmp(hl) > 0 {
r := limits[rn]
u := usedLimits[rn]
l := hardLimits[rn]

qd.Decision = rsapi.DecisionDeny
qd.Violations = append(qd.Violations,
fmt.Sprintf("Project quota exceeded. Requested: limits.%s=%s, Used: limits.%s=%s, Limited: limits.%s=%s", rn, r.String(), rn, u.String(), rn, l.String()))
}
}
}
}

return qd, nil
}

func extractRequestsLimits(res core.ResourceList) (core.ResourceList, core.ResourceList) {
requests := core.ResourceList{}
limits := core.ResourceList{}

for fullName, quan := range res {
identifier, name, found := strings.Cut(fullName.String(), ".")
if !found {
continue
}

if identifier == "requests" {
requests[core.ResourceName(name)] = quan
} else {
limits[core.ResourceName(name)] = quan
}
}

return requests, limits
}

func getProjectQuota(kc client.Client, u unstructured.Unstructured) *v1alpha1.ProjectQuota {
projectId, _, err := cluster.GetProjectId(kc, u.GetNamespace())
if err != nil {
return nil
}
var pj v1alpha1.ProjectQuota
err = kc.Get(context.TODO(), client.ObjectKey{Name: projectId}, &pj)
if err != nil {
return nil
}

return &pj
}

0 comments on commit 35954cd

Please sign in to comment.