mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 19:23:40 +00:00
When casting resource.Quantity to int64 it may overflow
We need to ensure that when converting resource.Quantity to int64 it does not overflow and if it does, then an error is returned back to the use rather than attempting expansion and provisioning operations with scaled value.
This commit is contained in:
parent
8f8ac98714
commit
75fdb6d554
@ -183,7 +183,13 @@ func (plugin *azureFilePlugin) ExpandVolumeDevice(
|
|||||||
return oldSize, err
|
return oldSize, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := azure.ResizeFileShare(resourceGroup, accountName, shareName, int(volumehelpers.RoundUpToGiB(newSize))); err != nil {
|
requestGiB, err := volumehelpers.RoundUpToGiBInt(newSize)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := azure.ResizeFileShare(resourceGroup, accountName, shareName, requestGiB); err != nil {
|
||||||
return oldSize, err
|
return oldSize, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,11 @@ func (a *azureFileProvisioner) Provision(selectedNode *v1.Node, allowedTopologie
|
|||||||
var sku, resourceGroup, location, account, shareName string
|
var sku, resourceGroup, location, account, shareName string
|
||||||
|
|
||||||
capacity := a.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
capacity := a.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||||
requestGiB := int(volumehelpers.RoundUpToGiB(capacity))
|
requestGiB, err := volumehelpers.RoundUpToGiBInt(capacity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
secretNamespace := a.options.PVC.Namespace
|
secretNamespace := a.options.PVC.Namespace
|
||||||
// Apply ProvisionerParameters (case-insensitive). We leave validation of
|
// Apply ProvisionerParameters (case-insensitive). We leave validation of
|
||||||
// the values to the cloud provider.
|
// the values to the cloud provider.
|
||||||
|
@ -101,7 +101,10 @@ func (util *GCEDiskUtil) CreateVolume(c *gcePersistentDiskProvisioner, node *v1.
|
|||||||
name := volumeutil.GenerateVolumeName(c.options.ClusterName, c.options.PVName, 63) // GCE PD name can have up to 63 characters
|
name := volumeutil.GenerateVolumeName(c.options.ClusterName, c.options.PVName, 63) // GCE PD name can have up to 63 characters
|
||||||
capacity := c.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
capacity := c.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||||
// GCE PDs are allocated in chunks of GiBs
|
// GCE PDs are allocated in chunks of GiBs
|
||||||
requestGB := volumehelpers.RoundUpToGiB(capacity)
|
requestGB, err := volumehelpers.RoundUpToGiB(capacity)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
// Apply Parameters.
|
// Apply Parameters.
|
||||||
// Values for parameter "replication-type" are canonicalized to lower case.
|
// Values for parameter "replication-type" are canonicalized to lower case.
|
||||||
@ -159,7 +162,7 @@ func (util *GCEDiskUtil) CreateVolume(c *gcePersistentDiskProvisioner, node *v1.
|
|||||||
name,
|
name,
|
||||||
diskType,
|
diskType,
|
||||||
selectedZones,
|
selectedZones,
|
||||||
int64(requestGB),
|
requestGB,
|
||||||
*c.options.CloudTags)
|
*c.options.CloudTags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.V(2).Infof("Error creating regional GCE PD volume: %v", err)
|
klog.V(2).Infof("Error creating regional GCE PD volume: %v", err)
|
||||||
@ -176,7 +179,7 @@ func (util *GCEDiskUtil) CreateVolume(c *gcePersistentDiskProvisioner, node *v1.
|
|||||||
name,
|
name,
|
||||||
diskType,
|
diskType,
|
||||||
selectedZone,
|
selectedZone,
|
||||||
int64(requestGB),
|
requestGB,
|
||||||
*c.options.CloudTags)
|
*c.options.CloudTags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.V(2).Infof("Error creating single-zone GCE PD volume: %v", err)
|
klog.V(2).Infof("Error creating single-zone GCE PD volume: %v", err)
|
||||||
|
@ -1212,11 +1212,18 @@ func (plugin *glusterfsPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize res
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find out delta size
|
// Find out delta size
|
||||||
expansionSize := resource.NewScaledQuantity((newSize.Value() - oldSize.Value()), 0)
|
expansionSize := newSize
|
||||||
expansionSizeGiB := int(volumehelpers.RoundUpToGiB(*expansionSize))
|
expansionSize.Sub(oldSize)
|
||||||
|
expansionSizeGiB, err := volumehelpers.RoundUpToGiBInt(expansionSize)
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
|
|
||||||
// Find out requested Size
|
// Find out requested Size
|
||||||
requestGiB := volumehelpers.RoundUpToGiB(newSize)
|
requestGiB, err := volumehelpers.RoundUpToGiB(newSize)
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
|
|
||||||
//Check the existing volume size
|
//Check the existing volume size
|
||||||
currentVolumeInfo, err := cli.VolumeInfo(volumeID)
|
currentVolumeInfo, err := cli.VolumeInfo(volumeID)
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
volumeclient "github.com/libopenstorage/openstorage/api/client/volume"
|
volumeclient "github.com/libopenstorage/openstorage/api/client/volume"
|
||||||
osdspec "github.com/libopenstorage/openstorage/api/spec"
|
osdspec "github.com/libopenstorage/openstorage/api/spec"
|
||||||
volumeapi "github.com/libopenstorage/openstorage/volume"
|
volumeapi "github.com/libopenstorage/openstorage/volume"
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
||||||
@ -60,7 +60,10 @@ func (util *portworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri
|
|||||||
|
|
||||||
capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||||
// Portworx Volumes are specified in GiB
|
// Portworx Volumes are specified in GiB
|
||||||
requestGiB := volumehelpers.RoundUpToGiB(capacity)
|
requestGiB, err := volumehelpers.RoundUpToGiB(capacity)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Perform a best-effort parsing of parameters. Portworx 1.2.9 and later parses volume parameters from
|
// Perform a best-effort parsing of parameters. Portworx 1.2.9 and later parses volume parameters from
|
||||||
// spec.VolumeLabels. So even if below SpecFromOpts() fails to parse certain parameters or
|
// spec.VolumeLabels. So even if below SpecFromOpts() fails to parse certain parameters or
|
||||||
@ -211,7 +214,11 @@ func (util *portworxVolumeUtil) ResizeVolume(spec *volume.Spec, newSize resource
|
|||||||
}
|
}
|
||||||
|
|
||||||
vol := vols[0]
|
vol := vols[0]
|
||||||
newSizeInBytes := uint64(volumehelpers.RoundUpToGiB(newSize) * volumehelpers.GiB)
|
tBytes, ok := newSize.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("quantity %v is too great, overflows int64", newSize)
|
||||||
|
}
|
||||||
|
newSizeInBytes := uint64(tBytes)
|
||||||
if vol.Spec.Size >= newSizeInBytes {
|
if vol.Spec.Size >= newSizeInBytes {
|
||||||
klog.Infof("Portworx volume: %s already at size: %d greater than or equal to new "+
|
klog.Infof("Portworx volume: %s already at size: %d greater than or equal to new "+
|
||||||
"requested size: %d. Skipping resize.", spec.Name(), vol.Spec.Size, newSizeInBytes)
|
"requested size: %d. Skipping resize.", spec.Name(), vol.Spec.Size, newSizeInBytes)
|
||||||
|
@ -649,7 +649,11 @@ func (util *rbdUtil) ExpandImage(rbdExpander *rbdVolumeExpander, oldSize resourc
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Convert to MB that rbd defaults on.
|
// Convert to MB that rbd defaults on.
|
||||||
sz := int(volumehelpers.RoundUpToMiB(newSize))
|
sz, err := volumehelpers.RoundUpToMiBInt(newSize)
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
|
|
||||||
newVolSz := fmt.Sprintf("%d", sz)
|
newVolSz := fmt.Sprintf("%d", sz)
|
||||||
newSizeQuant := resource.MustParse(fmt.Sprintf("%dMi", sz))
|
newSizeQuant := resource.MustParse(fmt.Sprintf("%dMi", sz))
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
|
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
|
||||||
|
"//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library",
|
||||||
"//vendor/github.com/thecodeteam/goscaleio/types/v1:go_default_library",
|
"//vendor/github.com/thecodeteam/goscaleio/types/v1:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
||||||
|
@ -55,6 +55,10 @@ type sioVolume struct {
|
|||||||
volume.MetricsNil
|
volume.MetricsNil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
minimumVolumeSizeGiB = 8
|
||||||
|
)
|
||||||
|
|
||||||
// *******************
|
// *******************
|
||||||
// volume.Volume Impl
|
// volume.Volume Impl
|
||||||
var _ volume.Volume = &sioVolume{}
|
var _ volume.Volume = &sioVolume{}
|
||||||
@ -272,21 +276,17 @@ func (v *sioVolume) Provision(selectedNode *api.Node, allowedTopologies []api.To
|
|||||||
|
|
||||||
// setup volume attrributes
|
// setup volume attrributes
|
||||||
genName := v.generateName("k8svol", 11)
|
genName := v.generateName("k8svol", 11)
|
||||||
eightGig := int64(8 * volumehelpers.GiB)
|
|
||||||
|
|
||||||
capacity := v.options.PVC.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)]
|
capacity := v.options.PVC.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)]
|
||||||
|
|
||||||
volSizeBytes := capacity.Value()
|
volSizeGiB, err := volumehelpers.RoundUpToGiB(capacity)
|
||||||
volSizeGB := int64(volumehelpers.RoundUpToGiB(capacity))
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
if volSizeBytes == 0 {
|
|
||||||
return nil, fmt.Errorf("invalid volume size of 0 specified")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if volSizeBytes < eightGig {
|
if volSizeGiB < minimumVolumeSizeGiB {
|
||||||
eightGiBCapacity := resource.NewQuantity(eightGig, resource.BinarySI)
|
volSizeGiB = minimumVolumeSizeGiB
|
||||||
volSizeGB = int64(volumehelpers.RoundUpToGiB(*eightGiBCapacity))
|
klog.V(4).Info(log("capacity less than 8Gi found, adjusted to %dGi", volSizeGiB))
|
||||||
klog.V(4).Info(log("capacity less than 8Gi found, adjusted to %dGi", volSizeGB))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ func (v *sioVolume) Provision(selectedNode *api.Node, allowedTopologies []api.To
|
|||||||
|
|
||||||
// create volume
|
// create volume
|
||||||
volName := genName
|
volName := genName
|
||||||
vol, err := v.sioMgr.CreateVolume(volName, volSizeGB)
|
vol, err := v.sioMgr.CreateVolume(volName, volSizeGiB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Error(log("provision failed while creating volume: %v", err))
|
klog.Error(log("provision failed while creating volume: %v", err))
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -333,7 +333,7 @@ func (v *sioVolume) Provision(selectedNode *api.Node, allowedTopologies []api.To
|
|||||||
AccessModes: v.options.PVC.Spec.AccessModes,
|
AccessModes: v.options.PVC.Spec.AccessModes,
|
||||||
Capacity: api.ResourceList{
|
Capacity: api.ResourceList{
|
||||||
api.ResourceName(api.ResourceStorage): resource.MustParse(
|
api.ResourceName(api.ResourceStorage): resource.MustParse(
|
||||||
fmt.Sprintf("%dGi", volSizeGB),
|
fmt.Sprintf("%dGi", volSizeGiB),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
|
|
||||||
api "k8s.io/api/core/v1"
|
api "k8s.io/api/core/v1"
|
||||||
@ -425,7 +426,7 @@ func TestVolumeProvisionerWithIncompleteConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
|
func TestVolumeProvisionerWithMinimumCapacity(t *testing.T) {
|
||||||
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
@ -441,7 +442,7 @@ func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
|
|||||||
options := volume.VolumeOptions{
|
options := volume.VolumeOptions{
|
||||||
ClusterName: "testcluster",
|
ClusterName: "testcluster",
|
||||||
PVName: "pvc-sio-dynamic-vol",
|
PVName: "pvc-sio-dynamic-vol",
|
||||||
PVC: volumetest.CreateTestPVC("0Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
|
PVC: volumetest.CreateTestPVC("100Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
|
||||||
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
|
PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
|
||||||
}
|
}
|
||||||
options.PVC.Namespace = testns
|
options.PVC.Namespace = testns
|
||||||
@ -466,11 +467,25 @@ func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
sioVol.sioMgr.client = sio
|
sioVol.sioMgr.client = sio
|
||||||
|
|
||||||
_, err = provisioner.Provision(nil, nil)
|
pv, err :=
|
||||||
if err == nil {
|
provisioner.Provision(nil, nil)
|
||||||
t.Fatalf("call to Provision() should fail with invalid capacity")
|
if err != nil {
|
||||||
|
t.Fatalf("call to Provision() failed %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pvSize := pv.Spec.Capacity.Storage()
|
||||||
|
if pvSize == nil {
|
||||||
|
t.Fatalf("unexpected pv size: nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
gibSize, err := volumehelpers.RoundUpToGiB(*pvSize)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error while converting size to GiB: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gibSize != minimumVolumeSizeGiB {
|
||||||
|
t.Fatalf("expected GiB size to be %v got %v", minimumVolumeSizeGiB, gibSize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVolumeProvisionerWithSecretNamespace(t *testing.T) {
|
func TestVolumeProvisionerWithSecretNamespace(t *testing.T) {
|
||||||
|
@ -39,74 +39,88 @@ const (
|
|||||||
KiB = 1024
|
KiB = 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
// RoundUpToGB rounds up given quantity to chunks of GB
|
|
||||||
func RoundUpToGB(size resource.Quantity) int64 {
|
|
||||||
requestBytes := size.Value()
|
|
||||||
return roundUpSize(requestBytes, GB)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RoundUpToGiB rounds up given quantity upto chunks of GiB
|
// RoundUpToGiB rounds up given quantity upto chunks of GiB
|
||||||
func RoundUpToGiB(size resource.Quantity) int64 {
|
func RoundUpToGiB(size resource.Quantity) (int64, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
return roundUpSize(requestBytes, GiB)
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
|
return roundUpSize(requestBytes, GiB), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToMB rounds up given quantity to chunks of MB
|
// RoundUpToMB rounds up given quantity to chunks of MB
|
||||||
func RoundUpToMB(size resource.Quantity) int64 {
|
func RoundUpToMB(size resource.Quantity) (int64, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
return roundUpSize(requestBytes, MB)
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
|
return roundUpSize(requestBytes, MB), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToMiB rounds up given quantity upto chunks of MiB
|
// RoundUpToMiB rounds up given quantity upto chunks of MiB
|
||||||
func RoundUpToMiB(size resource.Quantity) int64 {
|
func RoundUpToMiB(size resource.Quantity) (int64, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
return roundUpSize(requestBytes, MiB)
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
|
return roundUpSize(requestBytes, MiB), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToKB rounds up given quantity to chunks of KB
|
// RoundUpToKB rounds up given quantity to chunks of KB
|
||||||
func RoundUpToKB(size resource.Quantity) int64 {
|
func RoundUpToKB(size resource.Quantity) (int64, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
return roundUpSize(requestBytes, KB)
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
|
return roundUpSize(requestBytes, KB), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToKiB rounds up given quantity upto chunks of KiB
|
// RoundUpToKiB rounds up given quantity upto chunks of KiB
|
||||||
func RoundUpToKiB(size resource.Quantity) int64 {
|
func RoundUpToKiB(size resource.Quantity) (int64, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
return roundUpSize(requestBytes, KiB)
|
if !ok {
|
||||||
}
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
// RoundUpToGBInt rounds up given quantity to chunks of GB. It returns an
|
return roundUpSize(requestBytes, KiB), nil
|
||||||
// int instead of an int64 and an error if there's overflow
|
|
||||||
func RoundUpToGBInt(size resource.Quantity) (int, error) {
|
|
||||||
requestBytes := size.Value()
|
|
||||||
return roundUpSizeInt(requestBytes, GB)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToGiBInt rounds up given quantity upto chunks of GiB. It returns an
|
// RoundUpToGiBInt rounds up given quantity upto chunks of GiB. It returns an
|
||||||
// int instead of an int64 and an error if there's overflow
|
// int instead of an int64 and an error if there's overflow
|
||||||
func RoundUpToGiBInt(size resource.Quantity) (int, error) {
|
func RoundUpToGiBInt(size resource.Quantity) (int, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
return roundUpSizeInt(requestBytes, GiB)
|
return roundUpSizeInt(requestBytes, GiB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToMBInt rounds up given quantity to chunks of MB. It returns an
|
// RoundUpToMBInt rounds up given quantity to chunks of MB. It returns an
|
||||||
// int instead of an int64 and an error if there's overflow
|
// int instead of an int64 and an error if there's overflow
|
||||||
func RoundUpToMBInt(size resource.Quantity) (int, error) {
|
func RoundUpToMBInt(size resource.Quantity) (int, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
return roundUpSizeInt(requestBytes, MB)
|
return roundUpSizeInt(requestBytes, MB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToMiBInt rounds up given quantity upto chunks of MiB. It returns an
|
// RoundUpToMiBInt rounds up given quantity upto chunks of MiB. It returns an
|
||||||
// int instead of an int64 and an error if there's overflow
|
// int instead of an int64 and an error if there's overflow
|
||||||
func RoundUpToMiBInt(size resource.Quantity) (int, error) {
|
func RoundUpToMiBInt(size resource.Quantity) (int, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
return roundUpSizeInt(requestBytes, MiB)
|
return roundUpSizeInt(requestBytes, MiB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundUpToKBInt rounds up given quantity to chunks of KB. It returns an
|
// RoundUpToKBInt rounds up given quantity to chunks of KB. It returns an
|
||||||
// int instead of an int64 and an error if there's overflow
|
// int instead of an int64 and an error if there's overflow
|
||||||
func RoundUpToKBInt(size resource.Quantity) (int, error) {
|
func RoundUpToKBInt(size resource.Quantity) (int, error) {
|
||||||
requestBytes := size.Value()
|
requestBytes, ok := size.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
return roundUpSizeInt(requestBytes, KB)
|
return roundUpSizeInt(requestBytes, KB)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +131,16 @@ func RoundUpToKiBInt(size resource.Quantity) (int, error) {
|
|||||||
return roundUpSizeInt(requestBytes, KiB)
|
return roundUpSizeInt(requestBytes, KiB)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RoundUpToGiBInt32 rounds up given quantity up to chunks of GiB. It returns an
|
||||||
|
// int32 instead of an int64 and an error if there's overflow
|
||||||
|
func RoundUpToGiBInt32(size resource.Quantity) (int32, error) {
|
||||||
|
requestBytes, ok := size.AsInt64()
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int64", size)
|
||||||
|
}
|
||||||
|
return roundUpSizeInt32(requestBytes, GiB)
|
||||||
|
}
|
||||||
|
|
||||||
// roundUpSizeInt calculates how many allocation units are needed to accommodate
|
// roundUpSizeInt calculates how many allocation units are needed to accommodate
|
||||||
// a volume of given size. It returns an int instead of an int64 and an error if
|
// a volume of given size. It returns an int instead of an int64 and an error if
|
||||||
// there's overflow
|
// there's overflow
|
||||||
@ -129,6 +153,18 @@ func roundUpSizeInt(volumeSizeBytes int64, allocationUnitBytes int64) (int, erro
|
|||||||
return roundedUpInt, nil
|
return roundedUpInt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// roundUpSizeInt32 calculates how many allocation units are needed to accommodate
|
||||||
|
// a volume of given size. It returns an int32 instead of an int64 and an error if
|
||||||
|
// there's overflow
|
||||||
|
func roundUpSizeInt32(volumeSizeBytes int64, allocationUnitBytes int64) (int32, error) {
|
||||||
|
roundedUp := roundUpSize(volumeSizeBytes, allocationUnitBytes)
|
||||||
|
roundedUpInt32 := int32(roundedUp)
|
||||||
|
if int64(roundedUpInt32) != roundedUp {
|
||||||
|
return 0, fmt.Errorf("quantity %v is too great, overflows int32", roundedUp)
|
||||||
|
}
|
||||||
|
return roundedUpInt32, nil
|
||||||
|
}
|
||||||
|
|
||||||
// roundUpSize calculates how many allocation units are needed to accommodate
|
// roundUpSize calculates how many allocation units are needed to accommodate
|
||||||
// a volume of given size. E.g. when user wants 1500MiB volume, while AWS EBS
|
// a volume of given size. E.g. when user wants 1500MiB volume, while AWS EBS
|
||||||
// allocates volumes in gibibyte-sized chunks,
|
// allocates volumes in gibibyte-sized chunks,
|
||||||
|
@ -22,61 +22,12 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_RoundUpToGB(t *testing.T) {
|
|
||||||
testcases := []struct {
|
|
||||||
name string
|
|
||||||
resource resource.Quantity
|
|
||||||
roundedVal int64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "round Ki to GB",
|
|
||||||
resource: resource.MustParse("1000Ki"),
|
|
||||||
roundedVal: int64(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "round k to GB",
|
|
||||||
resource: resource.MustParse("1000k"),
|
|
||||||
roundedVal: int64(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "round Mi to GB",
|
|
||||||
resource: resource.MustParse("1000Mi"),
|
|
||||||
roundedVal: int64(2),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "round M to GB",
|
|
||||||
resource: resource.MustParse("1000M"),
|
|
||||||
roundedVal: int64(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "round G to GB",
|
|
||||||
resource: resource.MustParse("1000G"),
|
|
||||||
roundedVal: int64(1000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "round Gi to GB",
|
|
||||||
resource: resource.MustParse("1000Gi"),
|
|
||||||
roundedVal: int64(1074),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testcases {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
val := RoundUpToGB(test.resource)
|
|
||||||
if val != test.roundedVal {
|
|
||||||
t.Logf("actual rounded value: %d", val)
|
|
||||||
t.Logf("expected rounded value: %d", test.roundedVal)
|
|
||||||
t.Error("unexpected rounded value")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_RoundUpToGiB(t *testing.T) {
|
func Test_RoundUpToGiB(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
resource resource.Quantity
|
resource resource.Quantity
|
||||||
roundedVal int64
|
roundedVal int64
|
||||||
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "round Ki to GiB",
|
name: "round Ki to GiB",
|
||||||
@ -108,11 +59,25 @@ func Test_RoundUpToGiB(t *testing.T) {
|
|||||||
resource: resource.MustParse("1000Gi"),
|
resource: resource.MustParse("1000Gi"),
|
||||||
roundedVal: int64(1000),
|
roundedVal: int64(1000),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "round overflowed quantity to int64",
|
||||||
|
resource: resource.MustParse("73786976299133170k"),
|
||||||
|
roundedVal: int64(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
val := RoundUpToGiB(test.resource)
|
val, err := RoundUpToGiB(test.resource)
|
||||||
|
if !test.expectError && err != nil {
|
||||||
|
t.Errorf("expected no error got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectError && err == nil {
|
||||||
|
t.Errorf("expected error but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
if val != test.roundedVal {
|
if val != test.roundedVal {
|
||||||
t.Logf("actual rounded value: %d", val)
|
t.Logf("actual rounded value: %d", val)
|
||||||
t.Logf("expected rounded value: %d", test.roundedVal)
|
t.Logf("expected rounded value: %d", test.roundedVal)
|
||||||
@ -124,9 +89,10 @@ func Test_RoundUpToGiB(t *testing.T) {
|
|||||||
|
|
||||||
func Test_RoundUpToMB(t *testing.T) {
|
func Test_RoundUpToMB(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
resource resource.Quantity
|
resource resource.Quantity
|
||||||
roundedVal int64
|
roundedVal int64
|
||||||
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "round Ki to MB",
|
name: "round Ki to MB",
|
||||||
@ -158,11 +124,25 @@ func Test_RoundUpToMB(t *testing.T) {
|
|||||||
resource: resource.MustParse("1000Gi"),
|
resource: resource.MustParse("1000Gi"),
|
||||||
roundedVal: int64(1073742),
|
roundedVal: int64(1073742),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "round overflowed quantity to int64",
|
||||||
|
resource: resource.MustParse("73786976299133170k"),
|
||||||
|
roundedVal: int64(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
val := RoundUpToMB(test.resource)
|
val, err := RoundUpToMB(test.resource)
|
||||||
|
if !test.expectError && err != nil {
|
||||||
|
t.Errorf("expected no error got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectError && err == nil {
|
||||||
|
t.Errorf("expected error but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
if val != test.roundedVal {
|
if val != test.roundedVal {
|
||||||
t.Logf("actual rounded value: %d", val)
|
t.Logf("actual rounded value: %d", val)
|
||||||
t.Logf("expected rounded value: %d", test.roundedVal)
|
t.Logf("expected rounded value: %d", test.roundedVal)
|
||||||
@ -174,9 +154,10 @@ func Test_RoundUpToMB(t *testing.T) {
|
|||||||
|
|
||||||
func Test_RoundUpToMiB(t *testing.T) {
|
func Test_RoundUpToMiB(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
resource resource.Quantity
|
resource resource.Quantity
|
||||||
roundedVal int64
|
roundedVal int64
|
||||||
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "round Ki to MiB",
|
name: "round Ki to MiB",
|
||||||
@ -208,11 +189,25 @@ func Test_RoundUpToMiB(t *testing.T) {
|
|||||||
resource: resource.MustParse("1000Gi"),
|
resource: resource.MustParse("1000Gi"),
|
||||||
roundedVal: int64(1024000),
|
roundedVal: int64(1024000),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "round overflowed quantity to int64",
|
||||||
|
resource: resource.MustParse("73786976299133170k"),
|
||||||
|
roundedVal: int64(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
val := RoundUpToMiB(test.resource)
|
val, err := RoundUpToMiB(test.resource)
|
||||||
|
if !test.expectError && err != nil {
|
||||||
|
t.Errorf("expected no error got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectError && err == nil {
|
||||||
|
t.Errorf("expected error but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
if val != test.roundedVal {
|
if val != test.roundedVal {
|
||||||
t.Logf("actual rounded value: %d", val)
|
t.Logf("actual rounded value: %d", val)
|
||||||
t.Logf("expected rounded value: %d", test.roundedVal)
|
t.Logf("expected rounded value: %d", test.roundedVal)
|
||||||
@ -224,9 +219,10 @@ func Test_RoundUpToMiB(t *testing.T) {
|
|||||||
|
|
||||||
func Test_RoundUpToKB(t *testing.T) {
|
func Test_RoundUpToKB(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
resource resource.Quantity
|
resource resource.Quantity
|
||||||
roundedVal int64
|
roundedVal int64
|
||||||
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "round Ki to KB",
|
name: "round Ki to KB",
|
||||||
@ -258,11 +254,25 @@ func Test_RoundUpToKB(t *testing.T) {
|
|||||||
resource: resource.MustParse("1000Gi"),
|
resource: resource.MustParse("1000Gi"),
|
||||||
roundedVal: int64(1073741824),
|
roundedVal: int64(1073741824),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "round overflowed quantity to int64",
|
||||||
|
resource: resource.MustParse("73786976299133170k"),
|
||||||
|
roundedVal: int64(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
val := RoundUpToKB(test.resource)
|
val, err := RoundUpToKB(test.resource)
|
||||||
|
if !test.expectError && err != nil {
|
||||||
|
t.Errorf("expected no error got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectError && err == nil {
|
||||||
|
t.Errorf("expected error but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
if val != test.roundedVal {
|
if val != test.roundedVal {
|
||||||
t.Logf("actual rounded value: %d", val)
|
t.Logf("actual rounded value: %d", val)
|
||||||
t.Logf("expected rounded value: %d", test.roundedVal)
|
t.Logf("expected rounded value: %d", test.roundedVal)
|
||||||
@ -274,9 +284,10 @@ func Test_RoundUpToKB(t *testing.T) {
|
|||||||
|
|
||||||
func Test_RoundUpToKiB(t *testing.T) {
|
func Test_RoundUpToKiB(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
resource resource.Quantity
|
resource resource.Quantity
|
||||||
roundedVal int64
|
roundedVal int64
|
||||||
|
expectError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "round Ki to KiB",
|
name: "round Ki to KiB",
|
||||||
@ -308,11 +319,90 @@ func Test_RoundUpToKiB(t *testing.T) {
|
|||||||
resource: resource.MustParse("1000Gi"),
|
resource: resource.MustParse("1000Gi"),
|
||||||
roundedVal: int64(1048576000),
|
roundedVal: int64(1048576000),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "round overflowed quantity to int64",
|
||||||
|
resource: resource.MustParse("73786976299133170k"),
|
||||||
|
roundedVal: int64(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
val := RoundUpToKiB(test.resource)
|
val, err := RoundUpToKiB(test.resource)
|
||||||
|
if !test.expectError && err != nil {
|
||||||
|
t.Errorf("expected no error got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectError && err == nil {
|
||||||
|
t.Errorf("expected error but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if val != test.roundedVal {
|
||||||
|
t.Logf("actual rounded value: %d", val)
|
||||||
|
t.Logf("expected rounded value: %d", test.roundedVal)
|
||||||
|
t.Error("unexpected rounded value")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RoundUpToGiBInt32(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
resource resource.Quantity
|
||||||
|
roundedVal int32
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "round Ki to GiB",
|
||||||
|
resource: resource.MustParse("1000Ki"),
|
||||||
|
roundedVal: int32(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "round k to GiB",
|
||||||
|
resource: resource.MustParse("1000k"),
|
||||||
|
roundedVal: int32(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "round Mi to GiB",
|
||||||
|
resource: resource.MustParse("1000Mi"),
|
||||||
|
roundedVal: int32(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "round M to GiB",
|
||||||
|
resource: resource.MustParse("1000M"),
|
||||||
|
roundedVal: int32(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "round G to GiB",
|
||||||
|
resource: resource.MustParse("1000G"),
|
||||||
|
roundedVal: int32(932),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "round Gi to GiB",
|
||||||
|
resource: resource.MustParse("1000Gi"),
|
||||||
|
roundedVal: int32(1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "round overflowed quantity to int32",
|
||||||
|
resource: resource.MustParse("73786976299133170k"),
|
||||||
|
roundedVal: int32(0),
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testcases {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
val, err := RoundUpToGiBInt32(test.resource)
|
||||||
|
if !test.expectError && err != nil {
|
||||||
|
t.Errorf("expected no error got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.expectError && err == nil {
|
||||||
|
t.Errorf("expected error but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
if val != test.roundedVal {
|
if val != test.roundedVal {
|
||||||
t.Logf("actual rounded value: %d", val)
|
t.Logf("actual rounded value: %d", val)
|
||||||
t.Logf("expected rounded value: %d", test.roundedVal)
|
t.Logf("expected rounded value: %d", test.roundedVal)
|
||||||
|
@ -2815,7 +2815,10 @@ func (c *Cloud) ResizeDisk(
|
|||||||
return oldSize, descErr
|
return oldSize, descErr
|
||||||
}
|
}
|
||||||
// AWS resizes in chunks of GiB (not GB)
|
// AWS resizes in chunks of GiB (not GB)
|
||||||
requestGiB := volumehelpers.RoundUpToGiB(newSize)
|
requestGiB, err := volumehelpers.RoundUpToGiB(newSize)
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
newSizeQuant := resource.MustParse(fmt.Sprintf("%dGi", requestGiB))
|
newSizeQuant := resource.MustParse(fmt.Sprintf("%dGi", requestGiB))
|
||||||
|
|
||||||
// If disk already if of greater or equal size than requested we return
|
// If disk already if of greater or equal size than requested we return
|
||||||
|
@ -284,7 +284,11 @@ func (c *ManagedDiskController) ResizeDisk(diskURI string, oldSize resource.Quan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Azure resizes in chunks of GiB (not GB)
|
// Azure resizes in chunks of GiB (not GB)
|
||||||
requestGiB := int32(volumehelpers.RoundUpToGiB(newSize))
|
requestGiB, err := volumehelpers.RoundUpToGiBInt32(newSize)
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
|
|
||||||
newSizeQuant := resource.MustParse(fmt.Sprintf("%dGi", requestGiB))
|
newSizeQuant := resource.MustParse(fmt.Sprintf("%dGi", requestGiB))
|
||||||
|
|
||||||
klog.V(2).Infof("azureDisk - begin to resize disk(%s) with new size(%d), old size(%v)", diskName, requestGiB, oldSize)
|
klog.V(2).Infof("azureDisk - begin to resize disk(%s) with new size(%d), old size(%v)", diskName, requestGiB, oldSize)
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
@ -809,7 +809,11 @@ func (g *Cloud) ResizeDisk(diskToResize string, oldSize resource.Quantity, newSi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GCE resizes in chunks of GiBs
|
// GCE resizes in chunks of GiBs
|
||||||
requestGIB := volumehelpers.RoundUpToGiB(newSize)
|
requestGIB, err := volumehelpers.RoundUpToGiB(newSize)
|
||||||
|
if err != nil {
|
||||||
|
return oldSize, err
|
||||||
|
}
|
||||||
|
|
||||||
newSizeQuant := resource.MustParse(fmt.Sprintf("%dGi", requestGIB))
|
newSizeQuant := resource.MustParse(fmt.Sprintf("%dGi", requestGIB))
|
||||||
|
|
||||||
// If disk is already of size equal or greater than requested size, we simply return
|
// If disk is already of size equal or greater than requested size, we simply return
|
||||||
|
Loading…
Reference in New Issue
Block a user