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() sizeInBytes := quantity.Value()
for _, capacity := range capacities { for _, capacity := range capacities {
if capacity.StorageClassName == storageClass.Name && if capacity.StorageClassName == storageClass.Name &&
capacity.Capacity != nil && capacitySufficient(capacity, sizeInBytes) &&
capacity.Capacity.Value() >= sizeInBytes &&
b.nodeHasAccess(node, capacity) { b.nodeHasAccess(node, capacity) {
// Enough capacity found. // Enough capacity found.
return true, nil return true, nil
@ -989,6 +988,15 @@ func (b *volumeBinder) hasEnoughCapacity(provisioner string, claim *v1.Persisten
return false, nil 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 { func (b *volumeBinder) nodeHasAccess(node *v1.Node, capacity *storagev1beta1.CSIStorageCapacity) bool {
if capacity.NodeTopology == nil { if capacity.NodeTopology == nil {
// Unavailable // 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{ c := &storagev1beta1.CSIStorageCapacity{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
@ -758,6 +758,10 @@ func makeCapacity(name, storageClassName string, node *v1.Node, capacityStr stri
capacityQuantity := resource.MustParse(capacityStr) capacityQuantity := resource.MustParse(capacityStr)
c.Capacity = &capacityQuantity c.Capacity = &capacityQuantity
} }
if maximumVolumeSizeStr != "" {
maximumVolumeSizeQuantity := resource.MustParse(maximumVolumeSizeStr)
c.MaximumVolumeSize = &maximumVolumeSizeQuantity
}
return c return c
} }
@ -2212,21 +2216,21 @@ func TestCapacity(t *testing.T) {
"network-attached": { "network-attached": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, nil, "1Gi"), makeCapacity("net", waitClassWithProvisioner, nil, "1Gi", ""),
}, },
}, },
"local-storage": { "local-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi"), makeCapacity("net", waitClassWithProvisioner, node1, "1Gi", ""),
}, },
}, },
"multiple": { "multiple": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, nil, "1Gi"), makeCapacity("net", waitClassWithProvisioner, nil, "1Gi", ""),
makeCapacity("net", waitClassWithProvisioner, node2, "1Gi"), makeCapacity("net", waitClassWithProvisioner, node2, "1Gi", ""),
makeCapacity("net", waitClassWithProvisioner, node1, "1Gi"), makeCapacity("net", waitClassWithProvisioner, node1, "1Gi", ""),
}, },
}, },
"no-storage": { "no-storage": {
@ -2236,35 +2240,49 @@ func TestCapacity(t *testing.T) {
"wrong-node": { "wrong-node": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node2, "1Gi"), makeCapacity("net", waitClassWithProvisioner, node2, "1Gi", ""),
}, },
reasons: ConflictReasons{ErrReasonNotEnoughSpace}, reasons: ConflictReasons{ErrReasonNotEnoughSpace},
}, },
"wrong-storage-class": { "wrong-storage-class": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClass, node1, "1Gi"), makeCapacity("net", waitClass, node1, "1Gi", ""),
}, },
reasons: ConflictReasons{ErrReasonNotEnoughSpace}, reasons: ConflictReasons{ErrReasonNotEnoughSpace},
}, },
"insufficient-storage": { "insufficient-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ 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}, reasons: ConflictReasons{ErrReasonNotEnoughSpace},
}, },
"zero-storage": { "zero-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ 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}, reasons: ConflictReasons{ErrReasonNotEnoughSpace},
}, },
"nil-storage": { "nil-storage": {
pvcs: []*v1.PersistentVolumeClaim{provisionedPVC}, pvcs: []*v1.PersistentVolumeClaim{provisionedPVC},
capacities: []*storagev1beta1.CSIStorageCapacity{ capacities: []*storagev1beta1.CSIStorageCapacity{
makeCapacity("net", waitClassWithProvisioner, node1, ""), makeCapacity("net", waitClassWithProvisioner, node1, "", ""),
}, },
reasons: ConflictReasons{ErrReasonNotEnoughSpace}, reasons: ConflictReasons{ErrReasonNotEnoughSpace},
}, },