mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			361 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
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_dd
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-10-01/compute"
 | 
						|
	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-07-01/storage"
 | 
						|
	"k8s.io/klog"
 | 
						|
 | 
						|
	"k8s.io/api/core/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						|
	"k8s.io/apimachinery/pkg/types"
 | 
						|
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						|
	"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
 | 
						|
	"k8s.io/kubernetes/pkg/volume"
 | 
						|
	"k8s.io/kubernetes/pkg/volume/util"
 | 
						|
)
 | 
						|
 | 
						|
// interface exposed by the cloud provider implementing Disk functionality
 | 
						|
type DiskController interface {
 | 
						|
	CreateBlobDisk(dataDiskName string, storageAccountType storage.SkuName, sizeGB int) (string, error)
 | 
						|
	DeleteBlobDisk(diskUri string) error
 | 
						|
 | 
						|
	CreateManagedDisk(options *azure.ManagedDiskOptions) (string, error)
 | 
						|
	DeleteManagedDisk(diskURI string) error
 | 
						|
 | 
						|
	// Attaches the disk to the host machine.
 | 
						|
	AttachDisk(isManagedDisk bool, diskName, diskUri string, nodeName types.NodeName, cachingMode compute.CachingTypes) error
 | 
						|
	// Detaches the disk, identified by disk name or uri, from the host machine.
 | 
						|
	DetachDisk(diskName, diskUri string, nodeName types.NodeName) error
 | 
						|
 | 
						|
	// Check if a list of volumes are attached to the node with the specified NodeName
 | 
						|
	DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error)
 | 
						|
 | 
						|
	// Get the LUN number of the disk that is attached to the host
 | 
						|
	GetDiskLun(diskName, diskUri string, nodeName types.NodeName) (int32, error)
 | 
						|
	// Get the next available LUN number to attach a new VHD
 | 
						|
	GetNextDiskLun(nodeName types.NodeName) (int32, error)
 | 
						|
 | 
						|
	// Create a VHD blob
 | 
						|
	CreateVolume(name, storageAccount, storageAccountType, location string, requestGB int) (string, string, int, error)
 | 
						|
	// Delete a VHD blob
 | 
						|
	DeleteVolume(diskURI string) error
 | 
						|
 | 
						|
	// Expand the disk to new size
 | 
						|
	ResizeDisk(diskURI string, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error)
 | 
						|
 | 
						|
	// GetAzureDiskLabels gets availability zone labels for Azuredisk.
 | 
						|
	GetAzureDiskLabels(diskURI string) (map[string]string, error)
 | 
						|
 | 
						|
	// GetActiveZones returns all the zones in which k8s nodes are currently running.
 | 
						|
	GetActiveZones() (sets.String, error)
 | 
						|
 | 
						|
	// GetLocation returns the location in which k8s cluster is currently running.
 | 
						|
	GetLocation() string
 | 
						|
}
 | 
						|
 | 
						|
type azureDataDiskPlugin struct {
 | 
						|
	host volume.VolumeHost
 | 
						|
}
 | 
						|
 | 
						|
var _ volume.VolumePlugin = &azureDataDiskPlugin{}
 | 
						|
var _ volume.PersistentVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
var _ volume.DeletableVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
var _ volume.ProvisionableVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
var _ volume.AttachableVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
var _ volume.VolumePluginWithAttachLimits = &azureDataDiskPlugin{}
 | 
						|
var _ volume.ExpandableVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
var _ volume.DeviceMountableVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
 | 
						|
// store vm size list in current region
 | 
						|
var vmSizeList *[]compute.VirtualMachineSize
 | 
						|
 | 
						|
const (
 | 
						|
	azureDataDiskPluginName = "kubernetes.io/azure-disk"
 | 
						|
	defaultAzureVolumeLimit = 16
 | 
						|
)
 | 
						|
 | 
						|
