Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #67851 from aniket-s-kulkarni/flexvolume-resize-im…
Browse files Browse the repository at this point in the history
…plementation

Flexvolume resize implementation
  • Loading branch information
k8s-ci-robot committed Nov 2, 2018
2 parents 8793122 + 82f83a8 commit 6813ebb
Show file tree
Hide file tree
Showing 25 changed files with 553 additions and 37 deletions.
7 changes: 5 additions & 2 deletions pkg/controller/volume/expand/expand_controller.go
Expand Up @@ -208,13 +208,16 @@ func (expc *expandController) pvcUpdate(oldObj, newObj interface{}) {
return
}

// Filter PVCs for which the corresponding volume plugins don't allow expansion.
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
volumePlugin, err := expc.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
if err != nil || volumePlugin == nil {
err = fmt.Errorf("didn't find a plugin capable of expanding the volume; " +
"waiting for an external controller to process this PVC")
expc.recorder.Event(newPVC, v1.EventTypeNormal, events.ExternalExpanding,
eventType := v1.EventTypeNormal
if err != nil {
eventType = v1.EventTypeWarning
}
expc.recorder.Event(newPVC, eventType, events.ExternalExpanding,
fmt.Sprintf("Ignoring the PVC: %v.", err))
glog.V(3).Infof("Ignoring the PVC %q (uid: %q) : %v.",
util.GetPersistentVolumeClaimQualifiedName(newPVC), newPVC.UID, err)
Expand Down
6 changes: 5 additions & 1 deletion pkg/controller/volume/expand/pvc_populator.go
Expand Up @@ -104,7 +104,11 @@ func (populator *pvcPopulator) Sync() {
if (err != nil || volumePlugin == nil) && pvcStatusSize.Cmp(pvcSize) < 0 {
err = fmt.Errorf("didn't find a plugin capable of expanding the volume; " +
"waiting for an external controller to process this PVC")
populator.recorder.Event(pvc, v1.EventTypeNormal, events.ExternalExpanding,
eventType := v1.EventTypeNormal
if err != nil {
eventType = v1.EventTypeWarning
}
populator.recorder.Event(pvc, eventType, events.ExternalExpanding,
fmt.Sprintf("Ignoring the PVC: %v.", err))
glog.V(3).Infof("Ignoring the PVC %q (uid: %q) : %v.",
util.GetPersistentVolumeClaimQualifiedName(pvc), pvc.UID, err)
Expand Down
1 change: 0 additions & 1 deletion pkg/controller/volume/expand/sync_volume_resize.go
Expand Up @@ -77,7 +77,6 @@ func (rc *syncResize) Sync() {
glog.V(10).Infof("Operation for PVC %v is already pending", pvcWithResizeRequest.QualifiedName())
continue
}
glog.V(5).Infof("Starting opsExecutor.ExpandVolume for volume %s", pvcWithResizeRequest.QualifiedName())
growFuncError := rc.opsExecutor.ExpandVolume(pvcWithResizeRequest, rc.resizeMap)
if growFuncError != nil && !exponentialbackoff.IsExponentialBackoff(growFuncError) {
glog.Errorf("Error growing pvc %s with %v", pvcWithResizeRequest.QualifiedName(), growFuncError)
Expand Down
1 change: 1 addition & 0 deletions pkg/volume/BUILD
Expand Up @@ -9,6 +9,7 @@ go_library(
"metrics_errors.go",
"metrics_nil.go",
"metrics_statfs.go",
"noop_expandable_plugin.go",
"plugins.go",
"volume.go",
"volume_linux.go",
Expand Down
6 changes: 6 additions & 0 deletions pkg/volume/awsebs/aws_ebs.go
Expand Up @@ -312,6 +312,12 @@ func (plugin *awsElasticBlockStorePlugin) ExpandVolumeDevice(
return awsVolume.ResizeDisk(volumeID, oldSize, newSize)
}

func (plugin *awsElasticBlockStorePlugin) ExpandFS(spec *volume.Spec, devicePath, deviceMountPath string, _, _ resource.Quantity) error {
_, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), devicePath, deviceMountPath)
return err
}

var _ volume.FSResizableVolumePlugin = &awsElasticBlockStorePlugin{}
var _ volume.ExpandableVolumePlugin = &awsElasticBlockStorePlugin{}
var _ volume.VolumePluginWithAttachLimits = &awsElasticBlockStorePlugin{}

Expand Down
7 changes: 7 additions & 0 deletions pkg/volume/azure_dd/azure_dd.go
Expand Up @@ -301,6 +301,13 @@ func (plugin *azureDataDiskPlugin) ExpandVolumeDevice(
return diskController.ResizeDisk(spec.PersistentVolume.Spec.AzureDisk.DataDiskURI, oldSize, newSize)
}

func (plugin *azureDataDiskPlugin) ExpandFS(spec *volume.Spec, devicePath, deviceMountPath string, _, _ resource.Quantity) error {
_, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), devicePath, deviceMountPath)
return err
}

var _ volume.FSResizableVolumePlugin = &azureDataDiskPlugin{}

func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
Expand Down
7 changes: 7 additions & 0 deletions pkg/volume/cinder/cinder.go
Expand Up @@ -267,6 +267,13 @@ func (plugin *cinderPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resour
return expandedSize, nil
}

func (plugin *cinderPlugin) ExpandFS(spec *volume.Spec, devicePath, deviceMountPath string, _, _ resource.Quantity) error {
_, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), devicePath, deviceMountPath)
return err
}

var _ volume.FSResizableVolumePlugin = &cinderPlugin{}

func (plugin *cinderPlugin) RequiresFSResize() bool {
return true
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/volume/flexvolume/BUILD
Expand Up @@ -14,6 +14,8 @@ go_library(
"detacher.go",
"detacher-defaults.go",
"driver-call.go",
"expander.go",
"expander-defaults.go",
"fake_watcher.go",
"mounter.go",
"mounter-defaults.go",
Expand All @@ -33,6 +35,7 @@ go_library(
"//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions pkg/volume/flexvolume/common_test.go
Expand Up @@ -76,11 +76,11 @@ func fakeResultOutput(result interface{}) fakeexec.FakeCombinedOutputAction {
}

func successOutput() fakeexec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true, nil})
return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true, nil, 0})
}

