make deep copy of quota objects before mutations

This commit is contained in:
deads2k
2016-08-29 13:55:13 -04:00
parent 8675adf138
commit f4380f6919

View File

@@ -194,8 +194,11 @@ func (e *quotaEvaluator) checkAttributes(ns string, admissionAttributes []*admis
// documents for these waiters have already been evaluated. Step 1, will mark all the ones that should already have succeeded. // documents for these waiters have already been evaluated. Step 1, will mark all the ones that should already have succeeded.
func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttributes []*admissionWaiter, remainingRetries int) { func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttributes []*admissionWaiter, remainingRetries int) {
// yet another copy to compare against originals to see if we actually have deltas // yet another copy to compare against originals to see if we actually have deltas
originalQuotas := make([]api.ResourceQuota, len(quotas), len(quotas)) originalQuotas, err := copyQuotas(quotas)
copy(originalQuotas, quotas) if err != nil {
utilruntime.HandleError(err)
return
}
atLeastOneChanged := false atLeastOneChanged := false
for i := range admissionAttributes { for i := range admissionAttributes {
@@ -210,7 +213,7 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
// that means that no quota docs applied, so it can get a pass // that means that no quota docs applied, so it can get a pass
atLeastOneChangeForThisWaiter := false atLeastOneChangeForThisWaiter := false
for j := range newQuotas { for j := range newQuotas {
if !quota.Equals(originalQuotas[j].Status.Used, newQuotas[j].Status.Used) { if !quota.Equals(quotas[j].Status.Used, newQuotas[j].Status.Used) {
atLeastOneChanged = true atLeastOneChanged = true
atLeastOneChangeForThisWaiter = true atLeastOneChangeForThisWaiter = true
break break
@@ -220,6 +223,7 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
if !atLeastOneChangeForThisWaiter { if !atLeastOneChangeForThisWaiter {
admissionAttribute.result = nil admissionAttribute.result = nil
} }
quotas = newQuotas quotas = newQuotas
} }
@@ -236,6 +240,7 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
var lastErr error var lastErr error
for i := range quotas { for i := range quotas {
newQuota := quotas[i] newQuota := quotas[i]
// if this quota didn't have its status changed, skip it // if this quota didn't have its status changed, skip it
if quota.Equals(originalQuotas[i].Status.Used, newQuota.Status.Used) { if quota.Equals(originalQuotas[i].Status.Used, newQuota.Status.Used) {
continue continue
@@ -297,6 +302,19 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
e.checkQuotas(quotasToCheck, admissionAttributes, remainingRetries-1) e.checkQuotas(quotasToCheck, admissionAttributes, remainingRetries-1)
} }
func copyQuotas(in []api.ResourceQuota) ([]api.ResourceQuota, error) {
out := make([]api.ResourceQuota, 0, len(in))
for _, quota := range in {
copied, err := api.Scheme.Copy(&quota)
if err != nil {
return nil, err
}
out = append(out, *copied.(*api.ResourceQuota))
}
return out, nil
}
// checkRequest verifies that the request does not exceed any quota constraint. it returns back a copy of quotas not yet persisted // checkRequest verifies that the request does not exceed any quota constraint. it returns back a copy of quotas not yet persisted
// that capture what the usage would be if the request succeeded. It return an error if the is insufficient quota to satisfy the request // that capture what the usage would be if the request succeeded. It return an error if the is insufficient quota to satisfy the request
func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.Attributes) ([]api.ResourceQuota, error) { func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.Attributes) ([]api.ResourceQuota, error) {
@@ -329,8 +347,7 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
hardResources := quota.ResourceNames(resourceQuota.Status.Hard) hardResources := quota.ResourceNames(resourceQuota.Status.Hard)
evaluatorResources := evaluator.MatchesResources() evaluatorResources := evaluator.MatchesResources()
requiredResources := quota.Intersection(hardResources, evaluatorResources) requiredResources := quota.Intersection(hardResources, evaluatorResources)
err := evaluator.Constraints(requiredResources, inputObject) if err := evaluator.Constraints(requiredResources, inputObject); err != nil {
if err != nil {
return nil, admission.NewForbidden(a, fmt.Errorf("failed quota: %s: %v", resourceQuota.Name, err)) return nil, admission.NewForbidden(a, fmt.Errorf("failed quota: %s: %v", resourceQuota.Name, err))
} }
if !hasUsageStats(&resourceQuota) { if !hasUsageStats(&resourceQuota) {
@@ -382,8 +399,13 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
return quotas, nil return quotas, nil
} }
outQuotas, err := copyQuotas(quotas)
if err != nil {
return nil, err
}
for _, index := range interestingQuotaIndexes { for _, index := range interestingQuotaIndexes {
resourceQuota := quotas[index] resourceQuota := outQuotas[index]
hardResources := quota.ResourceNames(resourceQuota.Status.Hard) hardResources := quota.ResourceNames(resourceQuota.Status.Hard)
requestedUsage := quota.Mask(deltaUsage, hardResources) requestedUsage := quota.Mask(deltaUsage, hardResources)
@@ -403,10 +425,10 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
} }
// update to the new usage number // update to the new usage number
quotas[index].Status.Used = newUsage outQuotas[index].Status.Used = newUsage
} }
return quotas, nil return outQuotas, nil
} }
func (e *quotaEvaluator) Evaluate(a admission.Attributes) error { func (e *quotaEvaluator) Evaluate(a admission.Attributes) error {