Merge pull request #47503 from chakri-nelluri/flexcap

Automatic merge from submit-queue (batch tested with PRs 47878, 47503, 47857)

Remove controller node plugin driver dependency for non-attachable fl…

…ex volume drivers (Ex: NFS).

**What this PR does / why we need it**:
Removes requirement to install flex volume drivers on master node for non-attachable drivers likes NFS.

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #47109


```release-note
Fixes issue w/Flex volume, introduced in 1.6.0, where drivers without an attacher would fail (node indefinitely waiting for attach). Drivers that don't implement attach should return `attach: false` on `init`.
```
This commit is contained in:
Kubernetes Submit Queue 2017-06-21 21:12:15 -07:00 committed by GitHub
commit d021db8204
10 changed files with 98 additions and 55 deletions

View File

@ -83,7 +83,7 @@ unmount() {
op=$1 op=$1
if [ "$op" = "init" ]; then if [ "$op" = "init" ]; then
log "{\"status\": \"Success\"}" log "{\"status\": \"Success\", \"capabilities\": {\"attach\": false}}"
exit 0 exit 0
fi fi
@ -100,9 +100,6 @@ case "$op" in
unmount) unmount)
unmount $* unmount $*
;; ;;
getvolumename)
getvolumename $*
;;
*) *)
log "{ \"status\": \"Not supported\" }" log "{ \"status\": \"Not supported\" }"
exit 0 exit 0

View File

@ -17,8 +17,6 @@ limitations under the License.
package flexvolume package flexvolume
import ( import (
"fmt"
"path"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
@ -33,30 +31,24 @@ type attacherDefaults flexVolumeAttacher
// Attach is part of the volume.Attacher interface // Attach is part of the volume.Attacher interface
func (a *attacherDefaults) Attach(spec *volume.Spec, hostName types.NodeName) (string, error) { 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 return "", nil
} }
// WaitForAttach is part of the volume.Attacher interface // WaitForAttach is part of the volume.Attacher interface
func (a *attacherDefaults) WaitForAttach(spec *volume.Spec, devicePath string, timeout time.Duration) (string, error) { 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 return devicePath, nil
} }
// GetDeviceMountPath is part of the volume.Attacher interface // GetDeviceMountPath is part of the volume.Attacher interface
func (a *attacherDefaults) GetDeviceMountPath(spec *volume.Spec, mountsDir string) (string, error) { 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) return a.plugin.getDeviceMountPath(spec)
volumeName, err := a.plugin.GetVolumeName(spec)
if err != nil {
return "", fmt.Errorf("GetVolumeName failed from GetDeviceMountPath: %s", err)
}
return path.Join(mountsDir, volumeName), nil
} }
// MountDevice is part of the volume.Attacher interface // MountDevice is part of the volume.Attacher interface
func (a *attacherDefaults) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, mounter mount.Interface) error { 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) volSource, readOnly := getVolumeSource(spec)
options := make([]string, 0) options := make([]string, 0)

View File

