2016-10-17 14:50:00 +00:00
/ *
2017-07-13 11:55:32 +00:00
Copyright 2017 The Kubernetes Authors .
2016-10-17 14:50:00 +00:00
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 (
2018-07-02 07:49:40 +00:00
"errors"
2016-10-17 14:50:00 +00:00
"fmt"
2018-07-23 08:38:47 +00:00
"strconv"
2016-10-17 14:50:00 +00:00
"strings"
2018-10-31 09:27:45 +00:00
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2018-07-01/storage"
2017-06-22 18:24:23 +00:00
"k8s.io/api/core/v1"
2017-01-25 13:13:07 +00:00
"k8s.io/apimachinery/pkg/api/resource"
2017-01-17 03:38:19 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-08-08 06:40:40 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2018-05-29 10:02:40 +00:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2019-02-05 18:44:45 +00:00
volumehelpers "k8s.io/cloud-provider/volume/helpers"
2018-07-23 08:38:47 +00:00
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
2018-05-29 10:02:40 +00:00
"k8s.io/kubernetes/pkg/features"
2016-10-17 14:50:00 +00:00
"k8s.io/kubernetes/pkg/volume"
2018-02-06 08:38:41 +00:00
"k8s.io/kubernetes/pkg/volume/util"
2016-10-17 14:50:00 +00:00
)
2017-07-13 11:55:32 +00:00
type azureDiskProvisioner struct {
plugin * azureDataDiskPlugin
options volume . VolumeOptions
}
2016-10-17 14:50:00 +00:00
type azureDiskDeleter struct {
2017-07-13 11:55:32 +00:00
* dataDisk
spec * volume . Spec
plugin * azureDataDiskPlugin
2016-10-17 14:50:00 +00:00
}
2017-07-13 11:55:32 +00:00
var _ volume . Provisioner = & azureDiskProvisioner { }
var _ volume . Deleter = & azureDiskDeleter { }
2016-10-17 14:50:00 +00:00
2017-07-13 11:55:32 +00:00
func ( d * azureDiskDeleter ) GetPath ( ) string {
return getPath ( d . podUID , d . dataDisk . diskName , d . plugin . host )
2016-10-17 14:50:00 +00:00
}
2017-07-13 11:55:32 +00:00
func ( d * azureDiskDeleter ) Delete ( ) error {
2018-05-07 09:14:40 +00:00
volumeSource , _ , err := getVolumeSource ( d . spec )
2017-07-13 11:55:32 +00:00
if err != nil {
return err
2016-10-17 14:50:00 +00:00
}
2017-07-13 11:55:32 +00:00
diskController , err := getDiskController ( d . plugin . host )
2016-10-17 14:50:00 +00:00
if err != nil {
2017-07-13 11:55:32 +00:00
return err
2016-10-17 14:50:00 +00:00
}
2017-07-13 11:55:32 +00:00
managed := ( * volumeSource . Kind == v1 . AzureManagedDisk )
2016-10-17 14:50:00 +00:00
2017-07-13 11:55:32 +00:00
if managed {
return diskController . DeleteManagedDisk ( volumeSource . DataDiskURI )
}
2016-10-17 14:50:00 +00:00
2017-10-27 08:52:46 +00:00
return diskController . DeleteBlobDisk ( volumeSource . DataDiskURI )
2016-10-17 14:50:00 +00:00
}
2018-07-23 08:38:47 +00:00
// parseZoned parsed 'zoned' for storage class. If zoned is not specified (empty string),
// then it defaults to true for managed disks.
func parseZoned ( zonedString string , kind v1 . AzureDataDiskKind ) ( bool , error ) {
if zonedString == "" {
return kind == v1 . AzureManagedDisk , nil
}
zoned , err := strconv . ParseBool ( zonedString )
if err != nil {
return false , fmt . Errorf ( "failed to parse 'zoned': %v" , err )
}
2018-07-24 06:24:27 +00:00
if zoned && kind != v1 . AzureManagedDisk {
return false , fmt . Errorf ( "zoned is only supported by managed disks" )
}
2018-07-23 08:38:47 +00:00
return zoned , nil
}
2018-05-23 08:12:20 +00:00
func ( p * azureDiskProvisioner ) Provision ( selectedNode * v1 . Node , allowedTopologies [ ] v1 . TopologySelectorTerm ) ( * v1 . PersistentVolume , error ) {
2018-02-06 08:38:41 +00:00
if ! util . AccessModesContainedInAll ( p . plugin . GetAccessModes ( ) , p . options . PVC . Spec . AccessModes ) {
2017-07-13 11:55:32 +00:00
return nil , fmt . Errorf ( "invalid AccessModes %v: only AccessModes %v are supported" , p . options . PVC . Spec . AccessModes , p . plugin . GetAccessModes ( ) )
}
supportedModes := p . plugin . GetAccessModes ( )
2016-10-17 14:50:00 +00:00
2017-07-13 11:55:32 +00:00
// perform static validation first
if p . options . PVC . Spec . Selector != nil {
return nil , fmt . Errorf ( "azureDisk - claim.Spec.Selector is not supported for dynamic provisioning on Azure disk" )
}
2016-10-17 14:50:00 +00:00
2017-07-13 11:55:32 +00:00
if len ( p . options . PVC . Spec . AccessModes ) > 1 {
return nil , fmt . Errorf ( "AzureDisk - multiple access modes are not supported on AzureDisk plugin" )
2017-06-09 21:59:08 +00:00
}
2017-07-13 11:55:32 +00:00
if len ( p . options . PVC . Spec . AccessModes ) == 1 {
if p . options . PVC . Spec . AccessModes [ 0 ] != supportedModes [ 0 ] {
2018-07-23 08:38:47 +00:00
return nil , fmt . Errorf ( "AzureDisk - mode %s is not supported by AzureDisk plugin (supported mode is %s)" , p . options . PVC . Spec . AccessModes [ 0 ] , supportedModes )
2017-07-13 11:55:32 +00:00
}
}
2016-10-17 14:50:00 +00:00
2017-07-13 11:55:32 +00:00
var (
location , account string
storageAccountType , fsType string
cachingMode v1 . AzureDataDiskCachingMode
strKind string
err error
2018-06-25 15:39:59 +00:00
resourceGroup string
2018-07-23 08:38:47 +00:00
2018-08-08 06:40:40 +00:00
zoned bool
zonePresent bool
zonesPresent bool
strZoned string
availabilityZone string
availabilityZones sets . String
selectedAvailabilityZone string
2018-10-31 09:27:45 +00:00
diskIopsReadWrite string
diskMbpsReadWrite string
2017-07-13 11:55:32 +00:00
)
2017-01-15 13:53:52 +00:00
// maxLength = 79 - (4 for ".vhd") = 75
2018-02-06 08:38:41 +00:00
name := util . GenerateVolumeName ( p . options . ClusterName , p . options . PVName , 75 )
2017-07-13 11:55:32 +00:00
capacity := p . options . PVC . Spec . Resources . Requests [ v1 . ResourceName ( v1 . ResourceStorage ) ]
2019-02-05 18:44:45 +00:00
requestGiB , err := volumehelpers . RoundUpToGiBInt ( capacity )
2018-07-20 19:54:55 +00:00
if err != nil {
return nil , err
}
2016-10-17 14:50:00 +00:00
2017-07-13 11:55:32 +00:00
for k , v := range p . options . Parameters {
2016-10-17 14:50:00 +00:00
switch strings . ToLower ( k ) {
case "skuname" :
2017-07-13 11:55:32 +00:00
storageAccountType = v
2016-10-17 14:50:00 +00:00
case "location" :
location = v
case "storageaccount" :
account = v
2017-07-13 11:55:32 +00:00
case "storageaccounttype" :
storageAccountType = v
case "kind" :
strKind = v
case "cachingmode" :
cachingMode = v1 . AzureDataDiskCachingMode ( v )
2017-02-01 14:23:10 +00:00
case volume . VolumeParameterFSType :
2017-07-13 11:55:32 +00:00
fsType = strings . ToLower ( v )
2018-06-25 15:39:59 +00:00
case "resourcegroup" :
resourceGroup = v
2018-07-23 08:38:47 +00:00
case "zone" :
zonePresent = true
availabilityZone = v
case "zones" :
zonesPresent = true
2019-02-05 18:44:45 +00:00
availabilityZones , err = volumehelpers . ZonesToSet ( v )
2018-08-08 06:40:40 +00:00
if err != nil {
return nil , fmt . Errorf ( "error parsing zones %s, must be strings separated by commas: %v" , v , err )
}
2018-07-23 08:38:47 +00:00
case "zoned" :
strZoned = v
2018-10-31 09:27:45 +00:00
case "diskiopsreadwrite" :
diskIopsReadWrite = v
case "diskmbpsreadwrite" :
diskMbpsReadWrite = v
2016-10-17 14:50:00 +00:00
default :
2017-07-13 11:55:32 +00:00
return nil , fmt . Errorf ( "AzureDisk - invalid option %s in storage class" , k )
2016-10-17 14:50:00 +00:00
}
}
2017-07-13 11:55:32 +00:00
// normalize values
skuName , err := normalizeStorageAccountType ( storageAccountType )
if err != nil {
return nil , err
}
kind , err := normalizeKind ( strFirstLetterToUpper ( strKind ) )
if err != nil {
return nil , err
2016-10-17 14:50:00 +00:00
}
2018-08-09 02:23:53 +00:00
zoned , err = parseZoned ( strZoned , kind )
if err != nil {
return nil , err
}
2018-08-08 06:40:40 +00:00
if kind != v1 . AzureManagedDisk {
if resourceGroup != "" {
return nil , errors . New ( "StorageClass option 'resourceGroup' can be used only for managed disks" )
}
if zoned {
return nil , errors . New ( "StorageClass option 'zoned' parameter is only supported for managed disks" )
}
}
2018-08-09 09:09:39 +00:00
if ! zoned && ( zonePresent || zonesPresent || len ( allowedTopologies ) > 0 ) {
return nil , fmt . Errorf ( "zone, zones and allowedTopologies StorageClass parameters must be used together with zoned parameter" )
2018-07-23 08:38:47 +00:00
}
2017-07-13 11:55:32 +00:00
if cachingMode , err = normalizeCachingMode ( cachingMode ) ; err != nil {
return nil , err
}
diskController , err := getDiskController ( p . plugin . host )
2016-10-17 14:50:00 +00:00
if err != nil {
return nil , err
}
2018-08-08 06:40:40 +00:00
// Select zone for managed disks based on zone, zones and allowedTopologies.
if zoned {
activeZones , err := diskController . GetActiveZones ( )
if err != nil {
return nil , fmt . Errorf ( "error querying active zones: %v" , err )
}
2018-08-30 06:40:07 +00:00
if availabilityZone != "" || availabilityZones . Len ( ) != 0 || activeZones . Len ( ) != 0 || len ( allowedTopologies ) != 0 {
2019-02-05 18:44:45 +00:00
selectedAvailabilityZone , err = volumehelpers . SelectZoneForVolume ( zonePresent , zonesPresent , availabilityZone , availabilityZones , activeZones , selectedNode , allowedTopologies , p . options . PVC . Name )
2018-08-30 06:40:07 +00:00
if err != nil {
return nil , err
}
2018-08-08 06:40:40 +00:00
}
2018-07-02 07:49:40 +00:00
}
2017-07-13 11:55:32 +00:00
// create disk
diskURI := ""
2018-07-23 08:38:47 +00:00
labels := map [ string ] string { }
2017-07-13 11:55:32 +00:00
if kind == v1 . AzureManagedDisk {
2018-05-29 07:45:50 +00:00
tags := make ( map [ string ] string )
if p . options . CloudTags != nil {
tags = * ( p . options . CloudTags )
}
2018-07-23 08:38:47 +00:00
volumeOptions := & azure . ManagedDiskOptions {
DiskName : name ,
StorageAccountType : skuName ,
ResourceGroup : resourceGroup ,
PVCName : p . options . PVC . Name ,
SizeGB : requestGiB ,
Tags : tags ,
2018-08-08 06:40:40 +00:00
AvailabilityZone : selectedAvailabilityZone ,
2018-10-31 09:27:45 +00:00
DiskIOPSReadWrite : diskIopsReadWrite ,
DiskMBpsReadWrite : diskMbpsReadWrite ,
2018-07-23 08:38:47 +00:00
}
diskURI , err = diskController . CreateManagedDisk ( volumeOptions )
if err != nil {
return nil , err
}
labels , err = diskController . GetAzureDiskLabels ( diskURI )
2017-07-13 11:55:32 +00:00
if err != nil {
return nil , err
}
} else {
if kind == v1 . AzureDedicatedBlobDisk {
2018-07-20 19:54:55 +00:00
_ , diskURI , _ , err = diskController . CreateVolume ( name , account , storageAccountType , location , requestGiB )
2017-10-27 08:52:46 +00:00
if err != nil {
return nil , err
2017-07-13 11:55:32 +00:00
}
} else {
2018-10-31 09:27:45 +00:00
diskURI , err = diskController . CreateBlobDisk ( name , storage . SkuName ( storageAccountType ) , requestGiB )
2017-07-13 11:55:32 +00:00
if err != nil {
return nil , err
}
}
}
2018-07-02 12:04:50 +00:00
var volumeMode * v1 . PersistentVolumeMode
if utilfeature . DefaultFeatureGate . Enabled ( features . BlockVolume ) {
volumeMode = p . options . PVC . Spec . VolumeMode
if volumeMode != nil && * volumeMode == v1 . PersistentVolumeBlock {
// Block volumes should not have any FSType
fsType = ""
}
}
2016-11-18 20:58:56 +00:00
pv := & v1 . PersistentVolume {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2017-07-13 11:55:32 +00:00
Name : p . options . PVName ,
2018-07-23 08:38:47 +00:00
Labels : labels ,
2016-10-17 14:50:00 +00:00
Annotations : map [ string ] string {
2017-07-13 11:55:32 +00:00
"volumehelper.VolumeDynamicallyCreatedByKey" : "azure-disk-dynamic-provisioner" ,
2016-10-17 14:50:00 +00:00
} ,
} ,
2016-11-18 20:58:56 +00:00
Spec : v1 . PersistentVolumeSpec {
2017-07-13 11:55:32 +00:00
PersistentVolumeReclaimPolicy : p . options . PersistentVolumeReclaimPolicy ,
AccessModes : supportedModes ,
2016-11-18 20:58:56 +00:00
Capacity : v1 . ResourceList {
2018-07-20 19:54:55 +00:00
v1 . ResourceName ( v1 . ResourceStorage ) : resource . MustParse ( fmt . Sprintf ( "%dGi" , requestGiB ) ) ,
2016-10-17 14:50:00 +00:00
} ,
2018-07-02 12:04:50 +00:00
VolumeMode : volumeMode ,
2016-11-18 20:58:56 +00:00
PersistentVolumeSource : v1 . PersistentVolumeSource {
AzureDisk : & v1 . AzureDiskVolumeSource {
2017-07-13 11:55:32 +00:00
CachingMode : & cachingMode ,
DiskName : name ,
DataDiskURI : diskURI ,
Kind : & kind ,
FSType : & fsType ,
2016-10-17 14:50:00 +00:00
} ,
} ,
2017-08-23 21:29:54 +00:00
MountOptions : p . options . MountOptions ,
2016-10-17 14:50:00 +00:00
} ,
}
2018-05-29 10:02:40 +00:00
2018-12-27 22:45:04 +00:00
nodeSelectorTerms := make ( [ ] v1 . NodeSelectorTerm , 0 )
2018-08-09 09:09:39 +00:00
2018-12-27 22:45:04 +00:00
if zoned {
// Set node affinity labels based on availability zone labels.
if len ( labels ) > 0 {
requirements := make ( [ ] v1 . NodeSelectorRequirement , 0 )
for k , v := range labels {
requirements = append ( requirements , v1 . NodeSelectorRequirement { Key : k , Operator : v1 . NodeSelectorOpIn , Values : [ ] string { v } } )
2018-08-09 09:09:39 +00:00
}
2018-12-27 22:45:04 +00:00
nodeSelectorTerms = append ( nodeSelectorTerms , v1 . NodeSelectorTerm {
MatchExpressions : requirements ,
} )
}
} else {
// Set node affinity labels based on fault domains.
// This is required because unzoned AzureDisk can't be attached to zoned nodes.
// There are at most 3 fault domains available in each region.
// Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
for i := 0 ; i < 3 ; i ++ {
requirements := [ ] v1 . NodeSelectorRequirement {
{
2019-02-01 02:43:49 +00:00
Key : v1 . LabelZoneRegion ,
2018-12-27 22:45:04 +00:00
Operator : v1 . NodeSelectorOpIn ,
Values : [ ] string { diskController . GetLocation ( ) } ,
} ,
{
2019-02-01 02:43:49 +00:00
Key : v1 . LabelZoneFailureDomain ,
2018-12-27 22:45:04 +00:00
Operator : v1 . NodeSelectorOpIn ,
Values : [ ] string { strconv . Itoa ( i ) } ,
2018-08-23 02:37:57 +00:00
} ,
}
2018-12-27 22:45:04 +00:00
nodeSelectorTerms = append ( nodeSelectorTerms , v1 . NodeSelectorTerm {
MatchExpressions : requirements ,
} )
}
}
if len ( nodeSelectorTerms ) > 0 {
pv . Spec . NodeAffinity = & v1 . VolumeNodeAffinity {
Required : & v1 . NodeSelector {
NodeSelectorTerms : nodeSelectorTerms ,
} ,
2018-08-08 06:40:40 +00:00
}
}
2016-10-17 14:50:00 +00:00
return pv , nil
}