From d9cce21f051800bdcd7a70fc679bd0e69ad11a92 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 15 May 2019 15:53:28 +0800 Subject: [PATCH] add quota left to namespace quota --- pkg/apis/resources/v1alpha2/register.go | 1 + pkg/models/quotas/quotas.go | 83 ++++++++++++++++++------- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/pkg/apis/resources/v1alpha2/register.go b/pkg/apis/resources/v1alpha2/register.go index 729fc2a42..0f0445917 100644 --- a/pkg/apis/resources/v1alpha2/register.go +++ b/pkg/apis/resources/v1alpha2/register.go @@ -180,6 +180,7 @@ func addWebService(c *restful.Container) error { webservice.Route(webservice.GET("/quotas"). To(quotas.GetClusterQuotas). + Deprecate(). Doc("get whole cluster's resource usage"). Writes(models.ResourceQuota{}). Metadata(restfulspec.KeyOpenAPITags, tags)) diff --git a/pkg/models/quotas/quotas.go b/pkg/models/quotas/quotas.go index b1cc20087..d85686a33 100644 --- a/pkg/models/quotas/quotas.go +++ b/pkg/models/quotas/quotas.go @@ -37,18 +37,33 @@ const ( servicesKey = "count/services" statefulsetsKey = "count/statefulsets.apps" persistentvolumeclaimsKey = "persistentvolumeclaims" - storageClassesKey = "count/storageClass" - namespaceKey = "count/namespace" jobsKey = "count/jobs.batch" cronJobsKey = "count/cronjobs.batch" ) +type NamespacedResourceQuota struct { + Namespace string `json:"namespace,omitempty"` + + Data struct { + v1.ResourceQuotaStatus + + // quota left status, do the math on the side, cause it's + // a lot easier with go-client library + Left v1.ResourceList `json:"left,omitempty"` + } `json:"data,omitempty"` +} + var ( - resourceMap = map[string]string{daemonsetsKey: resources.DaemonSets, deploymentsKey: resources.Deployments, - ingressKey: resources.Ingresses, servicesKey: resources.Services, - statefulsetsKey: resources.StatefulSets, persistentvolumeclaimsKey: resources.PersistentVolumeClaims, podsKey: resources.Pods, - namespaceKey: resources.Namespaces, storageClassesKey: resources.StorageClasses, - jobsKey: resources.Jobs, cronJobsKey: resources.CronJobs} + resourceMap = map[string]string{ + daemonsetsKey: resources.DaemonSets, + deploymentsKey: resources.Deployments, + ingressKey: resources.Ingresses, + servicesKey: resources.Services, + statefulsetsKey: resources.StatefulSets, + persistentvolumeclaimsKey: resources.PersistentVolumeClaims, + podsKey: resources.Pods, + jobsKey: resources.Jobs, + cronJobsKey: resources.CronJobs} ) func getUsage(namespace, resource string) (int, error) { @@ -61,12 +76,14 @@ func getUsage(namespace, resource string) (int, error) { } if err != nil { + glog.Error(err) return 0, err } return result.TotalCount, nil } +// no one use this api anymore, marked as deprecated func GetClusterQuotas() (*models.ResourceQuota, error) { quota := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)} @@ -85,7 +102,7 @@ func GetClusterQuotas() (*models.ResourceQuota, error) { } -func GetNamespaceQuotas(namespace string) (*models.ResourceQuota, error) { +func GetNamespaceQuotas(namespace string) (*NamespacedResourceQuota, error) { quota, err := getNamespaceResourceQuota(namespace) if err != nil { glog.Error(err) @@ -95,23 +112,43 @@ func GetNamespaceQuotas(namespace string) (*models.ResourceQuota, error) { quota = &v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)} } - for k, v := range resourceMap { - if _, exist := quota.Used[v1.ResourceName(k)]; !exist { - if k == namespaceKey || k == storageClassesKey { - continue + var resourceQuotaLeft = v1.ResourceList{} + + for key, hardLimit := range quota.Hard { + if used, ok := quota.Used[key]; ok { + left := hardLimit.DeepCopy() + left.Sub(used) + if hardLimit.Cmp(used) < 0 { + left = resource.MustParse("0") } - used, err := getUsage(namespace, v) - if err != nil { - return nil, err - } - var quantity resource.Quantity - quantity.Set(int64(used)) - quota.Used[v1.ResourceName(k)] = quantity + resourceQuotaLeft[key] = left } } - return &models.ResourceQuota{Namespace: namespace, Data: *quota}, nil + // add extra quota usage, cause user may not specify them + for key, val := range resourceMap { + // only add them when they don't exist in quotastatus + if _, ok := quota.Used[v1.ResourceName(key)]; !ok { + used, err := getUsage(namespace, val) + if err != nil { + glog.Error(err) + return nil, err + } + + quota.Used[v1.ResourceName(key)] = *(resource.NewQuantity(int64(used), resource.DecimalSI)) + } + } + + var result = NamespacedResourceQuota{ + Namespace: namespace, + } + result.Data.Hard = quota.Hard + result.Data.Used = quota.Used + result.Data.Left = resourceQuotaLeft + + return &result, nil + } func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) { @@ -127,14 +164,16 @@ func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) { tmpResourceList[res] = usage } } - } func getNamespaceResourceQuota(namespace string) (*v1.ResourceQuotaStatus, error) { resourceQuotaLister := informers.SharedInformerFactory().Core().V1().ResourceQuotas().Lister() quotaList, err := resourceQuotaLister.ResourceQuotas(namespace).List(labels.Everything()) - if err != nil || len(quotaList) == 0 { + if err != nil { + glog.Error(err) return nil, err + } else if len(quotaList) == 0 { + return nil, nil } quotaStatus := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}