diff --git a/cmd/kube-controller-manager/app/plugins.go b/cmd/kube-controller-manager/app/plugins.go index 170c366c90a..a421377f122 100644 --- a/cmd/kube-controller-manager/app/plugins.go +++ b/cmd/kube-controller-manager/app/plugins.go @@ -102,6 +102,7 @@ func ProbeExpandableVolumePlugins(config componentconfig.VolumeConfiguration) [] allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...) allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...) + allPlugins = append(allPlugins, azure_file.ProbeVolumePlugins()...) allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...) allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...) allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...) diff --git a/pkg/cloudprovider/providers/azure/azure_fakes.go b/pkg/cloudprovider/providers/azure/azure_fakes.go index 5233f605776..20e0a1dca6b 100644 --- a/pkg/cloudprovider/providers/azure/azure_fakes.go +++ b/pkg/cloudprovider/providers/azure/azure_fakes.go @@ -930,7 +930,7 @@ func (fRTC *fakeRouteTablesClient) Get(resourceGroupName string, routeTableName type fakeFileClient struct { } -func (fFC *fakeFileClient) createFileShare(accountName, accountKey, name string, sizeGB int) error { +func (fFC *fakeFileClient) createFileShare(accountName, accountKey, name string, sizeGiB int) error { return nil } @@ -938,6 +938,10 @@ func (fFC *fakeFileClient) deleteFileShare(accountName, accountKey, name string) return nil } +func (fFC *fakeFileClient) resizeFileShare(accountName, accountKey, name string, sizeGiB int) error { + return nil +} + type fakeStorageAccountClient struct { mutex *sync.Mutex FakeStore map[string]map[string]storage.Account diff --git a/pkg/cloudprovider/providers/azure/azure_file.go b/pkg/cloudprovider/providers/azure/azure_file.go index 39e54383dc4..138dabb5765 100644 --- a/pkg/cloudprovider/providers/azure/azure_file.go +++ b/pkg/cloudprovider/providers/azure/azure_file.go @@ -31,24 +31,29 @@ const ( // FileClient is the interface for creating file shares, interface for test // injection. type FileClient interface { - createFileShare(accountName, accountKey, name string, sizeGB int) error + createFileShare(accountName, accountKey, name string, sizeGiB int) error deleteFileShare(accountName, accountKey, name string) error + resizeFileShare(accountName, accountKey, name string, sizeGiB int) error } // create file share -func (az *Cloud) createFileShare(accountName, accountKey, name string, sizeGB int) error { - return az.FileClient.createFileShare(accountName, accountKey, name, sizeGB) +func (az *Cloud) createFileShare(accountName, accountKey, name string, sizeGiB int) error { + return az.FileClient.createFileShare(accountName, accountKey, name, sizeGiB) } func (az *Cloud) deleteFileShare(accountName, accountKey, name string) error { return az.FileClient.deleteFileShare(accountName, accountKey, name) } +func (az *Cloud) resizeFileShare(accountName, accountKey, name string, sizeGiB int) error { + return az.FileClient.resizeFileShare(accountName, accountKey, name, sizeGiB) +} + type azureFileClient struct { env azure.Environment } -func (f *azureFileClient) createFileShare(accountName, accountKey, name string, sizeGB int) error { +func (f *azureFileClient) createFileShare(accountName, accountKey, name string, sizeGiB int) error { fileClient, err := f.getFileSvcClient(accountName, accountKey) if err != nil { return err @@ -62,7 +67,7 @@ func (f *azureFileClient) createFileShare(accountName, accountKey, name string, if err = share.Create(nil); err != nil { return fmt.Errorf("failed to create file share, err: %v", err) } - share.Properties.Quota = sizeGB + share.Properties.Quota = sizeGiB if err = share.SetProperties(nil); err != nil { if err := share.Delete(nil); err != nil { glog.Errorf("Error deleting share: %v", err) @@ -81,6 +86,25 @@ func (f *azureFileClient) deleteFileShare(accountName, accountKey, name string) return fileClient.GetShareReference(name).Delete(nil) } +func (f *azureFileClient) resizeFileShare(accountName, accountKey, name string, sizeGiB int) error { + fileClient, err := f.getFileSvcClient(accountName, accountKey) + if err != nil { + return err + } + share := fileClient.GetShareReference(name) + if share.Properties.Quota >= sizeGiB { + glog.Warningf("file share size(%dGi) is already greater or equal than requested size(%dGi), accountName: %s, shareName: %s", + share.Properties.Quota, sizeGiB, accountName, name) + return nil + } + share.Properties.Quota = sizeGiB + if err = share.SetProperties(nil); err != nil { + return fmt.Errorf("failed to set quota on file share %s, err: %v", name, err) + } + glog.V(4).Infof("resize file share completed, accountName: %s, shareName: %s, sizeGiB: %d", accountName, name, sizeGiB) + return nil +} + func (f *azureFileClient) getFileSvcClient(accountName, accountKey string) (*azs.FileServiceClient, error) { fileClient, err := azs.NewClient(accountName, accountKey, f.env.StorageEndpointSuffix, azs.DefaultAPIVersion, useHTTPS) if err != nil { diff --git a/pkg/cloudprovider/providers/azure/azure_storage.go b/pkg/cloudprovider/providers/azure/azure_storage.go index b9dead6fac8..ab5f820ff6e 100644 --- a/pkg/cloudprovider/providers/azure/azure_storage.go +++ b/pkg/cloudprovider/providers/azure/azure_storage.go @@ -23,7 +23,7 @@ import ( ) // CreateFileShare creates a file share, using a matching storage account -func (az *Cloud) CreateFileShare(name, storageAccount, storageType, location string, requestGB int) (string, string, error) { +func (az *Cloud) CreateFileShare(name, storageAccount, storageType, location string, requestGiB int) (string, string, error) { var errResult error accounts := []accountWithLocation{} if len(storageAccount) > 0 { @@ -46,7 +46,7 @@ func (az *Cloud) CreateFileShare(name, storageAccount, storageType, location str continue } - if innerErr = az.createFileShare(account.Name, key, name, requestGB); innerErr != nil { + if innerErr = az.createFileShare(account.Name, key, name, requestGiB); innerErr != nil { errResult = fmt.Errorf("failed to create share %s in account %s: %v", name, account.Name, innerErr) continue } @@ -69,3 +69,8 @@ func (az *Cloud) DeleteFileShare(accountName, key, name string) error { glog.V(4).Infof("share %s deleted", name) return nil } + +// ResizeFileShare resizes a file share +func (az *Cloud) ResizeFileShare(accountName, accountKey, name string, sizeGiB int) error { + return az.resizeFileShare(accountName, accountKey, name, sizeGiB) +} diff --git a/pkg/volume/azure_file/azure_file.go b/pkg/volume/azure_file/azure_file.go index 7ac1962b8cd..7b925495a61 100644 --- a/pkg/volume/azure_file/azure_file.go +++ b/pkg/volume/azure_file/azure_file.go @@ -21,16 +21,16 @@ import ( "os" "runtime" + "github.com/golang/glog" "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/kubernetes/pkg/cloudprovider" + "k8s.io/kubernetes/pkg/cloudprovider/providers/azure" "k8s.io/kubernetes/pkg/util/mount" kstrings "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" - - "github.com/golang/glog" - "k8s.io/kubernetes/pkg/cloudprovider" - "k8s.io/kubernetes/pkg/cloudprovider/providers/azure" "k8s.io/kubernetes/pkg/volume/util" ) @@ -45,6 +45,7 @@ type azureFilePlugin struct { var _ volume.VolumePlugin = &azureFilePlugin{} var _ volume.PersistentVolumePlugin = &azureFilePlugin{} +var _ volume.ExpandableVolumePlugin = &azureFilePlugin{} const ( azureFilePluginName = "kubernetes.io/azure-file" @@ -139,6 +140,41 @@ func (plugin *azureFilePlugin) newUnmounterInternal(volName string, podUID types }}, nil } +func (plugin *azureFilePlugin) RequiresFSResize() bool { + return false +} + +func (plugin *azureFilePlugin) ExpandVolumeDevice( + spec *volume.Spec, + newSize resource.Quantity, + oldSize resource.Quantity) (resource.Quantity, error) { + + if spec.PersistentVolume != nil || spec.PersistentVolume.Spec.AzureFile == nil { + return oldSize, fmt.Errorf("invalid PV spec") + } + shareName := spec.PersistentVolume.Spec.AzureFile.ShareName + azure, err := getAzureCloudProvider(plugin.host.GetCloudProvider()) + if err != nil { + return oldSize, err + } + + secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace) + if err != nil { + return oldSize, err + } + + accountName, accountKey, err := (&azureSvc{}).GetAzureCredentials(plugin.host, secretNamespace, secretName) + if err != nil { + return oldSize, err + } + + if err := azure.ResizeFileShare(accountName, accountKey, shareName, int(volume.RoundUpToGiB(newSize))); err != nil { + return oldSize, err + } + + return newSize, nil +} + func (plugin *azureFilePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) { azureVolume := &v1.Volume{ Name: volName, diff --git a/pkg/volume/azure_file/azure_provision.go b/pkg/volume/azure_file/azure_provision.go index 882f496d122..76a9db87cea 100644 --- a/pkg/volume/azure_file/azure_provision.go +++ b/pkg/volume/azure_file/azure_provision.go @@ -38,9 +38,11 @@ var _ volume.ProvisionableVolumePlugin = &azureFilePlugin{} // azure cloud provider should implement it type azureCloudProvider interface { // create a file share - CreateFileShare(name, storageAccount, storageType, location string, requestGB int) (string, string, error) + CreateFileShare(name, storageAccount, storageType, location string, requestGiB int) (string, string, error) // delete a file share DeleteFileShare(accountName, key, name string) error + // resize a file share + ResizeFileShare(accountName, accountKey, name string, sizeGiB int) error } type azureFileDeleter struct { @@ -141,7 +143,7 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) { name = strings.Replace(name, "--", "-", -1) capacity := a.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] requestBytes := capacity.Value() - requestGB := int(volume.RoundUpSize(requestBytes, 1024*1024*1024)) + requestGiB := int(volume.RoundUpSize(requestBytes, 1024*1024*1024)) secretNamespace := a.options.PVC.Namespace // Apply ProvisionerParameters (case-insensitive). We leave validation of // the values to the cloud provider. @@ -164,7 +166,7 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) { return nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on Azure file") } - account, key, err := a.azureProvider.CreateFileShare(name, account, sku, location, requestGB) + account, key, err := a.azureProvider.CreateFileShare(name, account, sku, location, requestGiB) if err != nil { return nil, err } @@ -187,7 +189,7 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) { PersistentVolumeReclaimPolicy: a.options.PersistentVolumeReclaimPolicy, AccessModes: a.options.PVC.Spec.AccessModes, Capacity: v1.ResourceList{ - v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", requestGB)), + v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", requestGiB)), }, PersistentVolumeSource: v1.PersistentVolumeSource{ AzureFile: &v1.AzureFilePersistentVolumeSource{ diff --git a/plugin/pkg/admission/persistentvolume/resize/admission.go b/plugin/pkg/admission/persistentvolume/resize/admission.go index 9909f5f3fb7..3d04ae812dd 100644 --- a/plugin/pkg/admission/persistentvolume/resize/admission.go +++ b/plugin/pkg/admission/persistentvolume/resize/admission.go @@ -161,5 +161,8 @@ func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVol return true } + if pv.Spec.AzureFile != nil { + return true + } return false }