@ -17,7 +17,6 @@ limitations under the License.
package flexvolume package flexvolume
import ( import (
"path"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
@ -26,7 +25,7 @@ import (
) )
type flexVolumeAttacher struct { type flexVolumeAttacher struct {
plugin *flexVolumePlugin plugin *flexVolumeAttachablePlugin
} }
var _ volume.Attacher = &flexVolumeAttacher{} 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 // GetDeviceMountPath is part of the volume.Attacher interface
func (a *flexVolumeAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) { func (a *flexVolumeAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
mountsDir := path.Join(a.plugin.host.GetPluginDir(flexVolumePluginName), a.plugin.driverName, "mounts") return a.plugin.getDeviceMountPath(spec)
return (*attacherDefaults)(a).GetDeviceMountPath(spec, mountsDir)
} }
// MountDevice is part of the volume.Attacher interface // MountDevice is part of the volume.Attacher interface

View File

@ -28,16 +28,18 @@ import (
volumetesting "k8s.io/kubernetes/pkg/volume/testing" volumetesting "k8s.io/kubernetes/pkg/volume/testing"
) )
func testPlugin() (*flexVolumePlugin, string) { func testPlugin() (*flexVolumeAttachablePlugin, string) {
rootDir, err := utiltesting.MkTmpdir("flexvolume_test") rootDir, err := utiltesting.MkTmpdir("flexvolume_test")
if err != nil { if err != nil {
panic("error creating temp dir: " + err.Error()) panic("error creating temp dir: " + err.Error())
} }
return &flexVolumePlugin{ return &flexVolumeAttachablePlugin{
driverName: "test", flexVolumePlugin: &flexVolumePlugin{
execPath: "/plugin", driverName: "test",
host: volumetesting.NewFakeVolumeHost(rootDir, nil, nil), execPath: "/plugin",
unsupportedCommands: []string{}, host: volumetesting.NewFakeVolumeHost(rootDir, nil, nil),
unsupportedCommands: []string{},
},
}, rootDir }, rootDir
} }
@ -77,11 +79,11 @@ func fakeResultOutput(result interface{}) exec.FakeCombinedOutputAction {
} }
func successOutput() exec.FakeCombinedOutputAction { func successOutput() exec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true}) return fakeResultOutput(&DriverStatus{StatusSuccess, "", "", "", true, nil})
} }
func notSupportedOutput() exec.FakeCombinedOutputAction { func notSupportedOutput() exec.FakeCombinedOutputAction {
return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false}) return fakeResultOutput(&DriverStatus{StatusNotSupported, "", "", "", false, nil})
} }
func sameArgs(args, expectedArgs []string) bool { func sameArgs(args, expectedArgs []string) bool {
@ -126,7 +128,7 @@ func fakePersistentVolumeSpec() *volume.Spec {
return volume.NewSpecFromPersistentVolume(vol, false) 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) o, err := NewOptionsForDriver(spec, plugin.host, extraOptions)
if err != nil { if err != nil {
panic("Failed to convert spec: " + err.Error()) panic("Failed to convert spec: " + err.Error())

View File

@ -28,18 +28,18 @@ type detacherDefaults flexVolumeDetacher
// Detach is part of the volume.Detacher interface. // Detach is part of the volume.Detacher interface.
func (d *detacherDefaults) Detach(deviceName string, hostName types.NodeName) error { 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 return nil
} }
// WaitForDetach is part of the volume.Detacher interface. // WaitForDetach is part of the volume.Detacher interface.
func (d *detacherDefaults) WaitForDetach(devicePath string, timeout time.Duration) error { 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 return nil
} }
// UnmountDevice is part of the volume.Detacher interface. // UnmountDevice is part of the volume.Detacher interface.
func (d *detacherDefaults) UnmountDevice(deviceMountPath string) error { 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()) return util.UnmountPath(deviceMountPath, d.plugin.host.GetMounter())
} }

View File

@ -28,7 +28,7 @@ import (
) )
type flexVolumeDetacher struct { type flexVolumeDetacher struct {
plugin *flexVolumePlugin plugin *flexVolumeAttachablePlugin
} }
var _ volume.Detacher = &flexVolumeDetacher{} var _ volume.Detacher = &flexVolumeDetacher{}

View File

