From 7be94c4b0646ab5a76f0dd0686f37e3da1f65e59 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Mon, 20 Nov 2017 13:02:13 -0500 Subject: [PATCH] Implement resizing support for GCE Fix GCE attacher test Update bazel files --- pkg/cloudprovider/providers/gce/BUILD | 1 + pkg/cloudprovider/providers/gce/gce_disks.go | 86 +++++++++++++++++++ .../providers/gce/gce_disks_test.go | 13 +++ pkg/volume/gce_pd/BUILD | 1 + pkg/volume/gce_pd/attacher_test.go | 8 ++ pkg/volume/gce_pd/gce_pd.go | 23 +++++ .../operationexecutor/operation_generator.go | 9 +- .../persistentvolume/resize/admission.go | 4 + 8 files changed, 141 insertions(+), 4 deletions(-) diff --git a/pkg/cloudprovider/providers/gce/BUILD b/pkg/cloudprovider/providers/gce/BUILD index bcd8c5de9dc..9636a85c9e3 100644 --- a/pkg/cloudprovider/providers/gce/BUILD +++ b/pkg/cloudprovider/providers/gce/BUILD @@ -67,6 +67,7 @@ go_library( "//vendor/google.golang.org/api/googleapi:go_default_library", "//vendor/gopkg.in/gcfg.v1: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/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", diff --git a/pkg/cloudprovider/providers/gce/gce_disks.go b/pkg/cloudprovider/providers/gce/gce_disks.go index 1e6615062be..6918c57c0df 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks.go +++ b/pkg/cloudprovider/providers/gce/gce_disks.go @@ -24,6 +24,7 @@ import ( "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/kubernetes/pkg/cloudprovider" @@ -90,6 +91,9 @@ type diskServiceManager interface { instanceName string, devicePath string) (gceObject, error) + ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) (gceObject, error) + RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) (gceObject, error) + // Gets the persistent disk from GCE with the given diskName. GetDiskFromCloudProvider(zone string, diskName string) (*GCEDisk, error) @@ -264,6 +268,7 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider( Name: diskAlpha.Name, Kind: diskAlpha.Kind, Type: diskAlpha.Type, + SizeGb: diskAlpha.SizeGb, }, nil } @@ -289,6 +294,7 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider( Name: diskStable.Name, Kind: diskStable.Kind, Type: diskStable.Type, + SizeGb: diskStable.SizeGb, }, nil } @@ -313,6 +319,7 @@ func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider( Name: diskAlpha.Name, Kind: diskAlpha.Kind, Type: diskAlpha.Type, + SizeGb: diskAlpha.SizeGb, }, nil } @@ -469,6 +476,30 @@ func (manager *gceServiceManager) getRegionFromZone(zoneInfo zoneType) (string, return region, nil } +func (manager *gceServiceManager) ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) (gceObject, error) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { + resizeServiceRequest := &computealpha.DisksResizeRequest{ + SizeGb: sizeGb, + } + return manager.gce.serviceAlpha.Disks.Resize(manager.gce.projectID, zone, disk.Name, resizeServiceRequest).Do() + + } + resizeServiceRequest := &compute.DisksResizeRequest{ + SizeGb: sizeGb, + } + return manager.gce.service.Disks.Resize(manager.gce.projectID, zone, disk.Name, resizeServiceRequest).Do() +} + +func (manager *gceServiceManager) RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) (gceObject, error) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { + resizeServiceRequest := &computealpha.RegionDisksResizeRequest{ + SizeGb: sizeGb, + } + return manager.gce.serviceAlpha.RegionDisks.Resize(manager.gce.projectID, disk.Region, disk.Name, resizeServiceRequest).Do() + } + return nil, fmt.Errorf("RegionalResizeDiskOnCloudProvider is a regional PD feature and is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") +} + // Disks is interface for manipulation with GCE PDs. type Disks interface { // AttachDisk attaches given disk to the node with the specified NodeName. @@ -498,6 +529,9 @@ type Disks interface { // DeleteDisk deletes PD. DeleteDisk(diskToDelete string) error + // ResizeDisk resizes PD and returns new disk size + ResizeDisk(diskToResize string, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error) + // GetAutoLabelsForPD returns labels to apply to PersistentVolume // representing this PD, namely failure domain and zone. // zone can be provided to specify the zone for the PD, @@ -517,6 +551,7 @@ type GCEDisk struct { Name string Kind string Type string + SizeGb int64 } type zoneType interface { @@ -801,6 +836,57 @@ func (gce *GCECloud) DeleteDisk(diskToDelete string) error { return err } +// ResizeDisk expands given disk and returns new disk size +func (gce *GCECloud) ResizeDisk(diskToResize string, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error) { + disk, err := gce.GetDiskByNameUnknownZone(diskToResize) + if err != nil { + return oldSize, err + } + + requestBytes := newSize.Value() + // GCE resizes in chunks of GBs (not GiB) + requestGB := volume.RoundUpSize(requestBytes, 1000*1000*1000) + newSizeQuant := resource.MustParse(fmt.Sprintf("%dG", requestGB)) + + // If disk is already of size equal or greater than requested size, we simply return + if disk.SizeGb >= requestGB { + return newSizeQuant, nil + } + + var mc *metricContext + + switch zoneInfo := disk.ZoneInfo.(type) { + case singleZone: + mc = newDiskMetricContextZonal("resize", disk.Region, zoneInfo.zone) + resizeOp, err := gce.manager.ResizeDiskOnCloudProvider(disk, requestGB, zoneInfo.zone) + + if err != nil { + return oldSize, mc.Observe(err) + } + waitErr := gce.manager.WaitForZoneOp(resizeOp, zoneInfo.zone, mc) + if waitErr != nil { + return oldSize, waitErr + } + return newSizeQuant, nil + case multiZone: + mc = newDiskMetricContextRegional("resize", disk.Region) + resizeOp, err := gce.manager.RegionalResizeDiskOnCloudProvider(disk, requestGB) + + if err != nil { + return oldSize, mc.Observe(err) + } + waitErr := gce.manager.WaitForRegionalOp(resizeOp, mc) + if waitErr != nil { + return oldSize, waitErr + } + return newSizeQuant, nil + case nil: + return oldSize, fmt.Errorf("PD has nil ZoneInfo: %v", disk) + default: + return oldSize, fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo) + } +} + // Builds the labels that should be automatically added to a PersistentVolume backed by a GCE PD // Specifically, this builds FailureDomain (zone) and Region labels. // The PersistentVolumeLabel admission controller calls this and adds the labels when a PV is created. diff --git a/pkg/cloudprovider/providers/gce/gce_disks_test.go b/pkg/cloudprovider/providers/gce/gce_disks_test.go index 3c6b5ee4f70..34418d737a0 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks_test.go +++ b/pkg/cloudprovider/providers/gce/gce_disks_test.go @@ -897,6 +897,19 @@ func (manager *FakeServiceManager) GetRegionalDiskFromCloudProvider( }, nil } +func (manager *FakeServiceManager) ResizeDiskOnCloudProvider( + disk *GCEDisk, + size int64, + zone string) (gceObject, error) { + panic("Not implmented") +} + +func (manager *FakeServiceManager) RegionalResizeDiskOnCloudProvider( + disk *GCEDisk, + size int64) (gceObject, error) { + panic("Not implemented") +} + /** * Disk info is removed from the FakeServiceManager. */ diff --git a/pkg/volume/gce_pd/BUILD b/pkg/volume/gce_pd/BUILD index 45b973c02a1..8c83b4d0d70 100644 --- a/pkg/volume/gce_pd/BUILD +++ b/pkg/volume/gce_pd/BUILD @@ -47,6 +47,7 @@ go_test( "//pkg/volume/testing:go_default_library", "//vendor/github.com/golang/glog: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/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", diff --git a/pkg/volume/gce_pd/attacher_test.go b/pkg/volume/gce_pd/attacher_test.go index 3d8fe615a21..0539e11a572 100644 --- a/pkg/volume/gce_pd/attacher_test.go +++ b/pkg/volume/gce_pd/attacher_test.go @@ -22,6 +22,7 @@ import ( "testing" "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/kubernetes/pkg/volume" volumetest "k8s.io/kubernetes/pkg/volume/testing" @@ -373,3 +374,10 @@ func (testcase *testcase) DeleteDisk(diskToDelete string) error { func (testcase *testcase) GetAutoLabelsForPD(name string, zone string) (map[string]string, error) { return map[string]string{}, errors.New("Not implemented") } + +func (testcase *testcase) ResizeDisk( + diskName string, + oldSize resource.Quantity, + newSize resource.Quantity) (resource.Quantity, error) { + return oldSize, errors.New("Not implemented") +} diff --git a/pkg/volume/gce_pd/gce_pd.go b/pkg/volume/gce_pd/gce_pd.go index 9ddeb95787e..5576f1fabea 100644 --- a/pkg/volume/gce_pd/gce_pd.go +++ b/pkg/volume/gce_pd/gce_pd.go @@ -47,6 +47,7 @@ var _ volume.VolumePlugin = &gcePersistentDiskPlugin{} var _ volume.PersistentVolumePlugin = &gcePersistentDiskPlugin{} var _ volume.DeletableVolumePlugin = &gcePersistentDiskPlugin{} var _ volume.ProvisionableVolumePlugin = &gcePersistentDiskPlugin{} +var _ volume.ExpandableVolumePlugin = &gcePersistentDiskPlugin{} const ( gcePersistentDiskPluginName = "kubernetes.io/gce-pd" @@ -190,6 +191,28 @@ func (plugin *gcePersistentDiskPlugin) newProvisionerInternal(options volume.Vol }, nil } +func (plugin *gcePersistentDiskPlugin) RequiresFSResize() bool { + return true +} + +func (plugin *gcePersistentDiskPlugin) ExpandVolumeDevice( + spec *volume.Spec, + newSize resource.Quantity, + oldSize resource.Quantity) (resource.Quantity, error) { + cloud, err := getCloudProvider(plugin.host.GetCloudProvider()) + + if err != nil { + return oldSize, err + } + pdName := spec.PersistentVolume.Spec.GCEPersistentDisk.PDName + updatedQuantity, err := cloud.ResizeDisk(pdName, oldSize, newSize) + + if err != nil { + return oldSize, err + } + return updatedQuantity, nil +} + func (plugin *gcePersistentDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) { mounter := plugin.host.GetMounter(plugin.GetPluginName()) pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName()) diff --git a/pkg/volume/util/operationexecutor/operation_generator.go b/pkg/volume/util/operationexecutor/operation_generator.go index c99060ceafe..fc682fd607d 100644 --- a/pkg/volume/util/operationexecutor/operation_generator.go +++ b/pkg/volume/util/operationexecutor/operation_generator.go @@ -454,8 +454,9 @@ func (og *operationGenerator) GenerateMountVolumeFunc( glog.Infof(volumeToMount.GenerateMsgDetailed("MountVolume.WaitForAttach succeeded", fmt.Sprintf("DevicePath %q", devicePath))) - mounter := og.volumePluginMgr.Host.GetMounter(volumePlugin.GetPluginName()) - resizeError := og.resizeFileSystem(volumeToMount, devicePath, mounter) + // resizeFileSystem will resize the file system if user has requested a resize of + // underlying persistent volume and is allowed to do so. + resizeError := og.resizeFileSystem(volumeToMount, devicePath, volumePlugin.GetPluginName()) if resizeError != nil { return volumeToMount.GenerateErrorDetailed("MountVolume.Resize failed", resizeError) @@ -538,12 +539,12 @@ func (og *operationGenerator) GenerateMountVolumeFunc( }, volumePlugin.GetPluginName(), nil } -func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, devicePath string, mounter mount.Interface) error { +func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, devicePath string, pluginName string) error { if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) { glog.V(6).Infof("Resizing is not enabled for this volume %s", volumeToMount.VolumeName) return nil } - + mounter := og.volumePluginMgr.Host.GetMounter(pluginName) // Get expander, if possible expandableVolumePlugin, _ := og.volumePluginMgr.FindExpandablePluginBySpec(volumeToMount.VolumeSpec) diff --git a/plugin/pkg/admission/persistentvolume/resize/admission.go b/plugin/pkg/admission/persistentvolume/resize/admission.go index f5327ecf636..b550edb5ff4 100644 --- a/plugin/pkg/admission/persistentvolume/resize/admission.go +++ b/plugin/pkg/admission/persistentvolume/resize/admission.go @@ -155,6 +155,10 @@ func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVol if pv.Spec.Cinder != nil { return true } + + if pv.Spec.GCEPersistentDisk != nil { + return true + } return false }