Revert "removed the deprecated azureFile in-tree storage plugin"
				
					
				
			This commit is contained in:
		@@ -25,6 +25,7 @@ import (
 | 
				
			|||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume"
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume/azure_file"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/csimigration"
 | 
						"k8s.io/kubernetes/pkg/volume/csimigration"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/gcepd"
 | 
						"k8s.io/kubernetes/pkg/volume/gcepd"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/portworx"
 | 
						"k8s.io/kubernetes/pkg/volume/portworx"
 | 
				
			||||||
@@ -80,5 +81,24 @@ func appendExpandableLegacyProviderVolumes(logger klog.Logger, allPlugins []volu
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func appendLegacyProviderVolumes(logger klog.Logger, allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
 | 
					func appendLegacyProviderVolumes(logger klog.Logger, allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
 | 
				
			||||||
	return appendAttachableLegacyProviderVolumes(logger, allPlugins, featureGate)
 | 
						var err error
 | 
				
			||||||
 | 
						// First append attachable volumes
 | 
				
			||||||
 | 
						allPlugins, err = appendAttachableLegacyProviderVolumes(logger, allPlugins, featureGate)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return allPlugins, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Then append non-attachable volumes
 | 
				
			||||||
 | 
						pluginName := plugins.AzureFileInTreePluginName
 | 
				
			||||||
 | 
						pluginInfo := pluginInfo{
 | 
				
			||||||
 | 
							pluginMigrationFeature:  features.CSIMigrationAzureFile,
 | 
				
			||||||
 | 
							pluginUnregisterFeature: features.InTreePluginAzureFileUnregister,
 | 
				
			||||||
 | 
							pluginProbeFunction:     azure_file.ProbeVolumePlugins,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						allPlugins, err = appendPluginBasedOnFeatureFlags(logger, allPlugins, pluginName, featureGate, pluginInfo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return allPlugins, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return allPlugins, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ import (
 | 
				
			|||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume"
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume/azure_file"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/csimigration"
 | 
						"k8s.io/kubernetes/pkg/volume/csimigration"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/gcepd"
 | 
						"k8s.io/kubernetes/pkg/volume/gcepd"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/portworx"
 | 
						"k8s.io/kubernetes/pkg/volume/portworx"
 | 
				
			||||||
@@ -66,6 +67,7 @@ type pluginInfo struct {
 | 
				
			|||||||
func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
 | 
					func appendLegacyProviderVolumes(allPlugins []volume.VolumePlugin, featureGate featuregate.FeatureGate) ([]volume.VolumePlugin, error) {
 | 
				
			||||||
	pluginMigrationStatus := make(map[string]pluginInfo)
 | 
						pluginMigrationStatus := make(map[string]pluginInfo)
 | 
				
			||||||
	pluginMigrationStatus[plugins.GCEPDInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationGCE, pluginUnregisterFeature: features.InTreePluginGCEUnregister, pluginProbeFunction: gcepd.ProbeVolumePlugins}
 | 
						pluginMigrationStatus[plugins.GCEPDInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationGCE, pluginUnregisterFeature: features.InTreePluginGCEUnregister, pluginProbeFunction: gcepd.ProbeVolumePlugins}
 | 
				
			||||||
 | 
						pluginMigrationStatus[plugins.AzureFileInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationAzureFile, pluginUnregisterFeature: features.InTreePluginAzureFileUnregister, pluginProbeFunction: azure_file.ProbeVolumePlugins}
 | 
				
			||||||
	pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginUnregisterFeature: features.InTreePluginvSphereUnregister, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
 | 
						pluginMigrationStatus[plugins.VSphereInTreePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationvSphere, pluginUnregisterFeature: features.InTreePluginvSphereUnregister, pluginProbeFunction: vsphere_volume.ProbeVolumePlugins}
 | 
				
			||||||
	pluginMigrationStatus[plugins.PortworxVolumePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationPortworx, pluginUnregisterFeature: features.InTreePluginPortworxUnregister, pluginProbeFunction: portworx.ProbeVolumePlugins}
 | 
						pluginMigrationStatus[plugins.PortworxVolumePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationPortworx, pluginUnregisterFeature: features.InTreePluginPortworxUnregister, pluginProbeFunction: portworx.ProbeVolumePlugins}
 | 
				
			||||||
	pluginMigrationStatus[plugins.RBDVolumePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationRBD, pluginUnregisterFeature: features.InTreePluginRBDUnregister, pluginProbeFunction: rbd.ProbeVolumePlugins}
 | 
						pluginMigrationStatus[plugins.RBDVolumePluginName] = pluginInfo{pluginMigrationFeature: features.CSIMigrationRBD, pluginUnregisterFeature: features.InTreePluginRBDUnregister, pluginProbeFunction: rbd.ProbeVolumePlugins}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,6 +122,14 @@ const (
 | 
				
			|||||||
	// Allow the usage of options to fine-tune the cpumanager policies.
 | 
						// Allow the usage of options to fine-tune the cpumanager policies.
 | 
				
			||||||
	CPUManagerPolicyOptions featuregate.Feature = "CPUManagerPolicyOptions"
 | 
						CPUManagerPolicyOptions featuregate.Feature = "CPUManagerPolicyOptions"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// owner: @andyzhangx
 | 
				
			||||||
 | 
						// alpha: v1.15
 | 
				
			||||||
 | 
						// beta: v1.21
 | 
				
			||||||
 | 
						// GA: v1.26
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Enables the Azure File in-tree driver to Azure File Driver migration feature.
 | 
				
			||||||
 | 
						CSIMigrationAzureFile featuregate.Feature = "CSIMigrationAzureFile"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// owner: @davidz627
 | 
						// owner: @davidz627
 | 
				
			||||||
	// alpha: v1.14
 | 
						// alpha: v1.14
 | 
				
			||||||
	// beta: v1.17
 | 
						// beta: v1.17
 | 
				
			||||||
@@ -870,6 +878,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	CPUManagerPolicyOptions: {Default: true, PreRelease: featuregate.Beta},
 | 
						CPUManagerPolicyOptions: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CSIMigrationAzureFile: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.28
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CSIMigrationGCE: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27
 | 
						CSIMigrationGCE: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CSIMigrationPortworx: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires Portworx CSI driver)
 | 
						CSIMigrationPortworx: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires Portworx CSI driver)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								pkg/volume/azure_file/OWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								pkg/volume/azure_file/OWNERS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					# See the OWNERS docs at https://go.k8s.io/owners
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					approvers:
 | 
				
			||||||
 | 
					  - andyzhangx
 | 
				
			||||||
 | 
					  - feiskyer
 | 
				
			||||||
 | 
					  - khenidak
 | 
				
			||||||
 | 
					reviewers:
 | 
				
			||||||
 | 
					  - andyzhangx
 | 
				
			||||||
 | 
					  - feiskyer
 | 
				
			||||||
 | 
					  - jsafrane
 | 
				
			||||||
 | 
					  - jingxu97
 | 
				
			||||||
 | 
					  - khenidak
 | 
				
			||||||
 | 
					  - msau42
 | 
				
			||||||
 | 
					  - saad-ali
 | 
				
			||||||
 | 
					emeritus_approvers:
 | 
				
			||||||
 | 
					  - karataliu
 | 
				
			||||||
 | 
					  - rootfs
 | 
				
			||||||
 | 
					  - brendandburns
 | 
				
			||||||
							
								
								
									
										414
									
								
								pkg/volume/azure_file/azure_file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								pkg/volume/azure_file/azure_file.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,414 @@
 | 
				
			|||||||
 | 
					//go:build !providerless
 | 
				
			||||||
 | 
					// +build !providerless
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2016 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 azure_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
 | 
						"k8s.io/mount-utils"
 | 
				
			||||||
 | 
						utilstrings "k8s.io/utils/strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v1 "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/apimachinery/pkg/util/wait"
 | 
				
			||||||
 | 
						cloudprovider "k8s.io/cloud-provider"
 | 
				
			||||||
 | 
						volumehelpers "k8s.io/cloud-provider/volume/helpers"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
 | 
						volutil "k8s.io/kubernetes/pkg/volume/util"
 | 
				
			||||||
 | 
						"k8s.io/legacy-cloud-providers/azure"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ProbeVolumePlugins is the primary endpoint for volume plugins
 | 
				
			||||||
 | 
					func ProbeVolumePlugins() []volume.VolumePlugin {
 | 
				
			||||||
 | 
						return []volume.VolumePlugin{&azureFilePlugin{nil}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type azureFilePlugin struct {
 | 
				
			||||||
 | 
						host volume.VolumeHost
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ volume.VolumePlugin = &azureFilePlugin{}
 | 
				
			||||||
 | 
					var _ volume.PersistentVolumePlugin = &azureFilePlugin{}
 | 
				
			||||||
 | 
					var _ volume.ExpandableVolumePlugin = &azureFilePlugin{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						azureFilePluginName     = "kubernetes.io/azure-file"
 | 
				
			||||||
 | 
						minimumPremiumShareSize = 100 // GB
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
 | 
				
			||||||
 | 
						return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(azureFilePluginName), volName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) Init(host volume.VolumeHost) error {
 | 
				
			||||||
 | 
						plugin.host = host
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) GetPluginName() string {
 | 
				
			||||||
 | 
						return azureFilePluginName
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
 | 
				
			||||||
 | 
						share, _, err := getVolumeSource(spec)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return share, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) CanSupport(spec *volume.Spec) bool {
 | 
				
			||||||
 | 
						//TODO: check if mount.cifs is there
 | 
				
			||||||
 | 
						return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureFile != nil) ||
 | 
				
			||||||
 | 
							(spec.Volume != nil && spec.Volume.AzureFile != nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) RequiresRemount(spec *volume.Spec) bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) SupportsMountOption() bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) SupportsBulkVolumeVerification() bool {
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
 | 
				
			||||||
 | 
						return []v1.PersistentVolumeAccessMode{
 | 
				
			||||||
 | 
							v1.ReadWriteOnce,
 | 
				
			||||||
 | 
							v1.ReadOnlyMany,
 | 
				
			||||||
 | 
							v1.ReadWriteMany,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
 | 
				
			||||||
 | 
						return plugin.newMounterInternal(spec, pod, &azureSvc{}, plugin.host.GetMounter(plugin.GetPluginName()))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, util azureUtil, mounter mount.Interface) (volume.Mounter, error) {
 | 
				
			||||||
 | 
						share, readOnly, err := getVolumeSource(spec)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						secretName, secretNamespace, err := getSecretNameAndNamespace(spec, pod.Namespace)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// Log-and-continue instead of returning an error for now
 | 
				
			||||||
 | 
							// due to unspecified backwards compatibility concerns (a subject to revise)
 | 
				
			||||||
 | 
							klog.Errorf("get secret name and namespace from pod(%s) return with error: %v", pod.Name, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &azureFileMounter{
 | 
				
			||||||
 | 
							azureFile: &azureFile{
 | 
				
			||||||
 | 
								volName:         spec.Name(),
 | 
				
			||||||
 | 
								mounter:         mounter,
 | 
				
			||||||
 | 
								pod:             pod,
 | 
				
			||||||
 | 
								plugin:          plugin,
 | 
				
			||||||
 | 
								MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							util:            util,
 | 
				
			||||||
 | 
							secretNamespace: secretNamespace,
 | 
				
			||||||
 | 
							secretName:      secretName,
 | 
				
			||||||
 | 
							shareName:       share,
 | 
				
			||||||
 | 
							readOnly:        readOnly,
 | 
				
			||||||
 | 
							mountOptions:    volutil.MountOptionFromSpec(spec),
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
 | 
				
			||||||
 | 
						return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
 | 
				
			||||||
 | 
						return &azureFileUnmounter{&azureFile{
 | 
				
			||||||
 | 
							volName:         volName,
 | 
				
			||||||
 | 
							mounter:         mounter,
 | 
				
			||||||
 | 
							pod:             &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: podUID}},
 | 
				
			||||||
 | 
							plugin:          plugin,
 | 
				
			||||||
 | 
							MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
 | 
				
			||||||
 | 
						}}, 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, resourceGroup, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return oldSize, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if spec.PersistentVolume.ObjectMeta.Annotations[resourceGroupAnnotation] != "" {
 | 
				
			||||||
 | 
							resourceGroup = spec.PersistentVolume.ObjectMeta.Annotations[resourceGroupAnnotation]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return oldSize, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						accountName, _, err := (&azureSvc{}).GetAzureCredentials(plugin.host, secretNamespace, secretName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return oldSize, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 newSize, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) ConstructVolumeSpec(volName, mountPath string) (volume.ReconstructedVolume, error) {
 | 
				
			||||||
 | 
						azureVolume := &v1.Volume{
 | 
				
			||||||
 | 
							Name: volName,
 | 
				
			||||||
 | 
							VolumeSource: v1.VolumeSource{
 | 
				
			||||||
 | 
								AzureFile: &v1.AzureFileVolumeSource{
 | 
				
			||||||
 | 
									SecretName: volName,
 | 
				
			||||||
 | 
									ShareName:  volName,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return volume.ReconstructedVolume{
 | 
				
			||||||
 | 
							Spec: volume.NewSpecFromVolume(azureVolume),
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// azureFile volumes represent mount of an AzureFile share.
 | 
				
			||||||
 | 
					type azureFile struct {
 | 
				
			||||||
 | 
						volName string
 | 
				
			||||||
 | 
						podUID  types.UID
 | 
				
			||||||
 | 
						pod     *v1.Pod
 | 
				
			||||||
 | 
						mounter mount.Interface
 | 
				
			||||||
 | 
						plugin  *azureFilePlugin
 | 
				
			||||||
 | 
						volume.MetricsProvider
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (azureFileVolume *azureFile) GetPath() string {
 | 
				
			||||||
 | 
						return getPath(azureFileVolume.pod.UID, azureFileVolume.volName, azureFileVolume.plugin.host)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type azureFileMounter struct {
 | 
				
			||||||
 | 
						*azureFile
 | 
				
			||||||
 | 
						util            azureUtil
 | 
				
			||||||
 | 
						secretName      string
 | 
				
			||||||
 | 
						secretNamespace string
 | 
				
			||||||
 | 
						shareName       string
 | 
				
			||||||
 | 
						readOnly        bool
 | 
				
			||||||
 | 
						mountOptions    []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ volume.Mounter = &azureFileMounter{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *azureFileMounter) GetAttributes() volume.Attributes {
 | 
				
			||||||
 | 
						return volume.Attributes{
 | 
				
			||||||
 | 
							ReadOnly:       b.readOnly,
 | 
				
			||||||
 | 
							Managed:        !b.readOnly,
 | 
				
			||||||
 | 
							SELinuxRelabel: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetUp attaches the disk and bind mounts to the volume path.
 | 
				
			||||||
 | 
					func (b *azureFileMounter) SetUp(mounterArgs volume.MounterArgs) error {
 | 
				
			||||||
 | 
						return b.SetUpAt(b.GetPath(), mounterArgs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *azureFileMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
 | 
				
			||||||
 | 
						notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
 | 
				
			||||||
 | 
						klog.V(4).Infof("AzureFile mount set up: %s %v %v", dir, !notMnt, err)
 | 
				
			||||||
 | 
						if err != nil && !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !notMnt {
 | 
				
			||||||
 | 
							// testing original mount point, make sure the mount link is valid
 | 
				
			||||||
 | 
							if _, err := ioutil.ReadDir(dir); err == nil {
 | 
				
			||||||
 | 
								klog.V(4).Infof("azureFile - already mounted to target %s", dir)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// mount link is invalid, now unmount and remount later
 | 
				
			||||||
 | 
							klog.Warningf("azureFile - ReadDir %s failed with %v, unmount this directory", dir, err)
 | 
				
			||||||
 | 
							if err := b.mounter.Unmount(dir); err != nil {
 | 
				
			||||||
 | 
								klog.Errorf("azureFile - Unmount directory %s failed with %v", dir, err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var accountKey, accountName string
 | 
				
			||||||
 | 
						if accountName, accountKey, err = b.util.GetAzureCredentials(b.plugin.host, b.secretNamespace, b.secretName); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mountOptions []string
 | 
				
			||||||
 | 
						var sensitiveMountOptions []string
 | 
				
			||||||
 | 
						source := ""
 | 
				
			||||||
 | 
						osSeparator := string(os.PathSeparator)
 | 
				
			||||||
 | 
						source = fmt.Sprintf("%s%s%s.file.%s%s%s", osSeparator, osSeparator, accountName, getStorageEndpointSuffix(b.plugin.host.GetCloudProvider()), osSeparator, b.shareName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
							mountOptions = []string{fmt.Sprintf("AZURE\\%s", accountName)}
 | 
				
			||||||
 | 
							sensitiveMountOptions = []string{accountKey}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if err := os.MkdirAll(dir, 0700); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// parameters suggested by https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/
 | 
				
			||||||
 | 
							options := []string{}
 | 
				
			||||||
 | 
							sensitiveMountOptions = []string{fmt.Sprintf("username=%s,password=%s", accountName, accountKey)}
 | 
				
			||||||
 | 
							if b.readOnly {
 | 
				
			||||||
 | 
								options = append(options, "ro")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							mountOptions = volutil.JoinMountOptions(b.mountOptions, options)
 | 
				
			||||||
 | 
							mountOptions = appendDefaultMountOptions(mountOptions, mounterArgs.FsGroup)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mountComplete := false
 | 
				
			||||||
 | 
						err = wait.PollImmediate(1*time.Second, 2*time.Minute, func() (bool, error) {
 | 
				
			||||||
 | 
							err := b.mounter.MountSensitiveWithoutSystemd(source, dir, "cifs", mountOptions, sensitiveMountOptions)
 | 
				
			||||||
 | 
							mountComplete = true
 | 
				
			||||||
 | 
							return true, err
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if !mountComplete {
 | 
				
			||||||
 | 
							return fmt.Errorf("volume(%s) mount on %s timeout(10m)", source, dir)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
 | 
				
			||||||
 | 
							if mntErr != nil {
 | 
				
			||||||
 | 
								klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !notMnt {
 | 
				
			||||||
 | 
								if mntErr = b.mounter.Unmount(dir); mntErr != nil {
 | 
				
			||||||
 | 
									klog.Errorf("Failed to unmount: %v", mntErr)
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
 | 
				
			||||||
 | 
								if mntErr != nil {
 | 
				
			||||||
 | 
									klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if !notMnt {
 | 
				
			||||||
 | 
									// This is very odd, we don't expect it.  We'll try again next sync loop.
 | 
				
			||||||
 | 
									klog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", dir)
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							os.Remove(dir)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ volume.Unmounter = &azureFileUnmounter{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type azureFileUnmounter struct {
 | 
				
			||||||
 | 
						*azureFile
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *azureFileUnmounter) TearDown() error {
 | 
				
			||||||
 | 
						return c.TearDownAt(c.GetPath())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *azureFileUnmounter) TearDownAt(dir string) error {
 | 
				
			||||||
 | 
						return mount.CleanupMountPoint(dir, c.mounter, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getVolumeSource(spec *volume.Spec) (string, bool, error) {
 | 
				
			||||||
 | 
						if spec.Volume != nil && spec.Volume.AzureFile != nil {
 | 
				
			||||||
 | 
							share := spec.Volume.AzureFile.ShareName
 | 
				
			||||||
 | 
							readOnly := spec.Volume.AzureFile.ReadOnly
 | 
				
			||||||
 | 
							return share, readOnly, nil
 | 
				
			||||||
 | 
						} else if spec.PersistentVolume != nil &&
 | 
				
			||||||
 | 
							spec.PersistentVolume.Spec.AzureFile != nil {
 | 
				
			||||||
 | 
							share := spec.PersistentVolume.Spec.AzureFile.ShareName
 | 
				
			||||||
 | 
							readOnly := spec.ReadOnly
 | 
				
			||||||
 | 
							return share, readOnly, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", false, fmt.Errorf("Spec does not reference an AzureFile volume type")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getSecretNameAndNamespace(spec *volume.Spec, defaultNamespace string) (string, string, error) {
 | 
				
			||||||
 | 
						secretName := ""
 | 
				
			||||||
 | 
						secretNamespace := ""
 | 
				
			||||||
 | 
						if spec.Volume != nil && spec.Volume.AzureFile != nil {
 | 
				
			||||||
 | 
							secretName = spec.Volume.AzureFile.SecretName
 | 
				
			||||||
 | 
							secretNamespace = defaultNamespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else if spec.PersistentVolume != nil &&
 | 
				
			||||||
 | 
							spec.PersistentVolume.Spec.AzureFile != nil {
 | 
				
			||||||
 | 
							secretNamespace = defaultNamespace
 | 
				
			||||||
 | 
							if spec.PersistentVolume.Spec.AzureFile.SecretNamespace != nil {
 | 
				
			||||||
 | 
								secretNamespace = *spec.PersistentVolume.Spec.AzureFile.SecretNamespace
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							secretName = spec.PersistentVolume.Spec.AzureFile.SecretName
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("Spec does not reference an AzureFile volume type")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(secretNamespace) == 0 {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("invalid Azure volume: nil namespace")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return secretName, secretNamespace, nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getAzureCloud(cloudProvider cloudprovider.Interface) (*azure.Cloud, error) {
 | 
				
			||||||
 | 
						azure, ok := cloudProvider.(*azure.Cloud)
 | 
				
			||||||
 | 
						if !ok || azure == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to get Azure Cloud Provider. GetCloudProvider returned %v instead", cloudProvider)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return azure, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getStorageEndpointSuffix(cloudprovider cloudprovider.Interface) string {
 | 
				
			||||||
 | 
						const publicCloudStorageEndpointSuffix = "core.windows.net"
 | 
				
			||||||
 | 
						azure, err := getAzureCloud(cloudprovider)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.Warningf("No Azure cloud provider found. Using the Azure public cloud endpoint: %s", publicCloudStorageEndpointSuffix)
 | 
				
			||||||
 | 
							return publicCloudStorageEndpointSuffix
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return azure.Environment.StorageEndpointSuffix
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										454
									
								
								pkg/volume/azure_file/azure_file_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										454
									
								
								pkg/volume/azure_file/azure_file_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,454 @@
 | 
				
			|||||||
 | 
					//go:build !providerless
 | 
				
			||||||
 | 
					// +build !providerless
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2016 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 azure_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						goruntime "runtime"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
						"k8s.io/client-go/kubernetes/fake"
 | 
				
			||||||
 | 
						fakecloud "k8s.io/cloud-provider/fake"
 | 
				
			||||||
 | 
						"k8s.io/mount-utils"
 | 
				
			||||||
 | 
						"k8s.io/utils/pointer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
 | 
						volumetest "k8s.io/kubernetes/pkg/volume/testing"
 | 
				
			||||||
 | 
						"k8s.io/legacy-cloud-providers/azure"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCanSupport(t *testing.T) {
 | 
				
			||||||
 | 
						tmpDir, err := ioutil.TempDir(os.TempDir(), "azureFileTest")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("can't make a temp dir: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
						plugMgr := volume.VolumePluginMgr{}
 | 
				
			||||||
 | 
						plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal("Can't find the plugin by name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if plug.GetPluginName() != "kubernetes.io/azure-file" {
 | 
				
			||||||
 | 
							t.Errorf("Wrong name: %s", plug.GetPluginName())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !plug.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{AzureFile: &v1.AzureFileVolumeSource{}}}}) {
 | 
				
			||||||
 | 
							t.Errorf("Expected true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !plug.CanSupport(&volume.Spec{PersistentVolume: &v1.PersistentVolume{Spec: v1.PersistentVolumeSpec{PersistentVolumeSource: v1.PersistentVolumeSource{AzureFile: &v1.AzureFilePersistentVolumeSource{}}}}}) {
 | 
				
			||||||
 | 
							t.Errorf("Expected true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetAccessModes(t *testing.T) {
 | 
				
			||||||
 | 
						tmpDir, err := ioutil.TempDir(os.TempDir(), "azureFileTest")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("can't make a temp dir: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
						plugMgr := volume.VolumePluginMgr{}
 | 
				
			||||||
 | 
						plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plug, err := plugMgr.FindPersistentPluginByName("kubernetes.io/azure-file")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Can't find the plugin by name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteOnce) || !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadOnlyMany) || !volumetest.ContainsAccessMode(plug.GetAccessModes(), v1.ReadWriteMany) {
 | 
				
			||||||
 | 
							t.Errorf("Expected three AccessModeTypes:  %s, %s, and %s", v1.ReadWriteOnce, v1.ReadOnlyMany, v1.ReadWriteMany)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getAzureTestCloud(t *testing.T) *azure.Cloud {
 | 
				
			||||||
 | 
						config := `{
 | 
				
			||||||
 | 
					                "aadClientId": "--aad-client-id--",
 | 
				
			||||||
 | 
					                "aadClientSecret": "--aad-client-secret--"
 | 
				
			||||||
 | 
					        }`
 | 
				
			||||||
 | 
						configReader := strings.NewReader(config)
 | 
				
			||||||
 | 
						azureCloud, err := azure.NewCloudWithoutFeatureGates(configReader)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Error(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return azureCloud
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getTestTempDir(t *testing.T) string {
 | 
				
			||||||
 | 
						tmpDir, err := ioutil.TempDir(os.TempDir(), "azurefileTest")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("can't make a temp dir: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return tmpDir
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPluginAzureCloudProvider(t *testing.T) {
 | 
				
			||||||
 | 
						tmpDir := getTestTempDir(t)
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
						testPlugin(t, tmpDir, volumetest.NewFakeVolumeHostWithCloudProvider(t, tmpDir, nil, nil, getAzureTestCloud(t)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPluginWithoutCloudProvider(t *testing.T) {
 | 
				
			||||||
 | 
						tmpDir := getTestTempDir(t)
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
						testPlugin(t, tmpDir, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPluginWithOtherCloudProvider(t *testing.T) {
 | 
				
			||||||
 | 
						tmpDir := getTestTempDir(t)
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
						cloud := &fakecloud.Cloud{}
 | 
				
			||||||
 | 
						testPlugin(t, tmpDir, volumetest.NewFakeVolumeHostWithCloudProvider(t, tmpDir, nil, nil, cloud))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testPlugin(t *testing.T, tmpDir string, volumeHost volume.VolumeHost) {
 | 
				
			||||||
 | 
						plugMgr := volume.VolumePluginMgr{}
 | 
				
			||||||
 | 
						plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumeHost)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Can't find the plugin by name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spec := &v1.Volume{
 | 
				
			||||||
 | 
							Name: "vol1",
 | 
				
			||||||
 | 
							VolumeSource: v1.VolumeSource{
 | 
				
			||||||
 | 
								AzureFile: &v1.AzureFileVolumeSource{
 | 
				
			||||||
 | 
									SecretName: "secret",
 | 
				
			||||||
 | 
									ShareName:  "share",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fake := mount.NewFakeMounter(nil)
 | 
				
			||||||
 | 
						pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
 | 
				
			||||||
 | 
						mounter, err := plug.(*azureFilePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, &fakeAzureSvc{}, fake)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Failed to make a new Mounter: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if mounter == nil {
 | 
				
			||||||
 | 
							t.Errorf("Got a nil Mounter")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						volPath := filepath.Join(tmpDir, "pods/poduid/volumes/kubernetes.io~azure-file/vol1")
 | 
				
			||||||
 | 
						path := mounter.GetPath()
 | 
				
			||||||
 | 
						if path != volPath {
 | 
				
			||||||
 | 
							t.Errorf("Got unexpected path: %s", path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := mounter.SetUp(volume.MounterArgs{}); err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected success, got: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// On Windows, Mount will create the parent of dir and mklink (create a symbolic link) at the volume path later,
 | 
				
			||||||
 | 
						// so mounter.SetUp will not create the directory. Otherwise mklink will error: "Cannot create a file when that file already exists".
 | 
				
			||||||
 | 
						if goruntime.GOOS != "windows" {
 | 
				
			||||||
 | 
							if _, err := os.Stat(path); err != nil {
 | 
				
			||||||
 | 
								if os.IsNotExist(err) {
 | 
				
			||||||
 | 
									t.Errorf("SetUp() failed, volume path not created: %s", path)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									t.Errorf("SetUp() failed: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unmounter, err := plug.(*azureFilePlugin).newUnmounterInternal("vol1", types.UID("poduid"), mount.NewFakeMounter(nil))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Failed to make a new Unmounter: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if unmounter == nil {
 | 
				
			||||||
 | 
							t.Errorf("Got a nil Unmounter")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := unmounter.TearDown(); err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected success, got: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, err := os.Stat(path); err == nil {
 | 
				
			||||||
 | 
							t.Errorf("TearDown() failed, volume path still exists: %s", path)
 | 
				
			||||||
 | 
						} else if !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							t.Errorf("TearDown() failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPersistentClaimReadOnlyFlag(t *testing.T) {
 | 
				
			||||||
 | 
						pv := &v1.PersistentVolume{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "pvA",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: v1.PersistentVolumeSpec{
 | 
				
			||||||
 | 
								PersistentVolumeSource: v1.PersistentVolumeSource{
 | 
				
			||||||
 | 
									AzureFile: &v1.AzureFilePersistentVolumeSource{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								ClaimRef: &v1.ObjectReference{
 | 
				
			||||||
 | 
									Name: "claimA",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						claim := &v1.PersistentVolumeClaim{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:      "claimA",
 | 
				
			||||||
 | 
								Namespace: "nsA",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: v1.PersistentVolumeClaimSpec{
 | 
				
			||||||
 | 
								VolumeName: "pvA",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Status: v1.PersistentVolumeClaimStatus{
 | 
				
			||||||
 | 
								Phase: v1.ClaimBound,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := fake.NewSimpleClientset(pv, claim)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plugMgr := volume.VolumePluginMgr{}
 | 
				
			||||||
 | 
						plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, filepath.Join(os.TempDir(), "fake"), client, nil))
 | 
				
			||||||
 | 
						plug, _ := plugMgr.FindPluginByName(azureFilePluginName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// readOnly bool is supplied by persistent-claim volume source when its mounter creates other volumes
 | 
				
			||||||
 | 
						spec := volume.NewSpecFromPersistentVolume(pv, true)
 | 
				
			||||||
 | 
						pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
 | 
				
			||||||
 | 
						mounter, _ := plug.NewMounter(spec, pod, volume.VolumeOptions{})
 | 
				
			||||||
 | 
						if mounter == nil {
 | 
				
			||||||
 | 
							t.Fatalf("Got a nil Mounter")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !mounter.GetAttributes().ReadOnly {
 | 
				
			||||||
 | 
							t.Errorf("Expected true for mounter.IsReadOnly")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fakeAzureSvc struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *fakeAzureSvc) GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName string) (string, string, error) {
 | 
				
			||||||
 | 
						return "name", "key", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (s *fakeAzureSvc) SetAzureCredentials(host volume.VolumeHost, nameSpace, accountName, accountKey string) (string, error) {
 | 
				
			||||||
 | 
						return "secret", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestMounterAndUnmounterTypeAssert(t *testing.T) {
 | 
				
			||||||
 | 
						tmpDir, err := ioutil.TempDir(os.TempDir(), "azurefileTest")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("can't make a temp dir: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpDir)
 | 
				
			||||||
 | 
						plugMgr := volume.VolumePluginMgr{}
 | 
				
			||||||
 | 
						plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpDir, nil, nil))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plug, err := plugMgr.FindPluginByName("kubernetes.io/azure-file")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Can't find the plugin by name")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spec := &v1.Volume{
 | 
				
			||||||
 | 
							Name: "vol1",
 | 
				
			||||||
 | 
							VolumeSource: v1.VolumeSource{
 | 
				
			||||||
 | 
								AzureFile: &v1.AzureFileVolumeSource{
 | 
				
			||||||
 | 
									SecretName: "secret",
 | 
				
			||||||
 | 
									ShareName:  "share",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fake := mount.NewFakeMounter(nil)
 | 
				
			||||||
 | 
						pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
 | 
				
			||||||
 | 
						mounter, err := plug.(*azureFilePlugin).newMounterInternal(volume.NewSpecFromVolume(spec), pod, &fakeAzureSvc{}, fake)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("MounterInternal() failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := mounter.(volume.Unmounter); ok {
 | 
				
			||||||
 | 
							t.Errorf("Volume Mounter can be type-assert to Unmounter")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unmounter, err := plug.(*azureFilePlugin).newUnmounterInternal("vol1", types.UID("poduid"), mount.NewFakeMounter(nil))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("MounterInternal() failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, ok := unmounter.(volume.Mounter); ok {
 | 
				
			||||||
 | 
							t.Errorf("Volume Unmounter can be type-assert to Mounter")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type testcase struct {
 | 
				
			||||||
 | 
						name      string
 | 
				
			||||||
 | 
						defaultNs string
 | 
				
			||||||
 | 
						spec      *volume.Spec
 | 
				
			||||||
 | 
						// Expected return of the test
 | 
				
			||||||
 | 
						expectedName  string
 | 
				
			||||||
 | 
						expectedNs    string
 | 
				
			||||||
 | 
						expectedError error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetSecretNameAndNamespaceForPV(t *testing.T) {
 | 
				
			||||||
 | 
						secretNs := "ns"
 | 
				
			||||||
 | 
						tests := []testcase{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "persistent volume source",
 | 
				
			||||||
 | 
								defaultNs: "default",
 | 
				
			||||||
 | 
								spec: &volume.Spec{
 | 
				
			||||||
 | 
									PersistentVolume: &v1.PersistentVolume{
 | 
				
			||||||
 | 
										Spec: v1.PersistentVolumeSpec{
 | 
				
			||||||
 | 
											PersistentVolumeSource: v1.PersistentVolumeSource{
 | 
				
			||||||
 | 
												AzureFile: &v1.AzureFilePersistentVolumeSource{
 | 
				
			||||||
 | 
													ShareName:       "share",
 | 
				
			||||||
 | 
													SecretName:      "name",
 | 
				
			||||||
 | 
													SecretNamespace: &secretNs,
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectedName:  "name",
 | 
				
			||||||
 | 
								expectedNs:    "ns",
 | 
				
			||||||
 | 
								expectedError: nil,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "persistent volume source without namespace",
 | 
				
			||||||
 | 
								defaultNs: "default",
 | 
				
			||||||
 | 
								spec: &volume.Spec{
 | 
				
			||||||
 | 
									PersistentVolume: &v1.PersistentVolume{
 | 
				
			||||||
 | 
										Spec: v1.PersistentVolumeSpec{
 | 
				
			||||||
 | 
											PersistentVolumeSource: v1.PersistentVolumeSource{
 | 
				
			||||||
 | 
												AzureFile: &v1.AzureFilePersistentVolumeSource{
 | 
				
			||||||
 | 
													ShareName:  "share",
 | 
				
			||||||
 | 
													SecretName: "name",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectedName:  "name",
 | 
				
			||||||
 | 
								expectedNs:    "default",
 | 
				
			||||||
 | 
								expectedError: nil,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:      "pod volume source",
 | 
				
			||||||
 | 
								defaultNs: "default",
 | 
				
			||||||
 | 
								spec: &volume.Spec{
 | 
				
			||||||
 | 
									Volume: &v1.Volume{
 | 
				
			||||||
 | 
										VolumeSource: v1.VolumeSource{
 | 
				
			||||||
 | 
											AzureFile: &v1.AzureFileVolumeSource{
 | 
				
			||||||
 | 
												ShareName:  "share",
 | 
				
			||||||
 | 
												SecretName: "name",
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expectedName:  "name",
 | 
				
			||||||
 | 
								expectedNs:    "default",
 | 
				
			||||||
 | 
								expectedError: nil,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, testcase := range tests {
 | 
				
			||||||
 | 
							resultName, resultNs, err := getSecretNameAndNamespace(testcase.spec, testcase.defaultNs)
 | 
				
			||||||
 | 
							if err != testcase.expectedError || resultName != testcase.expectedName || resultNs != testcase.expectedNs {
 | 
				
			||||||
 | 
								t.Errorf("%s failed: expected err=%v ns=%q name=%q, got %v/%q/%q", testcase.name, testcase.expectedError, testcase.expectedNs, testcase.expectedName,
 | 
				
			||||||
 | 
									err, resultNs, resultName)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAppendDefaultMountOptions(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							options  []string
 | 
				
			||||||
 | 
							fsGroup  *int64
 | 
				
			||||||
 | 
							expected []string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{"dir_mode=0777"},
 | 
				
			||||||
 | 
								fsGroup: nil,
 | 
				
			||||||
 | 
								expected: []string{"dir_mode=0777",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", fileMode, defaultFileMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", vers, defaultVers),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", actimeo, defaultActimeo),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{"file_mode=0777"},
 | 
				
			||||||
 | 
								fsGroup: pointer.Int64(0),
 | 
				
			||||||
 | 
								expected: []string{"file_mode=0777",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", dirMode, defaultDirMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", vers, defaultVers),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=0", gid),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", actimeo, defaultActimeo),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{"vers=2.1"},
 | 
				
			||||||
 | 
								fsGroup: pointer.Int64(1000),
 | 
				
			||||||
 | 
								expected: []string{"vers=2.1",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", fileMode, defaultFileMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", dirMode, defaultDirMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=1000", gid),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", actimeo, defaultActimeo),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{""},
 | 
				
			||||||
 | 
								expected: []string{"", fmt.Sprintf("%s=%s",
 | 
				
			||||||
 | 
									fileMode, defaultFileMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", dirMode, defaultDirMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", vers, defaultVers),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", actimeo, defaultActimeo),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{"file_mode=0777", "dir_mode=0777"},
 | 
				
			||||||
 | 
								expected: []string{"file_mode=0777", "dir_mode=0777",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", vers, defaultVers),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", actimeo, defaultActimeo),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{"gid=2000"},
 | 
				
			||||||
 | 
								fsGroup: pointer.Int64(1000),
 | 
				
			||||||
 | 
								expected: []string{"gid=2000",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", fileMode, defaultFileMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", dirMode, defaultDirMode),
 | 
				
			||||||
 | 
									"vers=3.0",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", actimeo, defaultActimeo),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								options: []string{"actimeo=3"},
 | 
				
			||||||
 | 
								expected: []string{
 | 
				
			||||||
 | 
									"actimeo=3",
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", fileMode, defaultFileMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", dirMode, defaultDirMode),
 | 
				
			||||||
 | 
									fmt.Sprintf("%s=%s", vers, defaultVers),
 | 
				
			||||||
 | 
									mfsymlinks,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							result := appendDefaultMountOptions(test.options, test.fsGroup)
 | 
				
			||||||
 | 
							if !reflect.DeepEqual(result, test.expected) {
 | 
				
			||||||
 | 
								t.Errorf("input: %q, appendDefaultMountOptions result: %q, expected: %q", test.options, result, test.expected)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										284
									
								
								pkg/volume/azure_file/azure_provision.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								pkg/volume/azure_file/azure_provision.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
				
			|||||||
 | 
					//go:build !providerless
 | 
				
			||||||
 | 
					// +build !providerless
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2017 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 azure_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
 | 
				
			||||||
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						cloudprovider "k8s.io/cloud-provider"
 | 
				
			||||||
 | 
						volumehelpers "k8s.io/cloud-provider/volume/helpers"
 | 
				
			||||||
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume/util"
 | 
				
			||||||
 | 
						"k8s.io/legacy-cloud-providers/azure"
 | 
				
			||||||
 | 
						"k8s.io/legacy-cloud-providers/azure/clients/fileclient"
 | 
				
			||||||
 | 
						utilstrings "k8s.io/utils/strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						_ volume.DeletableVolumePlugin     = &azureFilePlugin{}
 | 
				
			||||||
 | 
						_ volume.ProvisionableVolumePlugin = &azureFilePlugin{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resourceGroupAnnotation = "kubernetes.io/azure-file-resource-group"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Abstract interface to file share operations.
 | 
				
			||||||
 | 
					// azure cloud provider should implement it
 | 
				
			||||||
 | 
					type azureCloudProvider interface {
 | 
				
			||||||
 | 
						// create a file share
 | 
				
			||||||
 | 
						CreateFileShare(account *azure.AccountOptions, fileShare *fileclient.ShareOptions) (string, string, error)
 | 
				
			||||||
 | 
						// delete a file share
 | 
				
			||||||
 | 
						DeleteFileShare(resourceGroup, accountName, shareName string) error
 | 
				
			||||||
 | 
						// resize a file share
 | 
				
			||||||
 | 
						ResizeFileShare(resourceGroup, accountName, name string, sizeGiB int) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type azureFileDeleter struct {
 | 
				
			||||||
 | 
						*azureFile
 | 
				
			||||||
 | 
						resourceGroup, accountName, shareName string
 | 
				
			||||||
 | 
						azureProvider                         azureCloudProvider
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) NewDeleter(logger klog.Logger, spec *volume.Spec) (volume.Deleter, error) {
 | 
				
			||||||
 | 
						azure, resourceGroup, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.V(4).Infof("failed to get azure provider")
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if spec.PersistentVolume != nil && spec.PersistentVolume.ObjectMeta.Annotations[resourceGroupAnnotation] != "" {
 | 
				
			||||||
 | 
							resourceGroup = spec.PersistentVolume.ObjectMeta.Annotations[resourceGroupAnnotation]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return plugin.newDeleterInternal(spec, &azureSvc{}, azure, resourceGroup)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) newDeleterInternal(spec *volume.Spec, util azureUtil, azure azureCloudProvider, resourceGroup string) (volume.Deleter, error) {
 | 
				
			||||||
 | 
						if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureFile == nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("invalid PV spec")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						shareName := spec.PersistentVolume.Spec.AzureFile.ShareName
 | 
				
			||||||
 | 
						if accountName, _, err := util.GetAzureCredentials(plugin.host, secretNamespace, secretName); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return &azureFileDeleter{
 | 
				
			||||||
 | 
								azureFile: &azureFile{
 | 
				
			||||||
 | 
									volName: spec.Name(),
 | 
				
			||||||
 | 
									plugin:  plugin,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								resourceGroup: resourceGroup,
 | 
				
			||||||
 | 
								shareName:     shareName,
 | 
				
			||||||
 | 
								accountName:   accountName,
 | 
				
			||||||
 | 
								azureProvider: azure,
 | 
				
			||||||
 | 
							}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) NewProvisioner(logger klog.Logger, options volume.VolumeOptions) (volume.Provisioner, error) {
 | 
				
			||||||
 | 
						azure, resourceGroup, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							klog.V(4).Infof("failed to get azure provider")
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(options.PVC.Spec.AccessModes) == 0 {
 | 
				
			||||||
 | 
							options.PVC.Spec.AccessModes = plugin.GetAccessModes()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resourceGroup != "" {
 | 
				
			||||||
 | 
							options.PVC.ObjectMeta.Annotations[resourceGroupAnnotation] = resourceGroup
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return plugin.newProvisionerInternal(options, azure)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (plugin *azureFilePlugin) newProvisionerInternal(options volume.VolumeOptions, azure azureCloudProvider) (volume.Provisioner, error) {
 | 
				
			||||||
 | 
						return &azureFileProvisioner{
 | 
				
			||||||
 | 
							azureFile: &azureFile{
 | 
				
			||||||
 | 
								plugin: plugin,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							azureProvider: azure,
 | 
				
			||||||
 | 
							util:          &azureSvc{},
 | 
				
			||||||
 | 
							options:       options,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ volume.Deleter = &azureFileDeleter{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *azureFileDeleter) GetPath() string {
 | 
				
			||||||
 | 
						name := azureFilePluginName
 | 
				
			||||||
 | 
						return f.plugin.host.GetPodVolumeDir(f.podUID, utilstrings.EscapeQualifiedName(name), f.volName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *azureFileDeleter) Delete() error {
 | 
				
			||||||
 | 
						klog.V(4).Infof("deleting volume %s", f.shareName)
 | 
				
			||||||
 | 
						return f.azureProvider.DeleteFileShare(f.resourceGroup, f.accountName, f.shareName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type azureFileProvisioner struct {
 | 
				
			||||||
 | 
						*azureFile
 | 
				
			||||||
 | 
						azureProvider azureCloudProvider
 | 
				
			||||||
 | 
						util          azureUtil
 | 
				
			||||||
 | 
						options       volume.VolumeOptions
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ volume.Provisioner = &azureFileProvisioner{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *azureFileProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
 | 
				
			||||||
 | 
						if !util.ContainsAllAccessModes(a.plugin.GetAccessModes(), a.options.PVC.Spec.AccessModes) {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", a.options.PVC.Spec.AccessModes, a.plugin.GetAccessModes())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if util.CheckPersistentVolumeClaimModeBlock(a.options.PVC) {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("%s does not support block volume provisioning", a.plugin.GetPluginName())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var sku, resourceGroup, location, account, shareName, customTags string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						capacity := a.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
 | 
				
			||||||
 | 
						requestGiB, err := volumehelpers.RoundUpToGiBInt(capacity)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretNamespace := a.options.PVC.Namespace
 | 
				
			||||||
 | 
						// Apply ProvisionerParameters (case-insensitive). We leave validation of
 | 
				
			||||||
 | 
						// the values to the cloud provider.
 | 
				
			||||||
 | 
						for k, v := range a.options.Parameters {
 | 
				
			||||||
 | 
							switch strings.ToLower(k) {
 | 
				
			||||||
 | 
							case "skuname":
 | 
				
			||||||
 | 
								sku = v
 | 
				
			||||||
 | 
							case "location":
 | 
				
			||||||
 | 
								location = v
 | 
				
			||||||
 | 
							case "storageaccount":
 | 
				
			||||||
 | 
								account = v
 | 
				
			||||||
 | 
							case "secretnamespace":
 | 
				
			||||||
 | 
								secretNamespace = v
 | 
				
			||||||
 | 
							case "resourcegroup":
 | 
				
			||||||
 | 
								resourceGroup = v
 | 
				
			||||||
 | 
							case "sharename":
 | 
				
			||||||
 | 
								shareName = v
 | 
				
			||||||
 | 
							case "tags":
 | 
				
			||||||
 | 
								customTags = v
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, a.plugin.GetPluginName())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// TODO: implement c.options.ProvisionerSelector parsing
 | 
				
			||||||
 | 
						if a.options.PVC.Spec.Selector != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on Azure file")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags, err := azure.ConvertTagsToMap(customTags)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if shareName == "" {
 | 
				
			||||||
 | 
							// File share name has a length limit of 63, it cannot contain two consecutive '-'s, and all letters must be lower case.
 | 
				
			||||||
 | 
							name := util.GenerateVolumeName(a.options.ClusterName, a.options.PVName, 63)
 | 
				
			||||||
 | 
							shareName = strings.Replace(name, "--", "-", -1)
 | 
				
			||||||
 | 
							shareName = strings.ToLower(shareName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if resourceGroup == "" {
 | 
				
			||||||
 | 
							resourceGroup = a.options.PVC.ObjectMeta.Annotations[resourceGroupAnnotation]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fileShareSize := int(requestGiB)
 | 
				
			||||||
 | 
						// when use azure file premium, account kind should be specified as FileStorage
 | 
				
			||||||
 | 
						accountKind := string(storage.StorageV2)
 | 
				
			||||||
 | 
						if strings.HasPrefix(strings.ToLower(sku), "premium") {
 | 
				
			||||||
 | 
							accountKind = string(storage.FileStorage)
 | 
				
			||||||
 | 
							// when using azure file premium, the shares have a minimum size
 | 
				
			||||||
 | 
							if fileShareSize < minimumPremiumShareSize {
 | 
				
			||||||
 | 
								fileShareSize = minimumPremiumShareSize
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						accountOptions := &azure.AccountOptions{
 | 
				
			||||||
 | 
							Name:          account,
 | 
				
			||||||
 | 
							Type:          sku,
 | 
				
			||||||
 | 
							Kind:          accountKind,
 | 
				
			||||||
 | 
							ResourceGroup: resourceGroup,
 | 
				
			||||||
 | 
							Location:      location,
 | 
				
			||||||
 | 
							Tags:          tags,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shareOptions := &fileclient.ShareOptions{
 | 
				
			||||||
 | 
							Name:       shareName,
 | 
				
			||||||
 | 
							Protocol:   storage.SMB,
 | 
				
			||||||
 | 
							RequestGiB: fileShareSize,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						account, key, err := a.azureProvider.CreateFileShare(accountOptions, shareOptions)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// create a secret for storage account and key
 | 
				
			||||||
 | 
						secretName, err := a.util.SetAzureCredentials(a.plugin.host, secretNamespace, account, key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// create PV
 | 
				
			||||||
 | 
						pv := &v1.PersistentVolume{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:   a.options.PVName,
 | 
				
			||||||
 | 
								Labels: map[string]string{},
 | 
				
			||||||
 | 
								Annotations: map[string]string{
 | 
				
			||||||
 | 
									util.VolumeDynamicallyCreatedByKey: "azure-file-dynamic-provisioner",
 | 
				
			||||||
 | 
									resourceGroupAnnotation:            resourceGroup,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: v1.PersistentVolumeSpec{
 | 
				
			||||||
 | 
								PersistentVolumeReclaimPolicy: a.options.PersistentVolumeReclaimPolicy,
 | 
				
			||||||
 | 
								AccessModes:                   a.options.PVC.Spec.AccessModes,
 | 
				
			||||||
 | 
								Capacity: v1.ResourceList{
 | 
				
			||||||
 | 
									v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", fileShareSize)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								PersistentVolumeSource: v1.PersistentVolumeSource{
 | 
				
			||||||
 | 
									AzureFile: &v1.AzureFilePersistentVolumeSource{
 | 
				
			||||||
 | 
										SecretName:      secretName,
 | 
				
			||||||
 | 
										ShareName:       shareName,
 | 
				
			||||||
 | 
										SecretNamespace: &secretNamespace,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								MountOptions: a.options.MountOptions,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return pv, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return cloud provider
 | 
				
			||||||
 | 
					func getAzureCloudProvider(cloudProvider cloudprovider.Interface) (azureCloudProvider, string, error) {
 | 
				
			||||||
 | 
						azureCloudProvider, ok := cloudProvider.(*azure.Cloud)
 | 
				
			||||||
 | 
						if !ok || azureCloudProvider == nil {
 | 
				
			||||||
 | 
							return nil, "", fmt.Errorf("failed to get Azure Cloud Provider. GetCloudProvider returned %v instead", cloudProvider)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return azureCloudProvider, azureCloudProvider.ResourceGroup, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										162
									
								
								pkg/volume/azure_file/azure_util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								pkg/volume/azure_file/azure_util.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
				
			|||||||
 | 
					//go:build !providerless
 | 
				
			||||||
 | 
					// +build !providerless
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2016 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 azure_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						fileMode        = "file_mode"
 | 
				
			||||||
 | 
						dirMode         = "dir_mode"
 | 
				
			||||||
 | 
						gid             = "gid"
 | 
				
			||||||
 | 
						vers            = "vers"
 | 
				
			||||||
 | 
						actimeo         = "actimeo"
 | 
				
			||||||
 | 
						mfsymlinks      = "mfsymlinks"
 | 
				
			||||||
 | 
						defaultFileMode = "0777"
 | 
				
			||||||
 | 
						defaultDirMode  = "0777"
 | 
				
			||||||
 | 
						defaultVers     = "3.0"
 | 
				
			||||||
 | 
						defaultActimeo  = "30"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Abstract interface to azure file operations.
 | 
				
			||||||
 | 
					type azureUtil interface {
 | 
				
			||||||
 | 
						GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName string) (string, string, error)
 | 
				
			||||||
 | 
						SetAzureCredentials(host volume.VolumeHost, nameSpace, accountName, accountKey string) (string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type azureSvc struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *azureSvc) GetAzureCredentials(host volume.VolumeHost, nameSpace, secretName string) (string, string, error) {
 | 
				
			||||||
 | 
						var accountKey, accountName string
 | 
				
			||||||
 | 
						kubeClient := host.GetKubeClient()
 | 
				
			||||||
 | 
						if kubeClient == nil {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("cannot get kube client")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						keys, err := kubeClient.CoreV1().Secrets(nameSpace).Get(context.TODO(), secretName, metav1.GetOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("couldn't get secret %v/%v", nameSpace, secretName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for name, data := range keys.Data {
 | 
				
			||||||
 | 
							if name == "azurestorageaccountname" {
 | 
				
			||||||
 | 
								accountName = string(data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if name == "azurestorageaccountkey" {
 | 
				
			||||||
 | 
								accountKey = string(data)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if accountName == "" || accountKey == "" {
 | 
				
			||||||
 | 
							return "", "", fmt.Errorf("invalid %v/%v, couldn't extract azurestorageaccountname or azurestorageaccountkey", nameSpace, secretName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						accountName = strings.TrimSpace(accountName)
 | 
				
			||||||
 | 
						return accountName, accountKey, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *azureSvc) SetAzureCredentials(host volume.VolumeHost, nameSpace, accountName, accountKey string) (string, error) {
 | 
				
			||||||
 | 
						kubeClient := host.GetKubeClient()
 | 
				
			||||||
 | 
						if kubeClient == nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("cannot get kube client")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						secretName := "azure-storage-account-" + accountName + "-secret"
 | 
				
			||||||
 | 
						secret := &v1.Secret{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Namespace: nameSpace,
 | 
				
			||||||
 | 
								Name:      secretName,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Data: map[string][]byte{
 | 
				
			||||||
 | 
								"azurestorageaccountname": []byte(accountName),
 | 
				
			||||||
 | 
								"azurestorageaccountkey":  []byte(accountKey),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Type: "Opaque",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err := kubeClient.CoreV1().Secrets(nameSpace).Create(context.TODO(), secret, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						if errors.IsAlreadyExists(err) {
 | 
				
			||||||
 | 
							err = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("couldn't create secret %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return secretName, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// check whether mountOptions contain file_mode, dir_mode, vers, gid, if not, append default mode
 | 
				
			||||||
 | 
					func appendDefaultMountOptions(mountOptions []string, fsGroup *int64) []string {
 | 
				
			||||||
 | 
						fileModeFlag := false
 | 
				
			||||||
 | 
						dirModeFlag := false
 | 
				
			||||||
 | 
						versFlag := false
 | 
				
			||||||
 | 
						gidFlag := false
 | 
				
			||||||
 | 
						actimeoFlag := false
 | 
				
			||||||
 | 
						mfsymlinksFlag := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, mountOption := range mountOptions {
 | 
				
			||||||
 | 
							if strings.HasPrefix(mountOption, fileMode) {
 | 
				
			||||||
 | 
								fileModeFlag = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if strings.HasPrefix(mountOption, dirMode) {
 | 
				
			||||||
 | 
								dirModeFlag = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if strings.HasPrefix(mountOption, vers) {
 | 
				
			||||||
 | 
								versFlag = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if strings.HasPrefix(mountOption, gid) {
 | 
				
			||||||
 | 
								gidFlag = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if strings.HasPrefix(mountOption, actimeo) {
 | 
				
			||||||
 | 
								actimeoFlag = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if strings.HasPrefix(mountOption, mfsymlinks) {
 | 
				
			||||||
 | 
								mfsymlinksFlag = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						allMountOptions := mountOptions
 | 
				
			||||||
 | 
						if !fileModeFlag {
 | 
				
			||||||
 | 
							allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", fileMode, defaultFileMode))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !dirModeFlag {
 | 
				
			||||||
 | 
							allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", dirMode, defaultDirMode))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !versFlag {
 | 
				
			||||||
 | 
							allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", vers, defaultVers))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !gidFlag && fsGroup != nil {
 | 
				
			||||||
 | 
							allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%d", gid, *fsGroup))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !actimeoFlag {
 | 
				
			||||||
 | 
							allMountOptions = append(allMountOptions, fmt.Sprintf("%s=%s", actimeo, defaultActimeo))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !mfsymlinksFlag {
 | 
				
			||||||
 | 
							allMountOptions = append(allMountOptions, mfsymlinks)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return allMountOptions
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								pkg/volume/azure_file/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								pkg/volume/azure_file/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2016 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 azure_file contains the internal representation of
 | 
				
			||||||
 | 
					// Azure File Service Volume
 | 
				
			||||||
 | 
					package azure_file // import "k8s.io/kubernetes/pkg/volume/azure_file"
 | 
				
			||||||
@@ -229,7 +229,7 @@ func (p *csiPlugin) Init(host volume.VolumeHost) error {
 | 
				
			|||||||
			return true
 | 
								return true
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		csitranslationplugins.AzureFileInTreePluginName: func() bool {
 | 
							csitranslationplugins.AzureFileInTreePluginName: func() bool {
 | 
				
			||||||
			return true
 | 
								return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFile)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		csitranslationplugins.VSphereInTreePluginName: func() bool {
 | 
							csitranslationplugins.VSphereInTreePluginName: func() bool {
 | 
				
			||||||
			return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
 | 
								return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationvSphere)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,7 +93,7 @@ func (pm PluginManager) IsMigrationEnabledForPlugin(pluginName string) bool {
 | 
				
			|||||||
	case csilibplugins.GCEPDInTreePluginName:
 | 
						case csilibplugins.GCEPDInTreePluginName:
 | 
				
			||||||
		return pm.featureGate.Enabled(features.CSIMigrationGCE)
 | 
							return pm.featureGate.Enabled(features.CSIMigrationGCE)
 | 
				
			||||||
	case csilibplugins.AzureFileInTreePluginName:
 | 
						case csilibplugins.AzureFileInTreePluginName:
 | 
				
			||||||
		return true
 | 
							return pm.featureGate.Enabled(features.CSIMigrationAzureFile)
 | 
				
			||||||
	case csilibplugins.AzureDiskInTreePluginName:
 | 
						case csilibplugins.AzureDiskInTreePluginName:
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	case csilibplugins.CinderInTreePluginName:
 | 
						case csilibplugins.CinderInTreePluginName:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user