azure_file.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // +build !providerless
  2. /*
  3. Copyright 2016 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package azure_file
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "runtime"
  20. "k8s.io/klog"
  21. "k8s.io/utils/mount"
  22. utilstrings "k8s.io/utils/strings"
  23. v1 "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/resource"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. cloudprovider "k8s.io/cloud-provider"
  28. volumehelpers "k8s.io/cloud-provider/volume/helpers"
  29. "k8s.io/kubernetes/pkg/volume"
  30. volutil "k8s.io/kubernetes/pkg/volume/util"
  31. "k8s.io/legacy-cloud-providers/azure"
  32. )
  33. // ProbeVolumePlugins is the primary endpoint for volume plugins
  34. func ProbeVolumePlugins() []volume.VolumePlugin {
  35. return []volume.VolumePlugin{&azureFilePlugin{nil}}
  36. }
  37. type azureFilePlugin struct {
  38. host volume.VolumeHost
  39. }
  40. var _ volume.VolumePlugin = &azureFilePlugin{}
  41. var _ volume.PersistentVolumePlugin = &azureFilePlugin{}
  42. var _ volume.ExpandableVolumePlugin = &azureFilePlugin{}
  43. const (
  44. azureFilePluginName = "kubernetes.io/azure-file"
  45. )
  46. func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
  47. return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(azureFilePluginName), volName)
  48. }
  49. func (plugin *azureFilePlugin) Init(host volume.VolumeHost) error {
  50. plugin.host = host
  51. return nil
  52. }
  53. func (plugin *azureFilePlugin) GetPluginName() string {
  54. return azureFilePluginName
  55. }
  56. func (plugin *azureFilePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  57. share, _, err := getVolumeSource(spec)
  58. if err != nil {
  59. return "", err
  60. }
  61. return share, nil
  62. }
  63. func (plugin *azureFilePlugin) CanSupport(spec *volume.Spec) bool {
  64. //TODO: check if mount.cifs is there
  65. return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureFile != nil) ||
  66. (spec.Volume != nil && spec.Volume.AzureFile != nil)
  67. }
  68. func (plugin *azureFilePlugin) RequiresRemount() bool {
  69. return false
  70. }
  71. func (plugin *azureFilePlugin) SupportsMountOption() bool {
  72. return true
  73. }
  74. func (plugin *azureFilePlugin) SupportsBulkVolumeVerification() bool {
  75. return false
  76. }
  77. func (plugin *azureFilePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  78. return []v1.PersistentVolumeAccessMode{
  79. v1.ReadWriteOnce,
  80. v1.ReadOnlyMany,
  81. v1.ReadWriteMany,
  82. }
  83. }
  84. func (plugin *azureFilePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  85. return plugin.newMounterInternal(spec, pod, &azureSvc{}, plugin.host.GetMounter(plugin.GetPluginName()))
  86. }
  87. func (plugin *azureFilePlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, util azureUtil, mounter mount.Interface) (volume.Mounter, error) {
  88. share, readOnly, err := getVolumeSource(spec)
  89. if err != nil {
  90. return nil, err
  91. }
  92. secretName, secretNamespace, err := getSecretNameAndNamespace(spec, pod.Namespace)
  93. return &azureFileMounter{
  94. azureFile: &azureFile{
  95. volName: spec.Name(),
  96. mounter: mounter,
  97. pod: pod,
  98. plugin: plugin,
  99. MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
  100. },
  101. util: util,
  102. secretNamespace: secretNamespace,
  103. secretName: secretName,
  104. shareName: share,
  105. readOnly: readOnly,
  106. mountOptions: volutil.MountOptionFromSpec(spec),
  107. }, nil
  108. }
  109. func (plugin *azureFilePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  110. return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()))
  111. }
  112. func (plugin *azureFilePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
  113. return &azureFileUnmounter{&azureFile{
  114. volName: volName,
  115. mounter: mounter,
  116. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: podUID}},
  117. plugin: plugin,
  118. MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
  119. }}, nil
  120. }
  121. func (plugin *azureFilePlugin) RequiresFSResize() bool {
  122. return false
  123. }
  124. func (plugin *azureFilePlugin) ExpandVolumeDevice(
  125. spec *volume.Spec,
  126. newSize resource.Quantity,
  127. oldSize resource.Quantity) (resource.Quantity, error) {
  128. if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.AzureFile == nil {
  129. return oldSize, fmt.Errorf("invalid PV spec")
  130. }
  131. shareName := spec.PersistentVolume.Spec.AzureFile.ShareName
  132. azure, err := getAzureCloudProvider(plugin.host.GetCloudProvider())
  133. if err != nil {
  134. return oldSize, err
  135. }
  136. secretName, secretNamespace, err := getSecretNameAndNamespace(spec, spec.PersistentVolume.Spec.ClaimRef.Namespace)
  137. if err != nil {
  138. return oldSize, err
  139. }
  140. accountName, accountKey, err := (&azureSvc{}).GetAzureCredentials(plugin.host, secretNamespace, secretName)
  141. if err != nil {
  142. return oldSize, err
  143. }
  144. if err := azure.ResizeFileShare(accountName, accountKey, shareName, int(volumehelpers.RoundUpToGiB(newSize))); err != nil {
  145. return oldSize, err
  146. }
  147. return newSize, nil
  148. }
  149. func (plugin *azureFilePlugin) ConstructVolumeSpec(volName, mountPath string) (*volume.Spec, error) {
  150. azureVolume := &v1.Volume{
  151. Name: volName,
  152. VolumeSource: v1.VolumeSource{
  153. AzureFile: &v1.AzureFileVolumeSource{
  154. SecretName: volName,
  155. ShareName: volName,
  156. },
  157. },
  158. }
  159. return volume.NewSpecFromVolume(azureVolume), nil
  160. }
  161. // azureFile volumes represent mount of an AzureFile share.
  162. type azureFile struct {
  163. volName string
  164. podUID types.UID
  165. pod *v1.Pod
  166. mounter mount.Interface
  167. plugin *azureFilePlugin
  168. volume.MetricsProvider
  169. }
  170. func (azureFileVolume *azureFile) GetPath() string {
  171. return getPath(azureFileVolume.pod.UID, azureFileVolume.volName, azureFileVolume.plugin.host)
  172. }
  173. type azureFileMounter struct {
  174. *azureFile
  175. util azureUtil
  176. secretName string
  177. secretNamespace string
  178. shareName string
  179. readOnly bool
  180. mountOptions []string
  181. }
  182. var _ volume.Mounter = &azureFileMounter{}
  183. func (b *azureFileMounter) GetAttributes() volume.Attributes {
  184. return volume.Attributes{
  185. ReadOnly: b.readOnly,
  186. Managed: !b.readOnly,
  187. SupportsSELinux: false,
  188. }
  189. }
  190. // Checks prior to mount operations to verify that the required components (binaries, etc.)
  191. // to mount the volume are available on the underlying node.
  192. // If not, it returns an error
  193. func (b *azureFileMounter) CanMount() error {
  194. return nil
  195. }
  196. // SetUp attaches the disk and bind mounts to the volume path.
  197. func (b *azureFileMounter) SetUp(mounterArgs volume.MounterArgs) error {
  198. return b.SetUpAt(b.GetPath(), mounterArgs)
  199. }
  200. func (b *azureFileMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  201. notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
  202. klog.V(4).Infof("AzureFile mount set up: %s %v %v", dir, !notMnt, err)
  203. if err != nil && !os.IsNotExist(err) {
  204. return err
  205. }
  206. if !notMnt {
  207. // testing original mount point, make sure the mount link is valid
  208. if _, err := ioutil.ReadDir(dir); err == nil {
  209. klog.V(4).Infof("azureFile - already mounted to target %s", dir)
  210. return nil
  211. }
  212. // mount link is invalid, now unmount and remount later
  213. klog.Warningf("azureFile - ReadDir %s failed with %v, unmount this directory", dir, err)
  214. if err := b.mounter.Unmount(dir); err != nil {
  215. klog.Errorf("azureFile - Unmount directory %s failed with %v", dir, err)
  216. return err
  217. }
  218. notMnt = true
  219. }
  220. var accountKey, accountName string
  221. if accountName, accountKey, err = b.util.GetAzureCredentials(b.plugin.host, b.secretNamespace, b.secretName); err != nil {
  222. return err
  223. }
  224. mountOptions := []string{}
  225. source := ""
  226. osSeparator := string(os.PathSeparator)
  227. source = fmt.Sprintf("%s%s%s.file.%s%s%s", osSeparator, osSeparator, accountName, getStorageEndpointSuffix(b.plugin.host.GetCloudProvider()), osSeparator, b.shareName)
  228. if runtime.GOOS == "windows" {
  229. mountOptions = []string{fmt.Sprintf("AZURE\\%s", accountName), accountKey}
  230. } else {
  231. if err := os.MkdirAll(dir, 0700); err != nil {
  232. return err
  233. }
  234. // parameters suggested by https://azure.microsoft.com/en-us/documentation/articles/storage-how-to-use-files-linux/
  235. options := []string{fmt.Sprintf("username=%s,password=%s", accountName, accountKey)}
  236. if b.readOnly {
  237. options = append(options, "ro")
  238. }
  239. mountOptions = volutil.JoinMountOptions(b.mountOptions, options)
  240. mountOptions = appendDefaultMountOptions(mountOptions, mounterArgs.FsGroup)
  241. }
  242. err = b.mounter.Mount(source, dir, "cifs", mountOptions)
  243. if err != nil {
  244. notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  245. if mntErr != nil {
  246. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  247. return err
  248. }
  249. if !notMnt {
  250. if mntErr = b.mounter.Unmount(dir); mntErr != nil {
  251. klog.Errorf("Failed to unmount: %v", mntErr)
  252. return err
  253. }
  254. notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  255. if mntErr != nil {
  256. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  257. return err
  258. }
  259. if !notMnt {
  260. // This is very odd, we don't expect it. We'll try again next sync loop.
  261. klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
  262. return err
  263. }
  264. }
  265. os.Remove(dir)
  266. return err
  267. }
  268. return nil
  269. }
  270. var _ volume.Unmounter = &azureFileUnmounter{}
  271. type azureFileUnmounter struct {
  272. *azureFile
  273. }
  274. func (c *azureFileUnmounter) TearDown() error {
  275. return c.TearDownAt(c.GetPath())
  276. }
  277. func (c *azureFileUnmounter) TearDownAt(dir string) error {
  278. return mount.CleanupMountPoint(dir, c.mounter, false)
  279. }
  280. func getVolumeSource(spec *volume.Spec) (string, bool, error) {
  281. if spec.Volume != nil && spec.Volume.AzureFile != nil {
  282. share := spec.Volume.AzureFile.ShareName
  283. readOnly := spec.Volume.AzureFile.ReadOnly
  284. return share, readOnly, nil
  285. } else if spec.PersistentVolume != nil &&
  286. spec.PersistentVolume.Spec.AzureFile != nil {
  287. share := spec.PersistentVolume.Spec.AzureFile.ShareName
  288. readOnly := spec.ReadOnly
  289. return share, readOnly, nil
  290. }
  291. return "", false, fmt.Errorf("Spec does not reference an AzureFile volume type")
  292. }
  293. func getSecretNameAndNamespace(spec *volume.Spec, defaultNamespace string) (string, string, error) {
  294. secretName := ""
  295. secretNamespace := ""
  296. if spec.Volume != nil && spec.Volume.AzureFile != nil {
  297. secretName = spec.Volume.AzureFile.SecretName
  298. secretNamespace = defaultNamespace
  299. } else if spec.PersistentVolume != nil &&
  300. spec.PersistentVolume.Spec.AzureFile != nil {
  301. secretNamespace = defaultNamespace
  302. if spec.PersistentVolume.Spec.AzureFile.SecretNamespace != nil {
  303. secretNamespace = *spec.PersistentVolume.Spec.AzureFile.SecretNamespace
  304. }
  305. secretName = spec.PersistentVolume.Spec.AzureFile.SecretName
  306. } else {
  307. return "", "", fmt.Errorf("Spec does not reference an AzureFile volume type")
  308. }
  309. if len(secretNamespace) == 0 {
  310. return "", "", fmt.Errorf("invalid Azure volume: nil namespace")
  311. }
  312. return secretName, secretNamespace, nil
  313. }
  314. func getAzureCloud(cloudProvider cloudprovider.Interface) (*azure.Cloud, error) {
  315. azure, ok := cloudProvider.(*azure.Cloud)
  316. if !ok || azure == nil {
  317. return nil, fmt.Errorf("Failed to get Azure Cloud Provider. GetCloudProvider returned %v instead", cloudProvider)
  318. }
  319. return azure, nil
  320. }
  321. func getStorageEndpointSuffix(cloudprovider cloudprovider.Interface) string {
  322. const publicCloudStorageEndpointSuffix = "core.windows.net"
  323. azure, err := getAzureCloud(cloudprovider)
  324. if err != nil {
  325. klog.Warningf("No Azure cloud provider found. Using the Azure public cloud endpoint: %s", publicCloudStorageEndpointSuffix)
  326. return publicCloudStorageEndpointSuffix
  327. }
  328. return azure.Environment.StorageEndpointSuffix
  329. }