CSIStorageCapacity: check MaximumVolumeSize during scheduling

If available, then the MaximumVolumeSize is a better indicator whether
creating a volume has a chance to succeed than the total (?) Capacity,
which is potentially larger and less well-defined.
This commit is contained in:
Patrick Ohly 2021-03-05 14:43:05 +01:00
parent a2972eba07
commit 3fa43540b6
2 changed files with 39 additions and 13 deletions

View File

@ -974,8 +974,7 @@ func (b *volumeBinder) hasEnoughCapacity(provisioner string, claim *v1.Persisten
sizeInBytes := quantity.Value()
for _, capacity := range capacities {
if capacity.StorageClassName == storageClass.Name &&
capacity.Capacity != nil &&
capacity.Capacity.Value() >= sizeInBytes &&
capacitySufficient(capacity, sizeInBytes) &&
b.nodeHasAccess(node, capacity) {
// Enough capacity found.
return true, nil
@ -989,6 +988,15 @@ func (b *volumeBinder) hasEnoughCapacity(provisioner string, claim *v1.Persisten
return false, nil
}
func capacitySufficient(capacity *storagev1beta1.CSIStorageCapacity, sizeInBytes int64) bool {
limit := capacity.Capacity
if capacity.MaximumVolumeSize != nil {
// Prefer MaximumVolumeSize if available, it is more precise.
limit = capacity.MaximumVolumeSize
}
return limit != nil && limit.Value() >= sizeInBytes
}
func (b *volumeBinder) nodeHasAccess(node *v1.Node, capacity *storagev1beta1.CSIStorageCapacity) bool {
if capacity.NodeTopology == nil {
// Unavailable

View File

@ -743,7 +743,7 @@ func makeCSIDriver(name string, storageCapacity bool) *storagev1.CSIDriver {
}
}
func makeCapacity(name, storageClassName string, node *v1.Node, capacityStr string) *storagev1beta1.CSIStorageCapacity {
func makeCapacity(name, storageClassName string, node *v1.Node, capacityStr, maximumVolumeSizeStr string) *storagev1beta1.CSIStorageCapacity {
c := &storagev1beta1.CSIStorageCapacity{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@ -758,6 +758,10 @@ func makeCapacity(name, storageClassName string, node *v1.Node, capacityStr stri
capacityQuantity := resource.MustParse(capacityStr)
c.Capacity = &capacityQuantity
}
if maximumVolumeSizeStr != "" {
maximumVolumeSizeQuantity := resource.MustParse(maximumVolumeSizeStr)
c.MaximumVolumeSize = &maximumVolumeSizeQuantity
}
return c
}
@ -2212,21 +2216,21 @@ func TestCapacity(t *testing.T) {
"network-attached": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, nil, "1Gi"),
makeCapacity("net", waitClassWithProvisioner, nil, "1Gi", ""),
},
},
"local-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi"),
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi", ""),
},
},
"multiple": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, nil, "1Gi"),
makeCapacity("net", waitClassWithProvisioner, node2, "1Gi"),
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi"),
makeCapacity("net", waitClassWithProvisioner, nil, "1Gi", ""),
makeCapacity("net", waitClassWithProvisioner, node2, "1Gi", ""),
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi", ""),
},
},
"no-storage": {
@ -2236,35 +2240,49 @@ func TestCapacity(t *testing.T) {
"wrong-node": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node2, "1Gi"),
makeCapacity("net", waitClassWithProvisioner, node2, "1Gi", ""),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},
"wrong-storage-class": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClass, node1, "1Gi"),
makeCapacity("net", waitClass, node1, "1Gi", ""),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},
"insufficient-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, "1Mi"),
makeCapacity("net", waitClassWithProvisioner, node1, "1Mi", ""),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},
"insufficient-volume-size": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi", "1Mi"),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},
"zero-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, "0Mi"),
makeCapacity("net", waitClassWithProvisioner, node1, "0Mi", ""),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},
"zero-volume-size": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, "", "0Mi"),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},
"nil-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, ""),
makeCapacity("net", waitClassWithProvisioner, node1, "", ""),
},
reasons: ConflictReasons{ErrReasonNotEnoughSpace},
},