func (e *clusterQuotaAccessor) GetQuotas(namespaceName string) ([]kapi.ResourceQuota, error) { clusterQuotaNames, err := e.waitForReadyClusterQuotaNames(namespaceName) if err != nil { return nil, err } resourceQuotas := []kapi.ResourceQuota{} for _, clusterQuotaName := range clusterQuotaNames { clusterQuota, err := e.clusterQuotaLister.Get(clusterQuotaName) if kapierrors.IsNotFound(err) { continue } if err != nil { return nil, err } clusterQuota = e.checkCache(clusterQuota) // now convert to a ResourceQuota convertedQuota := kapi.ResourceQuota{} convertedQuota.ObjectMeta = clusterQuota.ObjectMeta convertedQuota.Namespace = namespaceName convertedQuota.Spec = clusterQuota.Spec.Quota convertedQuota.Status = clusterQuota.Status.Total resourceQuotas = append(resourceQuotas, convertedQuota) } return resourceQuotas, nil }
func (q *quota) Admit(a admission.Attributes) (err error) { if a.GetSubresource() != "" { return nil } if a.GetOperation() == "DELETE" { return nil } key := &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Namespace: a.GetNamespace(), Name: "", }, } // concurrent operations that modify quota tracked resources can cause a conflict when incrementing usage // as a result, we will attempt to increment quota usage per request up to numRetries limit // we fuzz each retry with an interval period to attempt to improve end-user experience during concurrent operations numRetries := 10 interval := time.Duration(rand.Int63n(90)+int64(10)) * time.Millisecond items, err := q.indexer.Index("namespace", key) if err != nil { return admission.NewForbidden(a, fmt.Errorf("unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), a.GetResource())) } if len(items) == 0 { return nil } for i := range items { quota := items[i].(*api.ResourceQuota) for retry := 1; retry <= numRetries; retry++ { // we cannot modify the value directly in the cache, so we copy status := &api.ResourceQuotaStatus{ Hard: api.ResourceList{}, Used: api.ResourceList{}, } for k, v := range quota.Status.Hard { status.Hard[k] = *v.Copy() } for k, v := range quota.Status.Used { status.Used[k] = *v.Copy() } dirty, err := IncrementUsage(a, status, q.client) if err != nil { return admission.NewForbidden(a, err) } if dirty { // construct a usage record usage := api.ResourceQuota{ ObjectMeta: api.ObjectMeta{ Name: quota.Name, Namespace: quota.Namespace, ResourceVersion: quota.ResourceVersion, Labels: quota.Labels, Annotations: quota.Annotations}, } usage.Status = *status _, err = q.client.ResourceQuotas(usage.Namespace).UpdateStatus(&usage) if err == nil { break } // we have concurrent requests to update quota, so look to retry if needed if retry == numRetries { return admission.NewForbidden(a, fmt.Errorf("unable to %s %s at this time because there are too many concurrent requests to increment quota", a.GetOperation(), a.GetResource())) } time.Sleep(interval) // manually get the latest quota quota, err = q.client.ResourceQuotas(usage.Namespace).Get(quota.Name) if err != nil { return admission.NewForbidden(a, err) } } } } return nil }