func ProbeVolumePlugins() []volume.VolumePlugin {
 | 
						|
	return []volume.VolumePlugin{&azureDataDiskPlugin{}}
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) Init(host volume.VolumeHost) error {
 | 
						|
	plugin.host = host
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) GetPluginName() string {
 | 
						|
	return azureDataDiskPluginName
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
 | 
						|
	volumeSource, _, err := getVolumeSource(spec)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	return volumeSource.DataDiskURI, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) CanSupport(spec *volume.Spec) bool {
 | 
						|
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureDisk != nil) ||
 | 
						|
		(spec.Volume != nil && spec.Volume.AzureDisk != nil)
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) IsMigratedToCSI() bool {
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) RequiresRemount() bool {
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) SupportsMountOption() bool {
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) SupportsBulkVolumeVerification() bool {
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) GetVolumeLimits() (map[string]int64, error) {
 | 
						|
	volumeLimits := map[string]int64{
 | 
						|
		util.AzureVolumeLimitKey: defaultAzureVolumeLimit,
 | 
						|
	}
 | 
						|
 | 
						|
	az, err := getCloud(plugin.host)
 | 
						|
	if err != nil {
 | 
						|
		// if we can't fetch cloudprovider we return an error
 | 
						|
		// hoping external CCM or admin can set it. Returning
 | 
						|
		// default values from here will mean, no one can
 | 
						|
		// override them.
 | 
						|
		return nil, fmt.Errorf("failed to get azure cloud in GetVolumeLimits, plugin.host: %s", plugin.host.GetHostName())
 | 
						|
	}
 | 
						|
 | 
						|
	instances, ok := az.Instances()
 | 
						|
	if !ok {
 | 
						|
		klog.Warningf("Failed to get instances from cloud provider")
 | 
						|
		return volumeLimits, nil
 | 
						|
	}
 | 
						|
 | 
						|
	instanceType, err := instances.InstanceType(context.TODO(), plugin.host.GetNodeName())
 | 
						|
	if err != nil {
 | 
						|
		klog.Errorf("Failed to get instance type from Azure cloud provider, nodeName: %s", plugin.host.GetNodeName())
 | 
						|
		return volumeLimits, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if vmSizeList == nil {
 | 
						|
		ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
 | 
						|
		defer cancel()
 | 
						|
		result, err := az.VirtualMachineSizesClient.List(ctx, az.Location)
 | 
						|
		if err != nil || result.Value == nil {
 | 
						|
			klog.Errorf("failed to list vm sizes in GetVolumeLimits, plugin.host: %s, location: %s", plugin.host.GetHostName(), az.Location)
 | 
						|
			return volumeLimits, nil
 | 
						|
		}
 | 
						|
		vmSizeList = result.Value
 | 
						|
	}
 | 
						|
 | 
						|
	volumeLimits = map[string]int64{
 | 
						|
		util.AzureVolumeLimitKey: getMaxDataDiskCount(instanceType, vmSizeList),
 | 
						|
	}
 | 
						|
 | 
						|
	return volumeLimits, nil
 | 
						|
}
 | 
						|
 | 
						|
func getMaxDataDiskCount(instanceType string, sizeList *[]compute.VirtualMachineSize) int64 {
 | 
						|
	if sizeList == nil {
 | 
						|
		return defaultAzureVolumeLimit
 | 
						|
	}
 | 
						|
 | 
						|
	vmsize := strings.ToUpper(instanceType)
 | 
						|
	for _, size := range *sizeList {
 | 
						|
		if size.Name == nil || size.MaxDataDiskCount == nil {
 | 
						|
			klog.Errorf("failed to get vm size in getMaxDataDiskCount")
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if strings.ToUpper(*size.Name) == vmsize {
 | 
						|
			klog.V(12).Infof("got a matching size in getMaxDataDiskCount, Name: %s, MaxDataDiskCount: %d", *size.Name, *size.MaxDataDiskCount)
 | 
						|
			return int64(*size.MaxDataDiskCount)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return defaultAzureVolumeLimit
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) VolumeLimitKey(spec *volume.Spec) string {
 | 
						|
	return util.AzureVolumeLimitKey
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
 | 
						|
	return []v1.PersistentVolumeAccessMode{
 | 
						|
		v1.ReadWriteOnce,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// NewAttacher initializes an Attacher
 | 
						|
func (plugin *azureDataDiskPlugin) NewAttacher() (volume.Attacher, error) {
 | 
						|
	azure, err := getCloud(plugin.host)
 | 
						|
	if err != nil {
 | 
						|
		klog.Errorf("failed to get azure cloud in NewAttacher, plugin.host : %s, err:%v", plugin.host.GetHostName(), err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &azureDiskAttacher{
 | 
						|
		plugin: plugin,
 | 
						|
		cloud:  azure,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewDetacher() (volume.Detacher, error) {
 | 
						|
	azure, err := getCloud(plugin.host)
 | 
						|
	if err != nil {
 | 
						|
		klog.V(4).Infof("failed to get azure cloud in NewDetacher, plugin.host : %s", plugin.host.GetHostName())
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &azureDiskDetacher{
 | 
						|
		plugin: plugin,
 | 
						|
		cloud:  azure,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) CanAttach(spec *volume.Spec) (bool, error) {
 | 
						|
	return true, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
 | 
						|
	return true, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
 | 
						|
	volumeSource, _, err := getVolumeSource(spec)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	disk := makeDataDisk(spec.Name(), "", volumeSource.DiskName, plugin.host, plugin)
 | 
						|
 | 
						|
	return &azureDiskDeleter{
 | 
						|
		spec:     spec,
 | 
						|
		plugin:   plugin,
 | 
						|
		dataDisk: disk,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
 | 
						|
	if len(options.PVC.Spec.AccessModes) == 0 {
 | 
						|
		options.PVC.Spec.AccessModes = plugin.GetAccessModes()
 | 
						|
	}
 | 
						|
 | 
						|
	return &azureDiskProvisioner{
 | 
						|
		plugin:  plugin,
 | 
						|
		options: options,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, options volume.VolumeOptions) (volume.Mounter, error) {
 | 
						|
	volumeSource, _, err := getVolumeSource(spec)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	disk := makeDataDisk(spec.Name(), pod.UID, volumeSource.DiskName, plugin.host, plugin)
 | 
						|
 | 
						|
	return &azureDiskMounter{
 | 
						|
		plugin:   plugin,
 | 
						|
		spec:     spec,
 | 
						|
		options:  options,
 | 
						|
		dataDisk: disk,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
 | 
						|
	disk := makeDataDisk(volName, podUID, "", plugin.host, plugin)
 | 
						|
 | 
						|
	return &azureDiskUnmounter{
 | 
						|
		plugin:   plugin,
 | 
						|
		dataDisk: disk,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) RequiresFSResize() bool {
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) ExpandVolumeDevice(
 | 
						|
	spec *volume.Spec,
 | 
						|
	newSize resource.Quantity,
 | 
						|
	oldSize resource.Quantity) (resource.Quantity, error) {
 | 
						|
	if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.AzureDisk == nil {
 | 
						|
		return oldSize, fmt.Errorf("invalid PV spec")
 | 
						|
	}
 | 
						|
 | 
						|
	diskController, err := getDiskController(plugin.host)
 | 
						|
	if err != nil {
 | 
						|
		return oldSize, err
 | 
						|
	}
 | 
						|
 | 
						|
	return diskController.ResizeDisk(spec.PersistentVolume.Spec.AzureDisk.DataDiskURI, oldSize, newSize)
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) {
 | 
						|
	_, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath)
 | 
						|
	if err != nil {
 | 
						|
		return false, err
 | 
						|
	}
 | 
						|
	return true, nil
 | 
						|
}
 | 
						|
 | 
						|
var _ volume.NodeExpandableVolumePlugin = &azureDataDiskPlugin{}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
 | 
						|
	mounter := plugin.host.GetMounter(plugin.GetPluginName())
 | 
						|
	pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
 | 
						|
	sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	azureVolume := &v1.Volume{
 | 
						|
		Name: volumeName,
 | 
						|
		VolumeSource: v1.VolumeSource{
 | 
						|
			AzureDisk: &v1.AzureDiskVolumeSource{
 | 
						|
				DataDiskURI: sourceName,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	return volume.NewSpecFromVolume(azureVolume), nil
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
 | 
						|
	m := plugin.host.GetMounter(plugin.GetPluginName())
 | 
						|
	return m.GetMountRefs(deviceMountPath)
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
 | 
						|
	return plugin.NewAttacher()
 | 
						|
}
 | 
						|
 | 
						|
func (plugin *azureDataDiskPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
 | 
						|
	return plugin.NewDetacher()
 | 
						|
}
 |