@ -58,6 +58,8 @@ const (
optionKeyPodUID = "kubernetes.io/pod.uid" optionKeyPodUID = "kubernetes.io/pod.uid"
optionKeyServiceAccountName = "kubernetes.io/serviceAccount.name" optionKeyServiceAccountName = "kubernetes.io/serviceAccount.name"
attachCapability = "attach"
) )
const ( const (
@ -199,6 +201,10 @@ type DriverStatus struct {
VolumeName string `json:"volumeName,omitempty"` VolumeName string `json:"volumeName,omitempty"`
// Represents volume is attached on the node // Represents volume is attached on the node
Attached bool `json:"attached,omitempty"` 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 // isCmdNotSupportedErr checks if the error corresponds to command not supported by

View File

@ -17,8 +17,6 @@ limitations under the License.
package flexvolume package flexvolume
import ( import (
"fmt"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
@ -31,13 +29,9 @@ type mounterDefaults flexVolumeMounter
func (f *mounterDefaults) SetUpAt(dir string, fsGroup *int64) error { func (f *mounterDefaults) SetUpAt(dir string, fsGroup *int64) error {
glog.Warning(logPrefix(f.plugin), "using default SetUpAt to ", dir) glog.Warning(logPrefix(f.plugin), "using default SetUpAt to ", dir)
a, err := f.plugin.NewAttacher() src, err := f.plugin.getDeviceMountPath(f.spec)
if err != nil { if err != nil {
return fmt.Errorf("NewAttacher failed: %v", err) return err
}
src, err := a.GetDeviceMountPath(f.spec)
if err != nil {
return fmt.Errorf("GetDeviceMountPath failed: %v", err)
} }
if err := doMount(f.mounter, src, dir, "auto", []string{"bind"}); err != nil { if err := doMount(f.mounter, src, dir, "auto", []string{"bind"}); err != nil {

View File

@ -17,6 +17,7 @@ limitations under the License.
package flexvolume package flexvolume
import ( import (
"fmt"
"path" "path"
"strings" "strings"
"sync" "sync"
@ -27,6 +28,7 @@ import (
api "k8s.io/kubernetes/pkg/api/v1" api "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/util/exec" "k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/mount"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
) )
@ -43,9 +45,56 @@ type flexVolumePlugin struct {
unsupportedCommands []string unsupportedCommands []string
} }
var _ volume.AttachableVolumePlugin = &flexVolumePlugin{} type flexVolumeAttachablePlugin struct {
*flexVolumePlugin
}
var _ volume.AttachableVolumePlugin = &flexVolumeAttachablePlugin{}
var _ volume.PersistentVolumePlugin = &flexVolumePlugin{} 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. // Init is part of the volume.VolumePlugin interface.
func (plugin *flexVolumePlugin) Init(host volume.VolumeHost) error { func (plugin *flexVolumePlugin) Init(host volume.VolumeHost) error {
plugin.host = host plugin.host = host
@ -155,12 +204,12 @@ func (plugin *flexVolumePlugin) newUnmounterInternal(volName string, podUID type
} }
// NewAttacher is part of the volume.AttachableVolumePlugin interface. // 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 return &flexVolumeAttacher{plugin}, nil
} }
// NewDetacher is part of the volume.AttachableVolumePlugin interface. // 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 return &flexVolumeDetacher{plugin}, nil
} }
@ -208,3 +257,13 @@ func (plugin *flexVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]st
mounter := plugin.host.GetMounter() mounter := plugin.host.GetMounter()
return mount.GetMountRefs(mounter, deviceMountPath) 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
}

View File

@ -18,10 +18,7 @@ package flexvolume
import ( import (
"io/ioutil" "io/ioutil"
"path"
"k8s.io/kubernetes/pkg/util/exec"
utilstrings "k8s.io/kubernetes/pkg/util/strings"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
) )
@ -37,13 +34,12 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
// e.g. dirname = vendor~cifs // e.g. dirname = vendor~cifs
// then, executable will be pluginDir/dirname/cifs // then, executable will be pluginDir/dirname/cifs
if f.IsDir() { if f.IsDir() {
execPath := path.Join(pluginDir, f.Name()) plugin, err := NewFlexVolumePlugin(pluginDir, f.Name())
plugins = append(plugins, &flexVolumePlugin{ if err != nil {
driverName: utilstrings.UnescapePluginName(f.Name()), continue
execPath: execPath, }
runner: exec.New(),
unsupportedCommands: []string{}, plugins = append(plugins, plugin)
})
} }
} }
return plugins return plugins