123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*
- 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"
- 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"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- cloudprovider "k8s.io/cloud-provider"
- volumehelpers "k8s.io/cloud-provider/volume/helpers"
- "k8s.io/klog"
- "k8s.io/kubernetes/pkg/features"
- "k8s.io/kubernetes/pkg/util/mount"
- "k8s.io/kubernetes/pkg/volume"
- volutil "k8s.io/kubernetes/pkg/volume/util"
- "k8s.io/legacy-cloud-providers/azure"
- utilstrings "k8s.io/utils/strings"
- )
- // 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"
- )
- 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) IsMigratedToCSI() bool {
- return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
- utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureFile)
- }
- func (plugin *azureFilePlugin) RequiresRemount() bool {
- return false
- }
- func (plugin *azureFilePlugin) SupportsMountOption() bool {
- return true
- }
- func (plugin *azureFilePlugin) SupportsBulkVolumeVerification() bool {
- return false
- }
- 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)
- 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, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
- if err != nil {
- return oldSize, err
- }
- secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace)
- if err != nil {
- return oldSize, err
- }
- accountName, accountKey, err := (&azureSvc{}).GetAzureCredentials(plugin.host, secretNamespace, secretName)
- if err != nil {
- return oldSize, err
- }
- if err := azure.ResizeFileShare(accountName, accountKey, shareName, int(volumehelpers.RoundUpToGiB(newSize))); err != nil {
- return oldSize, err
- }
- return newSize, nil
- }
- func (plugin *azureFilePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
- azureVolume := &v1.Volume{
- Name: volName,
- VolumeSource: v1.VolumeSource{
- AzureFile: &v1.AzureFileVolumeSource{
- SecretName: volName,
- ShareName: volName,
- },
- },
- }
- return 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,
- SupportsSELinux: false,
- }
- }
- // Checks prior to mount operations to verify that the required components (binaries, etc.)
- // to mount the volume are available on the underlying node.
- // If not, it returns an error
- func (b *azureFileMounter) CanMount() error {
- return nil
- }
- // 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
- }
- notMnt = true
- }
- var accountKey, accountName string
- if accountName, accountKey, err = b.util.GetAzureCredentials(b.plugin.host, b.secretNamespace, b.secretName); err != nil {
- return err
- }
- mountOptions := []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), 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{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)
- }
- err = b.mounter.Mount(source, dir, "cifs", mountOptions)
- 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
- }
|