plugin.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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 flexvolume
  14. import (
  15. "fmt"
  16. "path/filepath"
  17. "runtime"
  18. "strings"
  19. "sync"
  20. api "k8s.io/api/core/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. "k8s.io/klog"
  23. "k8s.io/kubernetes/pkg/volume"
  24. "k8s.io/kubernetes/pkg/volume/util"
  25. "k8s.io/utils/exec"
  26. "k8s.io/utils/mount"
  27. utilstrings "k8s.io/utils/strings"
  28. )
  29. const (
  30. flexVolumePluginName = "kubernetes.io/flexvolume"
  31. flexVolumePluginNamePrefix = "flexvolume-"
  32. )
  33. // FlexVolumePlugin object.
  34. type flexVolumePlugin struct {
  35. driverName string
  36. execPath string
  37. host volume.VolumeHost
  38. runner exec.Interface
  39. sync.Mutex
  40. unsupportedCommands []string
  41. capabilities DriverCapabilities
  42. }
  43. type flexVolumeAttachablePlugin struct {
  44. *flexVolumePlugin
  45. }
  46. var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{}
  47. var _ volume.PersistentVolumePlugin = &flexVolumePlugin{}
  48. var _ volume.NodeExpandableVolumePlugin = &flexVolumePlugin{}
  49. var _ volume.ExpandableVolumePlugin = &flexVolumePlugin{}
  50. var _ volume.DeviceMountableVolumePlugin = &flexVolumeAttachablePlugin{}
  51. // PluginFactory create flex volume plugin
  52. type PluginFactory interface {
  53. NewFlexVolumePlugin(pluginDir, driverName string, runner exec.Interface) (volume.VolumePlugin, error)
  54. }
  55. type pluginFactory struct{}
  56. func (pluginFactory) NewFlexVolumePlugin(pluginDir, name string, runner exec.Interface) (volume.VolumePlugin, error) {
  57. execPath := filepath.Join(pluginDir, name)
  58. driverName := utilstrings.UnescapeQualifiedName(name)
  59. flexPlugin := &flexVolumePlugin{
  60. driverName: driverName,
  61. execPath: execPath,
  62. runner: runner,
  63. unsupportedCommands: []string{},
  64. }
  65. // Initialize the plugin and probe the capabilities
  66. call := flexPlugin.NewDriverCall(initCmd)
  67. ds, err := call.Run()
  68. if err != nil {
  69. return nil, err
  70. }
  71. flexPlugin.capabilities = *ds.Capabilities
  72. if flexPlugin.capabilities.Attach {
  73. // Plugin supports attach/detach, so return flexVolumeAttachablePlugin
  74. return &flexVolumeAttachablePlugin{flexVolumePlugin: flexPlugin}, nil
  75. }
  76. return flexPlugin, nil
  77. }
  78. // Init is part of the volume.VolumePlugin interface.
  79. func (plugin *flexVolumePlugin) Init(host volume.VolumeHost) error {
  80. plugin.host = host
  81. // Hardwired 'success' as any errors from calling init() will be caught by NewFlexVolumePlugin()
  82. return nil
  83. }
  84. func (plugin *flexVolumePlugin) getExecutable() string {
  85. parts := strings.Split(plugin.driverName, "/")
  86. execName := parts[len(parts)-1]
  87. execPath := filepath.Join(plugin.execPath, execName)
  88. if runtime.GOOS == "windows" {
  89. execPath = util.GetWindowsPath(execPath)
  90. }
  91. return execPath
  92. }
  93. // Name is part of the volume.VolumePlugin interface.
  94. func (plugin *flexVolumePlugin) GetPluginName() string {
  95. return flexVolumePluginNamePrefix + plugin.driverName
  96. }
  97. // GetVolumeName is part of the volume.VolumePlugin interface.
  98. func (plugin *flexVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  99. call := plugin.NewDriverCall(getVolumeNameCmd)
  100. call.AppendSpec(spec, plugin.host, nil)
  101. _, err := call.Run()
  102. if isCmdNotSupportedErr(err) {
  103. return (*pluginDefaults)(plugin).GetVolumeName(spec)
  104. } else if err != nil {
  105. return "", err
  106. }
  107. name, err := (*pluginDefaults)(plugin).GetVolumeName(spec)
  108. if err != nil {
  109. return "", err
  110. }
  111. klog.V(4).Infof(logPrefix(plugin), "GetVolumeName is not supported yet. Defaulting to PV or volume name: ", name)
  112. return name, nil
  113. }
  114. // CanSupport is part of the volume.VolumePlugin interface.
  115. func (plugin *flexVolumePlugin) CanSupport(spec *volume.Spec) bool {
  116. sourceDriver, err := getDriver(spec)
  117. if err != nil {
  118. return false
  119. }
  120. return sourceDriver == plugin.driverName
  121. }
  122. // RequiresRemount is part of the volume.VolumePlugin interface.
  123. func (plugin *flexVolumePlugin) RequiresRemount() bool {
  124. return false
  125. }
  126. // GetAccessModes gets the allowed access modes for this plugin.
  127. func (plugin *flexVolumePlugin) GetAccessModes() []api.PersistentVolumeAccessMode {
  128. return []api.PersistentVolumeAccessMode{
  129. api.ReadWriteOnce,
  130. api.ReadOnlyMany,
  131. }
  132. }
  133. // NewMounter is part of the volume.VolumePlugin interface.
  134. func (plugin *flexVolumePlugin) NewMounter(spec *volume.Spec, pod *api.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  135. return plugin.newMounterInternal(spec, pod, plugin.host.GetMounter(plugin.GetPluginName()), plugin.runner)
  136. }
  137. // newMounterInternal is the internal mounter routine to build the volume.
  138. func (plugin *flexVolumePlugin) newMounterInternal(spec *volume.Spec, pod *api.Pod, mounter mount.Interface, runner exec.Interface) (volume.Mounter, error) {
  139. sourceDriver, err := getDriver(spec)
  140. if err != nil {
  141. return nil, err
  142. }
  143. readOnly, err := getReadOnly(spec)
  144. if err != nil {
  145. return nil, err
  146. }
  147. var metricsProvider volume.MetricsProvider
  148. if plugin.capabilities.SupportsMetrics {
  149. metricsProvider = volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(
  150. pod.UID, utilstrings.EscapeQualifiedName(sourceDriver), spec.Name()))
  151. } else {
  152. metricsProvider = &volume.MetricsNil{}
  153. }
  154. return &flexVolumeMounter{
  155. flexVolume: &flexVolume{
  156. driverName: sourceDriver,
  157. execPath: plugin.getExecutable(),
  158. mounter: mounter,
  159. plugin: plugin,
  160. podName: pod.Name,
  161. podUID: pod.UID,
  162. podNamespace: pod.Namespace,
  163. podServiceAccountName: pod.Spec.ServiceAccountName,
  164. volName: spec.Name(),
  165. MetricsProvider: metricsProvider,
  166. },
  167. runner: runner,
  168. spec: spec,
  169. readOnly: readOnly,
  170. }, nil
  171. }
  172. // NewUnmounter is part of the volume.VolumePlugin interface.
  173. func (plugin *flexVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  174. return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()), plugin.runner)
  175. }
  176. // newUnmounterInternal is the internal unmounter routine to clean the volume.
  177. func (plugin *flexVolumePlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface, runner exec.Interface) (volume.Unmounter, error) {
  178. var metricsProvider volume.MetricsProvider
  179. if plugin.capabilities.SupportsMetrics {
  180. metricsProvider = volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(
  181. podUID, utilstrings.EscapeQualifiedName(plugin.driverName), volName))
  182. } else {
  183. metricsProvider = &volume.MetricsNil{}
  184. }
  185. return &flexVolumeUnmounter{
  186. flexVolume: &flexVolume{
  187. driverName: plugin.driverName,
  188. execPath: plugin.getExecutable(),
  189. mounter: mounter,
  190. plugin: plugin,
  191. podUID: podUID,
  192. volName: volName,
  193. MetricsProvider: metricsProvider,
  194. },
  195. runner: runner,
  196. }, nil
  197. }
  198. // NewAttacher is part of the volume.AttachableVolumePlugin interface.
  199. func (plugin *flexVolumeAttachablePlugin) NewAttacher() (volume.Attacher, error) {
  200. return &flexVolumeAttacher{plugin}, nil
  201. }
  202. func (plugin *flexVolumeAttachablePlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
  203. return plugin.NewAttacher()
  204. }
  205. // NewDetacher is part of the volume.AttachableVolumePlugin interface.
  206. func (plugin *flexVolumeAttachablePlugin) NewDetacher() (volume.Detacher, error) {
  207. return &flexVolumeDetacher{plugin}, nil
  208. }
  209. func (plugin *flexVolumeAttachablePlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
  210. return plugin.NewDetacher()
  211. }
  212. func (plugin *flexVolumeAttachablePlugin) CanAttach(spec *volume.Spec) (bool, error) {
  213. return true, nil
  214. }
  215. func (plugin *flexVolumeAttachablePlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
  216. return true, nil
  217. }
  218. // ConstructVolumeSpec is part of the volume.AttachableVolumePlugin interface.
  219. func (plugin *flexVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  220. flexVolume := &api.Volume{
  221. Name: volumeName,
  222. VolumeSource: api.VolumeSource{
  223. FlexVolume: &api.FlexVolumeSource{
  224. Driver: plugin.driverName,
  225. },
  226. },
  227. }
  228. return volume.NewSpecFromVolume(flexVolume), nil
  229. }
  230. func (plugin *flexVolumePlugin) SupportsMountOption() bool {
  231. return false
  232. }
  233. // Mark the given commands as unsupported.
  234. func (plugin *flexVolumePlugin) unsupported(commands ...string) {
  235. plugin.Lock()
  236. defer plugin.Unlock()
  237. plugin.unsupportedCommands = append(plugin.unsupportedCommands, commands...)
  238. }
  239. func (plugin *flexVolumePlugin) SupportsBulkVolumeVerification() bool {
  240. return false
  241. }
  242. // Returns true iff the given command is known to be unsupported.
  243. func (plugin *flexVolumePlugin) isUnsupported(command string) bool {
  244. plugin.Lock()
  245. defer plugin.Unlock()
  246. for _, unsupportedCommand := range plugin.unsupportedCommands {
  247. if command == unsupportedCommand {
  248. return true
  249. }
  250. }
  251. return false
  252. }
  253. func (plugin *flexVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
  254. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  255. return mounter.GetMountRefs(deviceMountPath)
  256. }
  257. func (plugin *flexVolumePlugin) getDeviceMountPath(spec *volume.Spec) (string, error) {
  258. volumeName, err := plugin.GetVolumeName(spec)
  259. if err != nil {
  260. return "", fmt.Errorf("GetVolumeName failed from getDeviceMountPath: %s", err)
  261. }
  262. mountsDir := filepath.Join(plugin.host.GetPluginDir(flexVolumePluginName), plugin.driverName, "mounts")
  263. return filepath.Join(mountsDir, volumeName), nil
  264. }
  265. func (plugin *flexVolumePlugin) RequiresFSResize() bool {
  266. return plugin.capabilities.RequiresFSResize
  267. }