From 894b9b2add77034772cc8860857ca566a1eee110 Mon Sep 17 00:00:00 2001 From: Chakravarthy Nelluri Date: Wed, 14 Jun 2017 03:03:38 -0400 Subject: [PATCH] Remove controller node plugin driver dependency for non-attachable flex volume drivers (Ex: NFS). --- examples/volumes/flexvolume/nfs | 5 +- pkg/volume/flexvolume/attacher-defaults.go | 16 ++---- pkg/volume/flexvolume/attacher.go | 7 +-- pkg/volume/flexvolume/common_test.go | 20 ++++--- pkg/volume/flexvolume/detacher-defaults.go | 6 +- pkg/volume/flexvolume/detacher.go | 2 +- pkg/volume/flexvolume/driver-call.go | 6 ++ pkg/volume/flexvolume/mounter-defaults.go | 10 +--- pkg/volume/flexvolume/plugin.go | 65 +++++++++++++++++++++- pkg/volume/flexvolume/probe.go | 16 ++---- 10 files changed, 98 insertions(+), 55 deletions(-) diff --git a/examples/volumes/flexvolume/nfs b/examples/volumes/flexvolume/nfs index 6a3e29bc3e52..312545063277 100755 --- a/examples/volumes/flexvolume/nfs +++ b/examples/volumes/flexvolume/nfs @@ -83,7 +83,7 @@ unmount() { op=$1 if [ "$op" = "init" ]; then - log "{\"status\": \"Success\"}" + log "{\"status\": \"Success\", \"capabilities\": {\"attach\": false}}" exit 0 fi @@ -100,9 +100,6 @@ case "$op" in unmount) unmount $* ;; - getvolumename) - getvolumename $* - ;; *) log "{ \"status\": \"Not supported\" }" exit 0 diff --git a/pkg/volume/flexvolume/attacher-defaults.go b/pkg/volume/flexvolume/attacher-defaults.go index 6e213fc83b7a..8cfb3f00ed2c 100644 --- a/pkg/volume/flexvolume/attacher-defaults.go +++ b/pkg/volume/flexvolume/attacher-defaults.go @@ -17,8 +17,6 @@ limitations under the License. package flexvolume import ( - "fmt" - "path" "time" "github.com/golang/glog" @@ -33,30 +31,24 @@ type attacherDefaults flexVolumeAttacher // Attach is part of the volume.Attacher interface func (a *attacherDefaults) Attach(spec *volume.Spec, hostName types.NodeName) (string, error) { - glog.Warning(logPrefix(a.plugin), "using default Attach for volume ", spec.Name, ", host ", hostName) + glog.Warning(logPrefix(a.plugin.flexVolumePlugin), "using default Attach for volume ", spec.Name, ", host ", hostName) return "", nil } // WaitForAttach is part of the volume.Attacher interface func (a *attacherDefaults) WaitForAttach(spec *volume.Spec, devicePath string, timeout time.Duration) (string, error) { - glog.Warning(logPrefix(a.plugin), "using default WaitForAttach for volume ", spec.Name, ", device ", devicePath) + glog.Warning(logPrefix(a.plugin.flexVolumePlugin), "using default WaitForAttach for volume ", spec.Name, ", device ", devicePath) return devicePath, nil } // GetDeviceMountPath is part of the volume.Attacher interface func (a *attacherDefaults) GetDeviceMountPath(spec *volume.Spec, mountsDir string) (string, error) { - glog.Warning(logPrefix(a.plugin), "using default GetDeviceMountPath for volume ", spec.Name, ", mountsDir ", mountsDir) - volumeName, err := a.plugin.GetVolumeName(spec) - if err != nil { - return "", fmt.Errorf("GetVolumeName failed from GetDeviceMountPath: %s", err) - } - - return path.Join(mountsDir, volumeName), nil + return a.plugin.getDeviceMountPath(spec) } // MountDevice is part of the volume.Attacher interface func (a *attacherDefaults) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, mounter mount.Interface) error { - glog.Warning(logPrefix(a.plugin), "using default MountDevice for volume ", spec.Name, ", device ", devicePath, ", deviceMountPath ", deviceMountPath) + glog.Warning(logPrefix(a.plugin.flexVolumePlugin), "using default MountDevice for volume ", spec.Name, ", device ", devicePath, ", deviceMountPath ", deviceMountPath) volSource, readOnly := getVolumeSource(spec) options := make([]string, 0) diff --git a/pkg/volume/flexvolume/attacher.go b/pkg/volume/flexvolume/attacher.go index 58bfb7013299..73575be4c4e9 100644 --- a/pkg/volume/flexvolume/attacher.go +++ b/pkg/volume/flexvolume/attacher.go @@ -17,7 +17,6 @@ limitations under the License. package flexvolume import ( - "path" "time" "github.com/golang/glog" @@ -26,7 +25,7 @@ import ( ) type flexVolumeAttacher struct { - plugin *flexVolumePlugin + plugin *flexVolumeAttachablePlugin } var _ volume.Attacher = &flexVolumeAttacher{} @@ -64,9 +63,7 @@ func (a *flexVolumeAttacher) WaitForAttach(spec *volume.Spec, devicePath string, // GetDeviceMountPath is part of the volume.Attacher interface func (a *flexVolumeAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) { - mountsDir := path.Join(a.plugin.host.GetPluginDir(flexVolumePluginName), a.plugin.driverName, "mounts") - - return (*attacherDefaults)(a).GetDeviceMountPath(spec, mountsDir) + return a.plugin.getDeviceMountPath(spec) } // MountDevice is part of the volume.Attacher interface diff --git a/pkg/volume/flexvolume/common_test.go b/pkg/volume/flexvolume/common_test.go index dfb650f61254..2b5761ebe42b 100644 --- a/pkg/volume/flexvolume/common_test.go +++ b/pkg/volume/flexvolume/common_test.go @@ -28,16 +28,18 @@ import ( volumetesting "k8s.io/kubernetes/pkg/volume/testing" ) -func testPlugin() (*flexVolumePlugin, string) { +func testPlugin() (*flexVolumeAttachablePlugin, string) { rootDir, err := utiltesting.MkTmpdir("flexvolume_test") if err != nil { panic("error creating temp dir: " + err.Error()) } - return &flexVolumePlugin{ - driverName: "test", - execPath: "/plugin", - host: volumetesting.NewFakeVolumeHost(rootDir, nil, nil), - unsupportedCommands: []string{}, + return &flexVolumeAttachablePlugin{ + flexVolumePlugin: &flexVolumePlugin{ + driverName: "test", + execPath: "/plugin", + host: volumetesting.NewFakeVolumeHost(rootDir, nil, nil), + unsupportedCommands: []string{}, + }, }, rootDir } @@ -77,11 +79,11 @@ func fakeResultOutput(result interface{}) exec.FakeCombinedOutputAction { } func successOutput() exec.FakeCombinedOutputAction { - return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true}) + return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true, nil}) } func notSupportedOutput() exec.FakeCombinedOutputAction { - return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false}) + return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false, nil}) } func sameArgs(args, expectedArgs []string) bool { @@ -126,7 +128,7 @@ func fakePersistentVolumeSpec() *volume.Spec { return volume.NewSpecFromPersistentVolume(vol, false) } -func specJson(plugin *flexVolumePlugin, spec *volume.Spec, extraOptions map[string]string) string { +func specJson(plugin *flexVolumeAttachablePlugin, spec *volume.Spec, extraOptions map[string]string) string { o, err := NewOptionsForDriver(spec, plugin.host, extraOptions) if err != nil { panic("Failed to convert spec: " + err.Error()) diff --git a/pkg/volume/flexvolume/detacher-defaults.go b/pkg/volume/flexvolume/detacher-defaults.go index e3c9b6c83ba6..3226836f90e1 100644 --- a/pkg/volume/flexvolume/detacher-defaults.go +++ b/pkg/volume/flexvolume/detacher-defaults.go @@ -28,18 +28,18 @@ type detacherDefaults flexVolumeDetacher // Detach is part of the volume.Detacher interface. func (d *detacherDefaults) Detach(deviceName string, hostName types.NodeName) error { - glog.Warning(logPrefix(d.plugin), "using default Detach for device ", deviceName, ", host ", hostName) + glog.Warning(logPrefix(d.plugin.flexVolumePlugin), "using default Detach for device ", deviceName, ", host ", hostName) return nil } // WaitForDetach is part of the volume.Detacher interface. func (d *detacherDefaults) WaitForDetach(devicePath string, timeout time.Duration) error { - glog.Warning(logPrefix(d.plugin), "using default WaitForDetach for device ", devicePath) + glog.Warning(logPrefix(d.plugin.flexVolumePlugin), "using default WaitForDetach for device ", devicePath) return nil } // UnmountDevice is part of the volume.Detacher interface. func (d *detacherDefaults) UnmountDevice(deviceMountPath string) error { - glog.Warning(logPrefix(d.plugin), "using default UnmountDevice for device mount path ", deviceMountPath) + glog.Warning(logPrefix(d.plugin.flexVolumePlugin), "using default UnmountDevice for device mount path ", deviceMountPath) return util.UnmountPath(deviceMountPath, d.plugin.host.GetMounter()) } diff --git a/pkg/volume/flexvolume/detacher.go b/pkg/volume/flexvolume/detacher.go index ed4c1529ad50..dcca403619e3 100644 --- a/pkg/volume/flexvolume/detacher.go +++ b/pkg/volume/flexvolume/detacher.go @@ -28,7 +28,7 @@ import ( ) type flexVolumeDetacher struct { - plugin *flexVolumePlugin + plugin *flexVolumeAttachablePlugin } var _ volume.Detacher = &flexVolumeDetacher{} diff --git a/pkg/volume/flexvolume/driver-call.go b/pkg/volume/flexvolume/driver-call.go index 848998c56bfd..9821ddc39030 100644 --- a/pkg/volume/flexvolume/driver-call.go +++ b/pkg/volume/flexvolume/driver-call.go @@ -58,6 +58,8 @@ const ( optionKeyPodUID = "kubernetes.io/pod.uid" optionKeyServiceAccountName = "kubernetes.io/serviceAccount.name" + + attachCapability = "attach" ) const ( @@ -201,6 +203,10 @@ type DriverStatus struct { VolumeName string `json:"volumeName,omitempty"` // Represents volume is attached on the node Attached bool `json:"attached,omitempty"` + // Returns capabilities of the driver. + // By default we assume all the capabilities are supported. + // If the plugin does not support a capability, it can return false for that capability. + Capabilities map[string]bool } // isCmdNotSupportedErr checks if the error corresponds to command not supported by diff --git a/pkg/volume/flexvolume/mounter-defaults.go b/pkg/volume/flexvolume/mounter-defaults.go index b51bfb482aef..1553174999d7 100644 --- a/pkg/volume/flexvolume/mounter-defaults.go +++ b/pkg/volume/flexvolume/mounter-defaults.go @@ -17,8 +17,6 @@ limitations under the License. package flexvolume import ( - "fmt" - "github.com/golang/glog" "k8s.io/apimachinery/pkg/types" @@ -32,13 +30,9 @@ type mounterDefaults flexVolumeMounter func (f *mounterDefaults) SetUpAt(dir string, fsGroup *types.UnixGroupID) error { glog.Warning(logPrefix(f.plugin), "using default SetUpAt to ", dir) - a, err := f.plugin.NewAttacher() - if err != nil { - return fmt.Errorf("NewAttacher failed: %v", err) - } - src, err := a.GetDeviceMountPath(f.spec) + src, err := f.plugin.getDeviceMountPath(f.spec) if err != nil { - return fmt.Errorf("GetDeviceMountPath failed: %v", err) + return err } if err := doMount(f.mounter, src, dir, "auto", []string{"bind"}); err != nil { diff --git a/pkg/volume/flexvolume/plugin.go b/pkg/volume/flexvolume/plugin.go index 0e6ab8e1efc8..8db1faeffde2 100644 --- a/pkg/volume/flexvolume/plugin.go +++ b/pkg/volume/flexvolume/plugin.go @@ -17,6 +17,7 @@ limitations under the License. package flexvolume import ( + "fmt" "path" "strings" "sync" @@ -27,6 +28,7 @@ import ( api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/util/exec" "k8s.io/kubernetes/pkg/util/mount" + utilstrings "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" ) @@ -43,9 +45,56 @@ type flexVolumePlugin struct { unsupportedCommands []string } -var _ volume.AttachableVolumePlugin = &flexVolumePlugin{} +type flexVolumeAttachablePlugin struct { + *flexVolumePlugin +} + +var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{} var _ volume.PersistentVolumePlugin = &flexVolumePlugin{} +func NewFlexVolumePlugin(pluginDir, name string) (volume.VolumePlugin, error) { + execPath := path.Join(pluginDir, name) + + driverName := utilstrings.UnescapePluginName(name) + + flexPlugin := &flexVolumePlugin{ + driverName: driverName, + execPath: execPath, + runner: exec.New(), + unsupportedCommands: []string{}, + } + + // Check whether the plugin is attachable. + ok, err := isAttachable(flexPlugin) + if err != nil { + return nil, err + } + + if ok { + // Plugin supports attach/detach, so return flexVolumeAttachablePlugin + return &flexVolumeAttachablePlugin{flexVolumePlugin: flexPlugin}, nil + } else { + return flexPlugin, nil + } +} + +func isAttachable(plugin *flexVolumePlugin) (bool, error) { + call := plugin.NewDriverCall(initCmd) + res, err := call.Run() + if err != nil { + return false, err + } + + // By default all plugins are attachable, unless they report otherwise. + cap, ok := res.Capabilities[attachCapability] + if ok { + // cap is false, so plugin does not support attach/detach calls. + return cap, nil + } + + return true, nil +} + // Init is part of the volume.VolumePlugin interface. func (plugin *flexVolumePlugin) Init(host volume.VolumeHost) error { plugin.host = host @@ -155,12 +204,12 @@ func (plugin *flexVolumePlugin) newUnmounterInternal(volName string, podUID type } // NewAttacher is part of the volume.AttachableVolumePlugin interface. -func (plugin *flexVolumePlugin) NewAttacher() (volume.Attacher, error) { +func (plugin *flexVolumeAttachablePlugin) NewAttacher() (volume.Attacher, error) { return &flexVolumeAttacher{plugin}, nil } // NewDetacher is part of the volume.AttachableVolumePlugin interface. -func (plugin *flexVolumePlugin) NewDetacher() (volume.Detacher, error) { +func (plugin *flexVolumeAttachablePlugin) NewDetacher() (volume.Detacher, error) { return &flexVolumeDetacher{plugin}, nil } @@ -208,3 +257,13 @@ func (plugin *flexVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]st mounter := plugin.host.GetMounter() return mount.GetMountRefs(mounter, deviceMountPath) } + +func (plugin *flexVolumePlugin) getDeviceMountPath(spec *volume.Spec) (string, error) { + volumeName, err := plugin.GetVolumeName(spec) + if err != nil { + return "", fmt.Errorf("GetVolumeName failed from getDeviceMountPath: %s", err) + } + + mountsDir := path.Join(plugin.host.GetPluginDir(flexVolumePluginName), plugin.driverName, "mounts") + return path.Join(mountsDir, volumeName), nil +} diff --git a/pkg/volume/flexvolume/probe.go b/pkg/volume/flexvolume/probe.go index ec0581288756..431c340a22e2 100644 --- a/pkg/volume/flexvolume/probe.go +++ b/pkg/volume/flexvolume/probe.go @@ -18,10 +18,7 @@ package flexvolume import ( "io/ioutil" - "path" - "k8s.io/kubernetes/pkg/util/exec" - utilstrings "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" ) @@ -37,13 +34,12 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin { // e.g. dirname = vendor~cifs // then, executable will be pluginDir/dirname/cifs if f.IsDir() { - execPath := path.Join(pluginDir, f.Name()) - plugins = append(plugins, &flexVolumePlugin{ - driverName: utilstrings.UnescapePluginName(f.Name()), - execPath: execPath, - runner: exec.New(), - unsupportedCommands: []string{}, - }) + plugin, err := NewFlexVolumePlugin(pluginDir, f.Name()) + if err != nil { + continue + } + + plugins = append(plugins, plugin) } } return plugins