storageos.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package storageos
  14. import (
  15. "fmt"
  16. "io/ioutil"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "k8s.io/klog"
  21. utilexec "k8s.io/utils/exec"
  22. "k8s.io/utils/mount"
  23. utilstrings "k8s.io/utils/strings"
  24. v1 "k8s.io/api/core/v1"
  25. "k8s.io/apimachinery/pkg/api/resource"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/types"
  28. clientset "k8s.io/client-go/kubernetes"
  29. volumehelpers "k8s.io/cloud-provider/volume/helpers"
  30. "k8s.io/kubernetes/pkg/volume"
  31. "k8s.io/kubernetes/pkg/volume/util"
  32. )
  33. // ProbeVolumePlugins is the primary entrypoint for volume plugins.
  34. func ProbeVolumePlugins() []volume.VolumePlugin {
  35. return []volume.VolumePlugin{&storageosPlugin{nil}}
  36. }
  37. type storageosPlugin struct {
  38. host volume.VolumeHost
  39. }
  40. var _ volume.VolumePlugin = &storageosPlugin{}
  41. var _ volume.PersistentVolumePlugin = &storageosPlugin{}
  42. var _ volume.DeletableVolumePlugin = &storageosPlugin{}
  43. var _ volume.ProvisionableVolumePlugin = &storageosPlugin{}
  44. const (
  45. storageosPluginName = "kubernetes.io/storageos"
  46. defaultDeviceDir = "/var/lib/storageos/volumes"
  47. defaultAPIAddress = "tcp://localhost:5705"
  48. defaultAPIUser = "storageos"
  49. defaultAPIPassword = "storageos"
  50. defaultAPIVersion = "1"
  51. defaultFSType = "ext4"
  52. defaultNamespace = "default"
  53. )
  54. func getPath(uid types.UID, volNamespace string, volName string, pvName string, host volume.VolumeHost) string {
  55. if len(volNamespace) != 0 && len(volName) != 0 && strings.Count(volName, ".") == 0 {
  56. return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(storageosPluginName), pvName+"."+volNamespace+"."+volName)
  57. }
  58. return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(storageosPluginName), pvName)
  59. }
  60. func (plugin *storageosPlugin) Init(host volume.VolumeHost) error {
  61. plugin.host = host
  62. return nil
  63. }
  64. func (plugin *storageosPlugin) GetPluginName() string {
  65. return storageosPluginName
  66. }
  67. func (plugin *storageosPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  68. volumeSource, _, err := getVolumeSource(spec)
  69. if err != nil {
  70. return "", err
  71. }
  72. return fmt.Sprintf("%s/%s", volumeSource.VolumeNamespace, volumeSource.VolumeName), nil
  73. }
  74. func (plugin *storageosPlugin) CanSupport(spec *volume.Spec) bool {
  75. return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS != nil) ||
  76. (spec.Volume != nil && spec.Volume.StorageOS != nil)
  77. }
  78. func (plugin *storageosPlugin) RequiresRemount() bool {
  79. return false
  80. }
  81. func (plugin *storageosPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  82. return []v1.PersistentVolumeAccessMode{
  83. v1.ReadWriteOnce,
  84. v1.ReadOnlyMany,
  85. }
  86. }
  87. func (plugin *storageosPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  88. apiCfg, err := getAPICfg(spec, pod, plugin.host.GetKubeClient())
  89. if err != nil {
  90. return nil, err
  91. }
  92. return plugin.newMounterInternal(spec, pod, apiCfg, &storageosUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
  93. }
  94. func (plugin *storageosPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, apiCfg *storageosAPIConfig, manager storageosManager, mounter mount.Interface, exec utilexec.Interface) (volume.Mounter, error) {
  95. volName, volNamespace, fsType, readOnly, err := getVolumeInfoFromSpec(spec)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return &storageosMounter{
  100. storageos: &storageos{
  101. podUID: pod.UID,
  102. podNamespace: pod.GetNamespace(),
  103. pvName: spec.Name(),
  104. volName: volName,
  105. volNamespace: volNamespace,
  106. fsType: fsType,
  107. readOnly: readOnly,
  108. apiCfg: apiCfg,
  109. manager: manager,
  110. mounter: mounter,
  111. exec: exec,
  112. plugin: plugin,
  113. MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, volNamespace, volName, spec.Name(), plugin.host)),
  114. },
  115. diskMounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
  116. mountOptions: util.MountOptionFromSpec(spec),
  117. }, nil
  118. }
  119. func (plugin *storageosPlugin) NewUnmounter(pvName string, podUID types.UID) (volume.Unmounter, error) {
  120. return plugin.newUnmounterInternal(pvName, podUID, &storageosUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
  121. }
  122. func (plugin *storageosPlugin) newUnmounterInternal(pvName string, podUID types.UID, manager storageosManager, mounter mount.Interface, exec utilexec.Interface) (volume.Unmounter, error) {
  123. // Parse volume namespace & name from mountpoint if mounted
  124. volNamespace, volName, err := getVolumeInfo(pvName, podUID, plugin.host)
  125. if err != nil {
  126. return nil, err
  127. }
  128. return &storageosUnmounter{
  129. storageos: &storageos{
  130. podUID: podUID,
  131. pvName: pvName,
  132. volName: volName,
  133. volNamespace: volNamespace,
  134. manager: manager,
  135. mounter: mounter,
  136. exec: exec,
  137. plugin: plugin,
  138. MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volNamespace, volName, pvName, plugin.host)),
  139. },
  140. }, nil
  141. }
  142. func (plugin *storageosPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
  143. if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS == nil {
  144. return nil, fmt.Errorf("spec.PersistentVolumeSource.StorageOS is nil")
  145. }
  146. class, err := util.GetClassForVolume(plugin.host.GetKubeClient(), spec.PersistentVolume)
  147. if err != nil {
  148. return nil, err
  149. }
  150. var adminSecretName, adminSecretNamespace string
  151. for k, v := range class.Parameters {
  152. switch strings.ToLower(k) {
  153. case "adminsecretname":
  154. adminSecretName = v
  155. case "adminsecretnamespace":
  156. adminSecretNamespace = v
  157. }
  158. }
  159. apiCfg, err := parsePVSecret(adminSecretNamespace, adminSecretName, plugin.host.GetKubeClient())
  160. if err != nil {
  161. return nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
  162. }
  163. return plugin.newDeleterInternal(spec, apiCfg, &storageosUtil{})
  164. }
  165. func (plugin *storageosPlugin) newDeleterInternal(spec *volume.Spec, apiCfg *storageosAPIConfig, manager storageosManager) (volume.Deleter, error) {
  166. return &storageosDeleter{
  167. storageosMounter: &storageosMounter{
  168. storageos: &storageos{
  169. pvName: spec.Name(),
  170. volName: spec.PersistentVolume.Spec.StorageOS.VolumeName,
  171. volNamespace: spec.PersistentVolume.Spec.StorageOS.VolumeNamespace,
  172. apiCfg: apiCfg,
  173. manager: manager,
  174. plugin: plugin,
  175. },
  176. },
  177. pvUID: spec.PersistentVolume.UID,
  178. }, nil
  179. }
  180. func (plugin *storageosPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
  181. return plugin.newProvisionerInternal(options, &storageosUtil{})
  182. }
  183. func (plugin *storageosPlugin) newProvisionerInternal(options volume.VolumeOptions, manager storageosManager) (volume.Provisioner, error) {
  184. return &storageosProvisioner{
  185. storageosMounter: &storageosMounter{
  186. storageos: &storageos{
  187. manager: manager,
  188. plugin: plugin,
  189. },
  190. },
  191. options: options,
  192. }, nil
  193. }
  194. func (plugin *storageosPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  195. volNamespace, volName, err := getVolumeFromRef(volumeName)
  196. if err != nil {
  197. volNamespace = defaultNamespace
  198. volName = volumeName
  199. }
  200. storageosVolume := &v1.Volume{
  201. Name: volumeName,
  202. VolumeSource: v1.VolumeSource{
  203. StorageOS: &v1.StorageOSVolumeSource{
  204. VolumeName: volName,
  205. VolumeNamespace: volNamespace,
  206. },
  207. },
  208. }
  209. return volume.NewSpecFromVolume(storageosVolume), nil
  210. }
  211. func (plugin *storageosPlugin) SupportsMountOption() bool {
  212. return true
  213. }
  214. func (plugin *storageosPlugin) SupportsBulkVolumeVerification() bool {
  215. return false
  216. }
  217. func getVolumeSource(spec *volume.Spec) (*v1.StorageOSVolumeSource, bool, error) {
  218. if spec.Volume != nil && spec.Volume.StorageOS != nil {
  219. return spec.Volume.StorageOS, spec.Volume.StorageOS.ReadOnly, nil
  220. }
  221. return nil, false, fmt.Errorf("Spec does not reference a StorageOS volume type")
  222. }
  223. func getPersistentVolumeSource(spec *volume.Spec) (*v1.StorageOSPersistentVolumeSource, bool, error) {
  224. if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.StorageOS != nil {
  225. return spec.PersistentVolume.Spec.StorageOS, spec.ReadOnly, nil
  226. }
  227. return nil, false, fmt.Errorf("Spec does not reference a StorageOS persistent volume type")
  228. }
  229. // storageosManager is the abstract interface to StorageOS volume ops.
  230. type storageosManager interface {
  231. // Connects to the StorageOS API using the supplied configuration.
  232. NewAPI(apiCfg *storageosAPIConfig) error
  233. // Creates a StorageOS volume.
  234. CreateVolume(provisioner *storageosProvisioner) (*storageosVolume, error)
  235. // Attaches the disk to the kubelet's host machine.
  236. AttachVolume(mounter *storageosMounter) (string, error)
  237. // Attaches the device to the host at a mount path.
  238. AttachDevice(mounter *storageosMounter, deviceMountPath string) error
  239. // Detaches the disk from the kubelet's host machine.
  240. DetachVolume(unmounter *storageosUnmounter, dir string) error
  241. // Mounts the disk on the Kubelet's host machine.
  242. MountVolume(mounter *storageosMounter, mnt, dir string) error
  243. // Unmounts the disk from the Kubelet's host machine.
  244. UnmountVolume(unounter *storageosUnmounter) error
  245. // Deletes the storageos volume. All data will be lost.
  246. DeleteVolume(deleter *storageosDeleter) error
  247. // Gets the node's device path.
  248. DeviceDir(mounter *storageosMounter) string
  249. }
  250. // storageos volumes represent a bare host directory mount of an StorageOS export.
  251. type storageos struct {
  252. podUID types.UID
  253. podNamespace string
  254. pvName string
  255. volName string
  256. volNamespace string
  257. secretName string
  258. readOnly bool
  259. description string
  260. pool string
  261. fsType string
  262. sizeGB int
  263. labels map[string]string
  264. apiCfg *storageosAPIConfig
  265. manager storageosManager
  266. mounter mount.Interface
  267. exec utilexec.Interface
  268. plugin *storageosPlugin
  269. volume.MetricsProvider
  270. }
  271. type storageosMounter struct {
  272. *storageos
  273. // The directory containing the StorageOS devices
  274. deviceDir string
  275. // Interface used to mount the file or block device
  276. diskMounter *mount.SafeFormatAndMount
  277. mountOptions []string
  278. }
  279. var _ volume.Mounter = &storageosMounter{}
  280. func (b *storageosMounter) GetAttributes() volume.Attributes {
  281. return volume.Attributes{
  282. ReadOnly: b.readOnly,
  283. Managed: !b.readOnly,
  284. SupportsSELinux: true,
  285. }
  286. }
  287. // Checks prior to mount operations to verify that the required components (binaries, etc.)
  288. // to mount the volume are available on the underlying node.
  289. // If not, it returns an error
  290. func (b *storageosMounter) CanMount() error {
  291. return nil
  292. }
  293. // SetUp attaches the disk and bind mounts to the volume path.
  294. func (b *storageosMounter) SetUp(mounterArgs volume.MounterArgs) error {
  295. // Need a namespace to find the volume, try pod's namespace if not set.
  296. if b.volNamespace == "" {
  297. klog.V(2).Infof("Setting StorageOS volume namespace to pod namespace: %s", b.podNamespace)
  298. b.volNamespace = b.podNamespace
  299. }
  300. targetPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
  301. // Attach the device to the host.
  302. if err := b.manager.AttachDevice(b, targetPath); err != nil {
  303. klog.Errorf("Failed to attach device at %s: %s", targetPath, err.Error())
  304. return err
  305. }
  306. // Attach the StorageOS volume as a block device
  307. devicePath, err := b.manager.AttachVolume(b)
  308. if err != nil {
  309. klog.Errorf("Failed to attach StorageOS volume %s: %s", b.volName, err.Error())
  310. return err
  311. }
  312. // Mount the loop device into the plugin's disk global mount dir.
  313. err = b.manager.MountVolume(b, devicePath, targetPath)
  314. if err != nil {
  315. return err
  316. }
  317. klog.V(4).Infof("Successfully mounted StorageOS volume %s into global mount directory", b.volName)
  318. // Bind mount the volume into the pod
  319. return b.SetUpAt(b.GetPath(), mounterArgs)
  320. }
  321. // SetUp bind mounts the disk global mount to the give volume path.
  322. func (b *storageosMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  323. notMnt, err := b.mounter.IsLikelyNotMountPoint(dir)
  324. klog.V(4).Infof("StorageOS volume set up: %s %v %v", dir, !notMnt, err)
  325. if err != nil && !os.IsNotExist(err) {
  326. klog.Errorf("Cannot validate mount point: %s %v", dir, err)
  327. return err
  328. }
  329. if !notMnt {
  330. return nil
  331. }
  332. if err = os.MkdirAll(dir, 0750); err != nil {
  333. klog.Errorf("mkdir failed on disk %s (%v)", dir, err)
  334. return err
  335. }
  336. // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
  337. options := []string{"bind"}
  338. if b.readOnly {
  339. options = append(options, "ro")
  340. }
  341. mountOptions := util.JoinMountOptions(b.mountOptions, options)
  342. globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
  343. klog.V(4).Infof("Attempting to bind mount to pod volume at %s", dir)
  344. err = b.mounter.Mount(globalPDPath, dir, "", mountOptions)
  345. if err != nil {
  346. notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  347. if mntErr != nil {
  348. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  349. return err
  350. }
  351. if !notMnt {
  352. if mntErr = b.mounter.Unmount(dir); mntErr != nil {
  353. klog.Errorf("Failed to unmount: %v", mntErr)
  354. return err
  355. }
  356. notMnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  357. if mntErr != nil {
  358. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  359. return err
  360. }
  361. if !notMnt {
  362. klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir)
  363. return err
  364. }
  365. }
  366. os.Remove(dir)
  367. klog.Errorf("Mount of disk %s failed: %v", dir, err)
  368. return err
  369. }
  370. if !b.readOnly {
  371. volume.SetVolumeOwnership(b, mounterArgs.FsGroup)
  372. }
  373. klog.V(4).Infof("StorageOS volume setup complete on %s", dir)
  374. return nil
  375. }
  376. func makeGlobalPDName(host volume.VolumeHost, pvName, volNamespace, volName string) string {
  377. return filepath.Join(host.GetPluginDir(utilstrings.EscapeQualifiedName(storageosPluginName)), util.MountsInGlobalPDPath, pvName+"."+volNamespace+"."+volName)
  378. }
  379. // Given the pod id and PV name, finds the volume's namespace and name from the
  380. // name or volume mount. We mount as volNamespace.pvName, but k8s will specify
  381. // only the pvName to unmount.
  382. // Will return empty volNamespace/pvName if the volume is not mounted.
  383. func getVolumeInfo(pvName string, podUID types.UID, host volume.VolumeHost) (string, string, error) {
  384. if volNamespace, volName, err := getVolumeFromRef(pvName); err == nil {
  385. return volNamespace, volName, nil
  386. }
  387. volumeDir := filepath.Dir(host.GetPodVolumeDir(podUID, utilstrings.EscapeQualifiedName(storageosPluginName), pvName))
  388. files, err := ioutil.ReadDir(volumeDir)
  389. if err != nil {
  390. return "", "", fmt.Errorf("Could not read mounts from pod volume dir: %s", err)
  391. }
  392. for _, f := range files {
  393. if f.Mode().IsDir() && strings.HasPrefix(f.Name(), pvName+".") {
  394. if volNamespace, volName, err := getVolumeFromRef(f.Name()); err == nil {
  395. return volNamespace, volName, nil
  396. }
  397. }
  398. }
  399. return "", "", fmt.Errorf("Could not get info from unmounted pv %q at %q", pvName, volumeDir)
  400. }
  401. // Splits the volume ref on "." to return the volNamespace and pvName. Neither
  402. // namespaces nor service names allow "." in their names.
  403. func getVolumeFromRef(ref string) (volNamespace string, volName string, err error) {
  404. refParts := strings.Split(ref, ".")
  405. switch len(refParts) {
  406. case 2:
  407. return refParts[0], refParts[1], nil
  408. case 3:
  409. return refParts[1], refParts[2], nil
  410. }
  411. return "", "", fmt.Errorf("ref not in format volNamespace.volName or pvName.volNamespace.volName")
  412. }
  413. // GetPath returns the path to the user specific mount of a StorageOS volume
  414. func (storageosVolume *storageos) GetPath() string {
  415. return getPath(storageosVolume.podUID, storageosVolume.volNamespace, storageosVolume.volName, storageosVolume.pvName, storageosVolume.plugin.host)
  416. }
  417. type storageosUnmounter struct {
  418. *storageos
  419. }
  420. var _ volume.Unmounter = &storageosUnmounter{}
  421. func (b *storageosUnmounter) GetPath() string {
  422. return getPath(b.podUID, b.volNamespace, b.volName, b.pvName, b.plugin.host)
  423. }
  424. // Unmounts the bind mount, and detaches the disk only if the PD
  425. // resource was the last reference to that disk on the kubelet.
  426. func (b *storageosUnmounter) TearDown() error {
  427. if len(b.volNamespace) == 0 || len(b.volName) == 0 {
  428. klog.Warningf("volNamespace: %q, volName: %q not set, skipping TearDown", b.volNamespace, b.volName)
  429. return fmt.Errorf("pvName not specified for TearDown, waiting for next sync loop")
  430. }
  431. // Unmount from pod
  432. mountPath := b.GetPath()
  433. err := b.TearDownAt(mountPath)
  434. if err != nil {
  435. klog.Errorf("Unmount from pod failed: %v", err)
  436. return err
  437. }
  438. // Find device name from global mount
  439. globalPDPath := makeGlobalPDName(b.plugin.host, b.pvName, b.volNamespace, b.volName)
  440. devicePath, _, err := mount.GetDeviceNameFromMount(b.mounter, globalPDPath)
  441. if err != nil {
  442. klog.Errorf("Detach failed when getting device from global mount: %v", err)
  443. return err
  444. }
  445. // Unmount from plugin's disk global mount dir.
  446. err = b.TearDownAt(globalPDPath)
  447. if err != nil {
  448. klog.Errorf("Detach failed during unmount: %v", err)
  449. return err
  450. }
  451. // Detach loop device
  452. err = b.manager.DetachVolume(b, devicePath)
  453. if err != nil {
  454. klog.Errorf("Detach device %s failed for volume %s: %v", devicePath, b.pvName, err)
  455. return err
  456. }
  457. klog.V(4).Infof("Successfully unmounted StorageOS volume %s and detached devices", b.pvName)
  458. return nil
  459. }
  460. // Unmounts the bind mount, and detaches the disk only if the PD
  461. // resource was the last reference to that disk on the kubelet.
  462. func (b *storageosUnmounter) TearDownAt(dir string) error {
  463. if err := mount.CleanupMountPoint(dir, b.mounter, false); err != nil {
  464. klog.V(4).Infof("Unmounted StorageOS volume %s failed with: %v", b.pvName, err)
  465. }
  466. if err := b.manager.UnmountVolume(b); err != nil {
  467. klog.V(4).Infof("Mount reference for volume %s could not be removed from StorageOS: %v", b.pvName, err)
  468. }
  469. return nil
  470. }
  471. type storageosDeleter struct {
  472. *storageosMounter
  473. pvUID types.UID
  474. }
  475. var _ volume.Deleter = &storageosDeleter{}
  476. func (d *storageosDeleter) GetPath() string {
  477. return getPath(d.podUID, d.volNamespace, d.volName, d.pvName, d.plugin.host)
  478. }
  479. func (d *storageosDeleter) Delete() error {
  480. return d.manager.DeleteVolume(d)
  481. }
  482. type storageosProvisioner struct {
  483. *storageosMounter
  484. options volume.VolumeOptions
  485. }
  486. var _ volume.Provisioner = &storageosProvisioner{}
  487. func (c *storageosProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
  488. if !util.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) {
  489. return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes())
  490. }
  491. if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) {
  492. return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName())
  493. }
  494. var adminSecretName, adminSecretNamespace string
  495. // Apply ProvisionerParameters (case-insensitive). We leave validation of
  496. // the values to the cloud provider.
  497. for k, v := range c.options.Parameters {
  498. switch strings.ToLower(k) {
  499. case "adminsecretname":
  500. adminSecretName = v
  501. case "adminsecretnamespace":
  502. adminSecretNamespace = v
  503. case "volumenamespace":
  504. c.volNamespace = v
  505. case "description":
  506. c.description = v
  507. case "pool":
  508. c.pool = v
  509. case "fstype":
  510. c.fsType = v
  511. default:
  512. return nil, fmt.Errorf("invalid option %q for volume plugin %s", k, c.plugin.GetPluginName())
  513. }
  514. }
  515. // Set from PVC
  516. c.podNamespace = c.options.PVC.Namespace
  517. c.volName = c.options.PVName
  518. if c.volNamespace == "" {
  519. c.volNamespace = c.options.PVC.Namespace
  520. }
  521. c.labels = make(map[string]string)
  522. for k, v := range c.options.PVC.Labels {
  523. c.labels[k] = v
  524. }
  525. capacity := c.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
  526. var err error
  527. c.sizeGB, err = volumehelpers.RoundUpToGiBInt(capacity)
  528. if err != nil {
  529. return nil, err
  530. }
  531. apiCfg, err := parsePVSecret(adminSecretNamespace, adminSecretName, c.plugin.host.GetKubeClient())
  532. if err != nil {
  533. return nil, err
  534. }
  535. c.apiCfg = apiCfg
  536. vol, err := c.manager.CreateVolume(c)
  537. if err != nil {
  538. klog.Errorf("failed to create volume: %v", err)
  539. return nil, err
  540. }
  541. if vol.FSType == "" {
  542. vol.FSType = defaultFSType
  543. }
  544. pv := &v1.PersistentVolume{
  545. ObjectMeta: metav1.ObjectMeta{
  546. Name: vol.Name,
  547. Labels: map[string]string{},
  548. Annotations: map[string]string{
  549. util.VolumeDynamicallyCreatedByKey: "storageos-dynamic-provisioner",
  550. },
  551. },
  552. Spec: v1.PersistentVolumeSpec{
  553. PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy,
  554. AccessModes: c.options.PVC.Spec.AccessModes,
  555. Capacity: v1.ResourceList{
  556. v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", vol.SizeGB)),
  557. },
  558. PersistentVolumeSource: v1.PersistentVolumeSource{
  559. StorageOS: &v1.StorageOSPersistentVolumeSource{
  560. VolumeName: vol.Name,
  561. VolumeNamespace: vol.Namespace,
  562. FSType: vol.FSType,
  563. ReadOnly: false,
  564. SecretRef: &v1.ObjectReference{
  565. Name: adminSecretName,
  566. Namespace: adminSecretNamespace,
  567. },
  568. },
  569. },
  570. MountOptions: c.options.MountOptions,
  571. },
  572. }
  573. if len(c.options.PVC.Spec.AccessModes) == 0 {
  574. pv.Spec.AccessModes = c.plugin.GetAccessModes()
  575. }
  576. if len(vol.Labels) != 0 {
  577. if pv.Labels == nil {
  578. pv.Labels = make(map[string]string)
  579. }
  580. for k, v := range vol.Labels {
  581. pv.Labels[k] = v
  582. }
  583. }
  584. return pv, nil
  585. }
  586. // Returns StorageOS volume name, namespace, fstype and readonly from spec
  587. func getVolumeInfoFromSpec(spec *volume.Spec) (string, string, string, bool, error) {
  588. if spec.PersistentVolume != nil {
  589. source, readOnly, err := getPersistentVolumeSource(spec)
  590. if err != nil {
  591. return "", "", "", false, err
  592. }
  593. return source.VolumeName, source.VolumeNamespace, source.FSType, readOnly, nil
  594. }
  595. if spec.Volume != nil {
  596. source, readOnly, err := getVolumeSource(spec)
  597. if err != nil {
  598. return "", "", "", false, err
  599. }
  600. return source.VolumeName, source.VolumeNamespace, source.FSType, readOnly, nil
  601. }
  602. return "", "", "", false, fmt.Errorf("spec not Volume or PersistentVolume")
  603. }
  604. // Returns API config if secret set, otherwise empty struct so defaults can be
  605. // attempted.
  606. func getAPICfg(spec *volume.Spec, pod *v1.Pod, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
  607. if spec.PersistentVolume != nil {
  608. source, _, err := getPersistentVolumeSource(spec)
  609. if err != nil {
  610. return nil, err
  611. }
  612. if source.SecretRef == nil {
  613. return nil, nil
  614. }
  615. return parsePVSecret(source.SecretRef.Namespace, source.SecretRef.Name, kubeClient)
  616. }
  617. if spec.Volume != nil {
  618. source, _, err := getVolumeSource(spec)
  619. if err != nil {
  620. return nil, err
  621. }
  622. if source.SecretRef == nil {
  623. return nil, nil
  624. }
  625. return parsePodSecret(pod, source.SecretRef.Name, kubeClient)
  626. }
  627. return nil, fmt.Errorf("spec not Volume or PersistentVolume")
  628. }
  629. func parsePodSecret(pod *v1.Pod, secretName string, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
  630. secret, err := util.GetSecretForPod(pod, secretName, kubeClient)
  631. if err != nil {
  632. klog.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
  633. return nil, fmt.Errorf("failed to get secret from [%q/%q]", pod.Namespace, secretName)
  634. }
  635. return parseAPIConfig(secret)
  636. }
  637. // Important: Only to be called with data from a PV to avoid secrets being
  638. // loaded from a user-suppler namespace.
  639. func parsePVSecret(namespace, secretName string, kubeClient clientset.Interface) (*storageosAPIConfig, error) {
  640. secret, err := util.GetSecretForPV(namespace, secretName, storageosPluginName, kubeClient)
  641. if err != nil {
  642. klog.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
  643. return nil, fmt.Errorf("failed to get secret from [%q/%q]", namespace, secretName)
  644. }
  645. return parseAPIConfig(secret)
  646. }
  647. // Parse API configuration from parameters or secret
  648. func parseAPIConfig(params map[string]string) (*storageosAPIConfig, error) {
  649. if len(params) == 0 {
  650. return nil, fmt.Errorf("empty API config")
  651. }
  652. c := &storageosAPIConfig{}
  653. for name, data := range params {
  654. switch strings.ToLower(name) {
  655. case "apiaddress":
  656. c.apiAddr = string(data)
  657. case "apiusername":
  658. c.apiUser = string(data)
  659. case "apipassword":
  660. c.apiPass = string(data)
  661. case "apiversion":
  662. c.apiVersion = string(data)
  663. }
  664. }
  665. return c, nil
  666. }