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