func notSupportedOutput() fakeexec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false, nil})
return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false, nil, 0})
}

func sameArgs(args, expectedArgs []string) bool {
Expand Down
23 changes: 15 additions & 8 deletions pkg/volume/flexvolume/driver-call.go
Expand Up @@ -44,6 +44,9 @@ const (
mountCmd = "mount"
unmountCmd = "unmount"

expandVolumeCmd = "expandvolume"
expandFSCmd = "expandfs"

// Option keys
optionFSType = "kubernetes.io/fsType"
optionReadWrite = "kubernetes.io/readwrite"
Expand Down Expand Up @@ -221,22 +224,26 @@ type DriverStatus struct {
// By default we assume all the capabilities are supported.
// If the plugin does not support a capability, it can return false for that capability.
Capabilities *DriverCapabilities `json:",omitempty"`
// Returns the actual size of the volume after resizing is done, the size is in bytes.
ActualVolumeSize int64 `json:"volumeNewSize,omitempty"`
}

// DriverCapabilities represents what driver can do
type DriverCapabilities struct {
Attach bool `json:"attach"`
SELinuxRelabel bool `json:"selinuxRelabel"`
SupportsMetrics bool `json:"supportsMetrics"`
FSGroup bool `json:"fsGroup"`
Attach bool `json:"attach"`
SELinuxRelabel bool `json:"selinuxRelabel"`
SupportsMetrics bool `json:"supportsMetrics"`
FSGroup bool `json:"fsGroup"`
RequiresFSResize bool `json:"requiresFSResize"`
}

func defaultCapabilities() *DriverCapabilities {
return &DriverCapabilities{
Attach: true,
SELinuxRelabel: true,
SupportsMetrics: false,
FSGroup: true,
Attach: true,
SELinuxRelabel: true,
SupportsMetrics: false,
FSGroup: true,
RequiresFSResize: true,
}
}

Expand Down
45 changes: 45 additions & 0 deletions pkg/volume/flexvolume/expander-defaults.go
@@ -0,0 +1,45 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package flexvolume

import (
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util"
)

type expanderDefaults struct {
plugin *flexVolumePlugin
}

func newExpanderDefaults(plugin *flexVolumePlugin) *expanderDefaults {
return &expanderDefaults{plugin}
}

func (e *expanderDefaults) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
glog.Warning(logPrefix(e.plugin), "using default expand for volume ", spec.Name(), ", to size ", newSize, " from ", oldSize)
return newSize, nil
}

// the defaults for ExpandFS return a generic resize indicator that will trigger the operation executor to go ahead with
// generic filesystem resize
func (e *expanderDefaults) ExpandFS(spec *volume.Spec, devicePath, deviceMountPath string, _, _ resource.Quantity) error {
glog.Warning(logPrefix(e.plugin), "using default filesystem resize for volume ", spec.Name(), ", at ", devicePath)
_, err := util.GenericResizeFS(e.plugin.host, e.plugin.GetPluginName(), devicePath, deviceMountPath)
return err
}
67 changes: 67 additions & 0 deletions pkg/volume/flexvolume/expander.go
@@ -0,0 +1,67 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package flexvolume

import (
"fmt"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/kubernetes/pkg/volume"
"strconv"
)

func (plugin *flexVolumePlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
call := plugin.NewDriverCall(expandVolumeCmd)
call.AppendSpec(spec, plugin.host, nil)

devicePath, err := plugin.getDeviceMountPath(spec)
if err != nil {
return newSize, err
}
call.Append(devicePath)
call.Append(strconv.FormatInt(newSize.Value(), 10))
call.Append(strconv.FormatInt(oldSize.Value(), 10))

_, err = call.Run()
if isCmdNotSupportedErr(err) {
return newExpanderDefaults(plugin).ExpandVolumeDevice(spec, newSize, oldSize)
}
return newSize, err
}

func (plugin *flexVolumePlugin) ExpandFS(spec *volume.Spec, devicePath, deviceMountPath string, newSize, oldSize resource.Quantity) error {
// This method is called after we spec.PersistentVolume.Spec.Capacity
// has been updated to the new size. The underlying driver thus sees
// the _new_ (requested) size and can find out the _current_ size from
// its underlying storage implementation

if spec.PersistentVolume == nil {
return fmt.Errorf("PersistentVolume not found for spec: %s", spec.Name())
}

call := plugin.NewDriverCall(expandFSCmd)
call.AppendSpec(spec, plugin.host, nil)
call.Append(devicePath)
call.Append(deviceMountPath)
call.Append(strconv.FormatInt(newSize.Value(), 10))
call.Append(strconv.FormatInt(oldSize.Value(), 10))

_, err := call.Run()
if isCmdNotSupportedErr(err) {
return newExpanderDefaults(plugin).ExpandFS(spec, devicePath, deviceMountPath, newSize, oldSize)
}
return err
}
6 changes: 6 additions & 0 deletions pkg/volume/flexvolume/plugin.go
Expand Up @@ -57,6 +57,8 @@ type flexVolumeAttachablePlugin struct {

var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{}
var _ volume.PersistentVolumePlugin = &flexVolumePlugin{}
var _ volume.FSResizableVolumePlugin = &flexVolumePlugin{}
var _ volume.ExpandableVolumePlugin = &flexVolumePlugin{}

var _ volume.DeviceMountableVolumePlugin = &flexVolumeAttachablePlugin{}

Expand Down Expand Up @@ -305,3 +307,7 @@ func (plugin *flexVolumePlugin) getDeviceMountPath(spec *volume.Spec) (string, e
mountsDir := path.Join(plugin.host.GetPluginDir(flexVolumePluginName), plugin.driverName, "mounts")
return path.Join(mountsDir, volumeName), nil
}

func (plugin *flexVolumePlugin) RequiresFSResize() bool {
return plugin.capabilities.RequiresFSResize
}
7 changes: 7 additions & 0 deletions pkg/volume/gcepd/gce_pd.go
Expand Up @@ -284,6 +284,13 @@ func (plugin *gcePersistentDiskPlugin) ExpandVolumeDevice(
return updatedQuantity, nil
}

func (plugin *gcePersistentDiskPlugin) ExpandFS(spec *volume.Spec, devicePath, deviceMountPath string, _, _ resource.Quantity) error {
_, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), devicePath, deviceMountPath)
return err
}

