mirror of https://github.com/k3s-io/k3s
Add support to resize Portworx volume
Closes #62305 Signed-off-by: Harsh Desai <harsh@portworx.com> update comment and variable references to GiB Signed-off-by: Harsh Desai <harsh@portworx.com> explicitly check volume size after resize and fix size volume spec Signed-off-by: Harsh Desai <harsh@portworx.com> If Portworx volume is already greater than new size, skip resize Signed-off-by: Harsh Desai <harsh@portworx.com> Allow updated volume to be greater than requested size Signed-off-by: Harsh Desai <harsh@portworx.com>pull/8/head
parent
4761788b2a
commit
adc71854e2
|
@ -15,6 +15,7 @@ go_test(
|
|||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/testing:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/testing:go_default_library",
|
||||
],
|
||||
|
|
|
@ -50,6 +50,7 @@ var _ volume.VolumePlugin = &portworxVolumePlugin{}
|
|||
var _ volume.PersistentVolumePlugin = &portworxVolumePlugin{}
|
||||
var _ volume.DeletableVolumePlugin = &portworxVolumePlugin{}
|
||||
var _ volume.ProvisionableVolumePlugin = &portworxVolumePlugin{}
|
||||
var _ volume.ExpandableVolumePlugin = &portworxVolumePlugin{}
|
||||
|
||||
const (
|
||||
portworxVolumePluginName = "kubernetes.io/portworx-volume"
|
||||
|
@ -171,6 +172,24 @@ func (plugin *portworxVolumePlugin) newProvisionerInternal(options volume.Volume
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *portworxVolumePlugin) RequiresFSResize() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *portworxVolumePlugin) ExpandVolumeDevice(
|
||||
spec *volume.Spec,
|
||||
newSize resource.Quantity,
|
||||
oldSize resource.Quantity) (resource.Quantity, error) {
|
||||
glog.V(4).Infof("Expanding: %s from %v to %v", spec.Name(), oldSize, newSize)
|
||||
err := plugin.util.ResizeVolume(spec, newSize, plugin.host)
|
||||
if err != nil {
|
||||
return oldSize, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Successfully resized %s to %v", spec.Name(), newSize)
|
||||
return newSize, nil
|
||||
}
|
||||
|
||||
func (plugin *portworxVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
portworxVolume := &v1.Volume{
|
||||
Name: volumeName,
|
||||
|
@ -206,7 +225,7 @@ func getVolumeSource(
|
|||
// Abstract interface to PD operations.
|
||||
type portworxManager interface {
|
||||
// Creates a volume
|
||||
CreateVolume(provisioner *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error)
|
||||
CreateVolume(provisioner *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int64, labels map[string]string, err error)
|
||||
// Deletes a volume
|
||||
DeleteVolume(deleter *portworxVolumeDeleter) error
|
||||
// Attach a volume
|
||||
|
@ -217,6 +236,8 @@ type portworxManager interface {
|
|||
MountVolume(mounter *portworxVolumeMounter, mountDir string) error
|
||||
// Unmount a volume
|
||||
UnmountVolume(unmounter *portworxVolumeUnmounter, mountDir string) error
|
||||
// Resize a volume
|
||||
ResizeVolume(spec *volume.Spec, newSize resource.Quantity, host volume.VolumeHost) error
|
||||
}
|
||||
|
||||
// portworxVolume volumes are portworx block devices
|
||||
|
@ -362,7 +383,7 @@ func (c *portworxVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||
return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes())
|
||||
}
|
||||
|
||||
volumeID, sizeGB, labels, err := c.manager.CreateVolume(c)
|
||||
volumeID, sizeGiB, labels, err := c.manager.CreateVolume(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -379,7 +400,7 @@ func (c *portworxVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||
PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy,
|
||||
AccessModes: c.options.PVC.Spec.AccessModes,
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
|
||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGiB)),
|
||||
},
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
PortworxVolume: &v1.PortworxVolumeSource{
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
@ -106,7 +107,7 @@ func (fake *fakePortworxManager) UnmountVolume(c *portworxVolumeUnmounter, mount
|
|||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakePortworxManager) CreateVolume(c *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error) {
|
||||
func (fake *fakePortworxManager) CreateVolume(c *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int64, labels map[string]string, err error) {
|
||||
labels = make(map[string]string)
|
||||
labels["fakeportworxmanager"] = "yes"
|
||||
return PortworxTestVolume, 100, labels, nil
|
||||
|
@ -119,6 +120,10 @@ func (fake *fakePortworxManager) DeleteVolume(cd *portworxVolumeDeleter) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakePortworxManager) ResizeVolume(spec *volume.Spec, newSize resource.Quantity, volumeHost volume.VolumeHost) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("portworxVolumeTest")
|
||||
if err != nil {
|
||||
|
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package portworx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
osdapi "github.com/libopenstorage/openstorage/api"
|
||||
osdclient "github.com/libopenstorage/openstorage/api/client"
|
||||
|
@ -24,6 +26,7 @@ import (
|
|||
osdspec "github.com/libopenstorage/openstorage/api/spec"
|
||||
volumeapi "github.com/libopenstorage/openstorage/volume"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
@ -45,7 +48,7 @@ type PortworxVolumeUtil struct {
|
|||
}
|
||||
|
||||
// CreateVolume creates a Portworx volume.
|
||||
func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (string, int, map[string]string, error) {
|
||||
func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (string, int64, map[string]string, error) {
|
||||
driver, err := util.getPortworxDriver(p.plugin.host, false /*localOnly*/)
|
||||
if err != nil || driver == nil {
|
||||
glog.Errorf("Failed to get portworx driver. Err: %v", err)
|
||||
|
@ -55,8 +58,8 @@ func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri
|
|||
glog.Infof("Creating Portworx volume for PVC: %v", p.options.PVC.Name)
|
||||
|
||||
capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||
// Portworx Volumes are specified in GB
|
||||
requestGB := int(volutil.RoundUpSize(capacity.Value(), 1024*1024*1024))
|
||||
// Portworx Volumes are specified in GiB
|
||||
requestGiB := volutil.RoundUpToGiB(capacity)
|
||||
|
||||
// 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
|
||||
|
@ -71,7 +74,8 @@ func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri
|
|||
// Pass all parameters as volume labels for Portworx server-side processing.
|
||||
spec.VolumeLabels = p.options.Parameters
|
||||
// Update the requested size in the spec
|
||||
spec.Size = uint64(requestGB * 1024 * 1024 * 1024)
|
||||
spec.Size = uint64(requestGiB * volutil.GIB)
|
||||
|
||||
// Change the Portworx Volume name to PV name
|
||||
if locator == nil {
|
||||
locator = &osdapi.VolumeLocator{
|
||||
|
@ -99,7 +103,7 @@ func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri
|
|||
}
|
||||
|
||||
glog.Infof("Successfully created Portworx volume for PVC: %v", p.options.PVC.Name)
|
||||
return volumeID, requestGB, nil, err
|
||||
return volumeID, requestGiB, nil, err
|
||||
}
|
||||
|
||||
// DeleteVolume deletes a Portworx volume
|
||||
|
@ -182,6 +186,55 @@ func (util *PortworxVolumeUtil) UnmountVolume(u *portworxVolumeUnmounter, mountP
|
|||
return nil
|
||||
}
|
||||
|
||||
func (util *PortworxVolumeUtil) ResizeVolume(spec *volume.Spec, newSize resource.Quantity, volumeHost volume.VolumeHost) error {
|
||||
driver, err := util.getPortworxDriver(volumeHost, false /*localOnly*/)
|
||||
if err != nil || driver == nil {
|
||||
glog.Errorf("Failed to get portworx driver. Err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
vols, err := driver.Inspect([]string{spec.Name()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(vols) != 1 {
|
||||
return fmt.Errorf("failed to inspect Portworx volume: %s. Found: %d volumes", spec.Name(), len(vols))
|
||||
}
|
||||
|
||||
vol := vols[0]
|
||||
newSizeInBytes := uint64(volutil.RoundUpToGiB(newSize) * volutil.GIB)
|
||||
if vol.Spec.Size >= newSizeInBytes {
|
||||
glog.Infof("Portworx volume: %s already at size: %d greater than or equal to new "+
|
||||
"requested size: %d. Skipping resize.", vol.Spec.Size, newSizeInBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
vol.Spec.Size = newSizeInBytes
|
||||
err = driver.Set(spec.Name(), vol.Locator, vol.Spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if the volume's size actually got updated
|
||||
vols, err = driver.Inspect([]string{spec.Name()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(vols) != 1 {
|
||||
return fmt.Errorf("failed to inspect resized Portworx volume: %s. Found: %d volumes", spec.Name(), len(vols))
|
||||
}
|
||||
|
||||
updatedVol := vols[0]
|
||||
if updatedVol.Spec.Size < vol.Spec.Size {
|
||||
return fmt.Errorf("Portworx volume: %s doesn't match expected size after resize. expected:%v actual:%v",
|
||||
spec.Name(), vol.Spec.Size, updatedVol.Spec.Size)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isClientValid(client *osdclient.Client) (bool, error) {
|
||||
if client == nil {
|
||||
return false, nil
|
||||
|
|
|
@ -149,7 +149,7 @@ func (pvcr *persistentVolumeClaimResize) allowResize(pvc, oldPvc *api.Persistent
|
|||
|
||||
// checkVolumePlugin checks whether the volume plugin supports resize
|
||||
func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVolume) bool {
|
||||
if pv.Spec.Glusterfs != nil || pv.Spec.Cinder != nil || pv.Spec.RBD != nil {
|
||||
if pv.Spec.Glusterfs != nil || pv.Spec.Cinder != nil || pv.Spec.RBD != nil || pv.Spec.PortworxVolume != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue