Change:
Kubernetes version (use ): Client Version: version.Info{Major:"1", Minor:"7+", GitVersion:"v1.7.0-beta.1", GitCommit:"", GitTreeState:"not a git tree", BuildDate:"2017-06-08T13:08:32Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.3", GitCommit:"", GitTreeState:"clean", BuildDate:"2017-05-10T15:38:08Z", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"} Environment: Cloud provider or hardware configuration: Bare-Metal, HP SE316M, 2x X5675, 144GB, iSCSI/RDMA/iSer OS: CentOS 7 Kernel: Linux at-host-18 4.9.31 SMP Wed Jun 7 22:40:40 UTC 2017 x8664 x8664 x8664 GNU/Linux Install tools: Others: What happened: I want to mount a iSCSI volume via iSER (RDMA over Infiniband) in this case the iSCSI iface is "iser" and not tcp, therefore the glob path for device mount is selected (), which then is passed to the fsck and mount commands without globbing the path (e.g. calling filepath.Glob) Therefore the iSCSI volume is not mounted and the pod cannot be started. Logs: What you expected to happen: To successfully mount the volume. How to reproduce it (as minimally and precisely as possible): Setup an iSCSI via RDMA (iSER) environment without multipath and try to mount a volume for a pod. Anything else we need to know: I believe it's just needed to add the globbing at line 259 like so: devicePaths = append(devicePaths, filepath.Glob(devicePath)) As I'm not a go programmer and have no experience with it, probably somebody could create a commit which I would gladly test!
There are no sig labels on this issue. Please by:<br(1) mentioning a sig: <br(2) specifying the label manually: <br<brNote: method (1) will trigger a notification to the team. You can find the team list and label list
/sig storage
I'll take a look.
/sig storage
on a local build that fixed the issue: (e.g. it compiles)
I don't want to add filepath.Glob to that general path because that line is also executed "tcp" case. I'm considering the fix below. Pretty similar.
does that work from a go specific point? is that devicePath passed by reference? (from general c/c++/php/node/c# knowledge I would say that this would have no effect on the devicePath variable .. )
Can we suppose that filepath.Glob(devicePath) always only return one device path if transport is "tcp"?
does that work from a go specific point? is that devicePath passed by reference? (from general c/c++/php/node/c# knowledge I would say that this would have no effect on the devicePath variable .. ) Thank you for above comment. I updated the logic. Could you confirm the fix on the PR?
Pretty sure that tcp transport will always only have one path - and it will exist (there's no glob in the check path)

Discussion:
type StatFunc func(string) (os.FileInfo, error) type GlobFunc func(string) ([]string, error) <del> func waitForPathToExist(devicePath string, maxRetries int, deviceTransport string) bool { </del> <ins> func waitForPathToExist(devicePath *string, maxRetries int, deviceTransport string) bool { </ins> // This makes unit testing a lot easier return waitForPathToExistInternal(devicePath, maxRetries, deviceTransport, os.Stat, filepath.Glob) } <del> func waitForPathToExistInternal(devicePath string, maxRetries int, deviceTransport string, osStat StatFunc, filepathGlob GlobFunc) bool { for i := 0; i < maxRetries; i++ { var err error if deviceTransport == "tcp" { _, err = osStat(devicePath) } else { fpath, _ := filepathGlob(devicePath) if fpath == nil { err = os.ErrNotExist </del> <ins> func waitForPathToExistInternal(devicePath *string, maxRetries int, deviceTransport string, osStat StatFunc, filepathGlob GlobFunc) bool { if devicePath != nil { for i := 0; i < maxRetries; i++ { var err error if deviceTransport == "tcp" { _, err = osStat(*devicePath) } else { fpath, _ := filepathGlob(*devicePath) if fpath == nil { err = os.ErrNotExist } else { // There might be a case that fpath contains multiple device paths if // multiple PCI devices connect to same iscsi target. We handle this // case at subsequent logic. Pick up only first path here. *devicePath = fpath[0] } </ins> } <ins> if err == nil { return true } if err != nil && !os.IsNotExist(err) { return false } if i == maxRetries-1 { break } time.Sleep(time.Second) </ins> } <del> if err == nil { return true } if err != nil && !os.IsNotExist(err) { return false } if i == maxRetries-1 { break } time.Sleep(time.Second) </del> } return false }

} else { devicePath = strings.Join([]string{"/dev/disk/by-path/pci", "*", "ip", tp, "iscsi", b.Iqn, "lun", b.lun}, "-") } <del> exist := waitForPathToExist(devicePath, 1, iscsiTransport) </del> <ins> exist := waitForPathToExist(&devicePath, 1, iscsiTransport) </ins> if exist == false { // build discoverydb and discover iscsi target b.plugin.execCommand("iscsiadm", []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", b.Iface, "-o", "new"})

lastErr = fmt.Errorf("iscsi: failed to attach disk: Error: %s (%v)", string(out), err) continue } <del> exist = waitForPathToExist(devicePath, 10, iscsiTransport) </del> <ins> exist = waitForPathToExist(&devicePath, 10, iscsiTransport) </ins> if !exist { glog.Errorf("Could not attach disk: Timeout after 10s") // update last error

return []string{devicePath}, nil } <ins> func fakeFilepathGlob2(devicePath string) (globs []string, err error) { return []string{ "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0", }, nil } </ins> func TestExtractTransportname(t *testing.T) { fakeIscsiadmOutput := []string{ "# BEGIN RECORD 2.0-873n" +

func TestWaitForPathToExist(t *testing.T) { devicePath := []string{"/dev/disk/by-path/ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0", <del> "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"} </del> <ins> "/dev/disk/by-path/pci-*-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"} fpath := "/dev/disk/by-path/pci-0000:00:00.0-ip-127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0" </ins> <del> exist := waitForPathToExistInternal(devicePath[0], 1, "tcp", fakeOsStat, filepath.Glob) </del> <ins> exist := waitForPathToExistInternal(&devicePath[0], 1, "tcp", fakeOsStat, filepath.Glob) </ins> if exist == false { t.Errorf("waitForPathToExist: could not find path %s", devicePath[0]) } <del> exist = waitForPathToExistInternal(devicePath[0], 1, "fake_iface", fakeOsStat, filepath.Glob) </del> <ins> exist = waitForPathToExistInternal(&devicePath[0], 1, "fake_iface", fakeOsStat, filepath.Glob) </ins> if exist != false { t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[0]) } <del> exist = waitForPathToExistInternal(devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob) </del> <ins> exist = waitForPathToExistInternal(&devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob) </ins> if exist == false { t.Errorf("waitForPathToExist: could not find path %s", devicePath[1]) } <del> exist = waitForPathToExistInternal(devicePath[1], 1, "tcp", os.Stat, fakeFilepathGlob) </del> <ins> exist = waitForPathToExistInternal(&devicePath[1], 1, "tcp", os.Stat, fakeFilepathGlob) </ins> if exist != false { t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1]) } <ins> exist = waitForPathToExistInternal(&devicePath[1], 1, "fake_iface", os.Stat, fakeFilepathGlob2) if devicePath[1] != fpath { t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1]) } </ins> }