var _ volume.FSResizableVolumePlugin = &gcePersistentDiskPlugin{}

func (plugin *gcePersistentDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
mounter := plugin.host.GetMounter(plugin.GetPluginName())
pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
Expand Down
77 changes: 77 additions & 0 deletions pkg/volume/noop_expandable_plugin.go
@@ -0,0 +1,77 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package volume

import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types"
)

type noopExpandableVolumePluginInstance struct {
spec *Spec
}

var _ ExpandableVolumePlugin = &noopExpandableVolumePluginInstance{}

func (n *noopExpandableVolumePluginInstance) ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
return newSize, nil
}

func (n *noopExpandableVolumePluginInstance) Init(host VolumeHost) error {
return nil
}

func (n *noopExpandableVolumePluginInstance) GetPluginName() string {
return n.spec.KubeletExpandablePluginName()
}

func (n *noopExpandableVolumePluginInstance) GetVolumeName(spec *Spec) (string, error) {
return n.spec.Name(), nil
}

func (n *noopExpandableVolumePluginInstance) CanSupport(spec *Spec) bool {
return true
}

func (n *noopExpandableVolumePluginInstance) RequiresRemount() bool {
return false
}

func (n *noopExpandableVolumePluginInstance) NewMounter(spec *Spec, podRef *v1.Pod, opts VolumeOptions) (Mounter, error) {
return nil, nil
}

func (n *noopExpandableVolumePluginInstance) NewUnmounter(name string, podUID types.UID) (Unmounter, error) {
return nil, nil
}

func (n *noopExpandableVolumePluginInstance) ConstructVolumeSpec(volumeName, mountPath string) (*Spec, error) {
return n.spec, nil
}

func (n *noopExpandableVolumePluginInstance) SupportsMountOption() bool {
return true
}

func (n *noopExpandableVolumePluginInstance) SupportsBulkVolumeVerification() bool {
return false
}

func (n *noopExpandableVolumePluginInstance) RequiresFSResize() bool {
return true
}

0 comments on commit 6813ebb

Please sign in to comment.