Change:
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! If the matter is security related, please disclose it privately via --What happened: When a pvc was specified with a quantity including the suffix : attempts to include that quantity in the resourcequota's storage resulted in the following error, which blocked all other updates to the resourcequota: What you expected to happen: Updates to all other quantities in the resourcequota should not have been blocked by the error above In general, as a suffix (milli) doesn't really make sense when used in the context of memory, and causes issues like the above. Would it be possible to deny the usage of this suffix at admission time for things like pvcs? (Our mitigation right now is to add a rule that does this in Open Policy Agent). How to reproduce it (as minimally and precisely as possible): Create a resourcequota object with limits on pvcs and the a storage type, e.g. Then create a PVC with the following spec: The resource quota controller should start producing the error log above. Anything else we need to know?: Environment: Kubernetes version (use ): v1.17.3 Cloud provider or hardware configuration: OS (e.g: ): Kernel (e.g. ): Install tools: Network plugin and version (if this is a network-related bug): Others:
/sig api-machinery
/remove-sig api-machinery /sig storage
i guess we should leave api-machinery! /sig api-machinery
/assign I think we should validate the value of field.
I've filed to address this issue.
looks like it is not easy to tighten the validation and that requires careful consideration xref:
/assign
"Updates to all other quantities in the resourcequota should not have been blocked by the error above" should be fixed separately
Opened to round storage requests up to the nearest byte in the quota evaluator.

Discussion:
// charge for storage if request, found := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; found { <ins> roundedRequest := request.DeepCopy() if !roundedRequest.RoundUp(0) { // Ensure storage requests are counted as whole byte values, to pass resourcequota validation. // See http://issue.k8s.io/94313 request = roundedRequest } </ins> result[corev1.ResourceRequestsStorage] = request // charge usage to the storage class (if present) if len(storageClassRef) > 0 {

StorageClassName: &classGold, }) <ins> validClaimWithNonIntegerStorage := validClaim.DeepCopy() validClaimWithNonIntegerStorage.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)] = resource.MustParse("1001m") validClaimByStorageClassWithNonIntegerStorage := validClaimByStorageClass.DeepCopy() validClaimByStorageClassWithNonIntegerStorage.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)] = resource.MustParse("1001m") </ins> evaluator := NewPersistentVolumeClaimEvaluator(nil) testCases := map[string]struct { pvc   *api.PersistentVolumeClaim

generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), }, }, <ins> "pvc-usage-rounded": { pvc: validClaimWithNonIntegerStorage, usage: corev1.ResourceList{ corev1.ResourceRequestsStorage:        resource.MustParse("2"), // 1001m -> 2 corev1.ResourcePersistentVolumeClaims: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), }, }, "pvc-usage-by-class-rounded": { pvc: validClaimByStorageClassWithNonIntegerStorage, usage: corev1.ResourceList{ corev1.ResourceRequestsStorage:                                                                    resource.MustParse("2"), // 1001m -> 2 corev1.ResourcePersistentVolumeClaims:                                                             resource.MustParse("1"), V1ResourceByStorageClass(classGold, corev1.ResourceRequestsStorage):                               resource.MustParse("2"), // 1001m -> 2 V1ResourceByStorageClass(classGold, corev1.ResourcePersistentVolumeClaims):                        resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), }, }, </ins> } for testName, testCase := range testCases { actual, err := evaluator.Usage(testCase.pvc)

t.Errorf("%s unexpected error: %v", testName, err) } if !quota.Equals(testCase.usage, actual) { <del> t.Errorf("%s expected: %v, actual: %v", testName, testCase.usage, actual) </del> <ins> t.Errorf("%s expected:n%vn, actual:n%v", testName, testCase.usage, actual) </ins> } } }