From 8f1f2639a5df2ee9f5be496e683250c20ee2d9a9 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 30 Oct 2015 16:57:21 -0400 Subject: [PATCH 1/5] UPSTREAM: 15236: Better error output from gluster --- .../pkg/volume/glusterfs/glusterfs.go | 47 ++++++++++++------- .../pkg/volume/glusterfs/glusterfs_test.go | 7 +-- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs.go index 2e3bc401bde2..c9cc63c6fa1e 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs.go @@ -17,6 +17,7 @@ limitations under the License. package glusterfs import ( + "fmt" "os" "path" @@ -31,11 +32,12 @@ import ( // This is the primary entrypoint for volume plugins. func ProbeVolumePlugins() []volume.VolumePlugin { - return []volume.VolumePlugin{&glusterfsPlugin{nil}} + return []volume.VolumePlugin{&glusterfsPlugin{nil, exec.New()}} } type glusterfsPlugin struct { host volume.VolumeHost + exe exec.Interface } var _ volume.VolumePlugin = &glusterfsPlugin{} @@ -54,8 +56,20 @@ func (plugin *glusterfsPlugin) Name() string { } func (plugin *glusterfsPlugin) CanSupport(spec *volume.Spec) bool { - return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Glusterfs != nil) || - (spec.Volume != nil && spec.Volume.Glusterfs != nil) + if (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Glusterfs == nil) || + (spec.Volume != nil && spec.Volume.Glusterfs == nil) { + return false + } + // see if glusterfs mount helper is there + // this needs a ls because the plugin container may be on a filesystem + // that is not visible to the volume plugin process. + _, err := plugin.execCommand("ls", []string{"/sbin/mount.glusterfs"}) + if err == nil { + return true + } + + return false + } func (plugin *glusterfsPlugin) GetAccessModes() []api.PersistentVolumeAccessMode { @@ -72,10 +86,10 @@ func (plugin *glusterfsPlugin) NewBuilder(spec *volume.Spec, pod *api.Pod, _ vol ns := pod.Namespace ep, err := plugin.host.GetKubeClient().Endpoints(ns).Get(ep_name) if err != nil { - glog.Errorf("Glusterfs: failed to get endpoints %s[%v]", ep_name, err) + glog.Errorf("glusterfs: failed to get endpoints %s[%v]", ep_name, err) return nil, err } - glog.V(1).Infof("Glusterfs: endpoints %v", ep) + glog.V(1).Infof("glusterfs: endpoints %v", ep) return plugin.newBuilderInternal(spec, ep, pod, plugin.host.GetMounter(), exec.New()) } @@ -117,6 +131,11 @@ func (plugin *glusterfsPlugin) newCleanerInternal(volName string, podUID types.U }}, nil } +func (plugin *glusterfsPlugin) execCommand(command string, args []string) ([]byte, error) { + cmd := plugin.exe.Command(command, args...) + return cmd.CombinedOutput() +} + // Glusterfs volumes represent a bare host file or directory mount of an Glusterfs export. type glusterfs struct { volName string @@ -146,7 +165,7 @@ func (b *glusterfsBuilder) SetUp() error { func (b *glusterfsBuilder) SetUpAt(dir string) error { notMnt, err := b.mounter.IsLikelyNotMountPoint(dir) - glog.V(4).Infof("Glusterfs: mount set up: %s %v %v", dir, !notMnt, err) + glog.V(4).Infof("glusterfs: mount set up: %s %v %v", dir, !notMnt, err) if err != nil && !os.IsNotExist(err) { return err } @@ -196,25 +215,22 @@ func (c *glusterfsCleaner) TearDownAt(dir string) error { func (c *glusterfsCleaner) cleanup(dir string) error { notMnt, err := c.mounter.IsLikelyNotMountPoint(dir) if err != nil { - glog.Errorf("Glusterfs: Error checking IsLikelyNotMountPoint: %v", err) - return err + return fmt.Errorf("glusterfs: Error checking IsLikelyNotMountPoint: %v", err) } if notMnt { return os.RemoveAll(dir) } if err := c.mounter.Unmount(dir); err != nil { - glog.Errorf("Glusterfs: Unmounting failed: %v", err) - return err + return fmt.Errorf("glusterfs: Unmounting failed: %v", err) } notMnt, mntErr := c.mounter.IsLikelyNotMountPoint(dir) if mntErr != nil { - glog.Errorf("Glusterfs: IsLikelyNotMountPoint check failed: %v", mntErr) - return mntErr + return fmt.Errorf("glusterfs: IsLikelyNotMountPoint check failed: %v", mntErr) } if notMnt { if err := os.RemoveAll(dir); err != nil { - return err + return fmt.Errorf("glusterfs: RemoveAll failed: %v", err) } } @@ -231,7 +247,7 @@ func (b *glusterfsBuilder) setUpAtInternal(dir string) error { p := path.Join(b.glusterfs.plugin.host.GetPluginDir(glusterfsPluginName), b.glusterfs.volName) if err := os.MkdirAll(p, 0750); err != nil { - return err + return fmt.Errorf("glusterfs: mkdir failed: %v", err) } log := path.Join(p, "glusterfs.log") options = append(options, "log-file="+log) @@ -251,6 +267,5 @@ func (b *glusterfsBuilder) setUpAtInternal(dir string) error { return nil } } - glog.Errorf("Glusterfs: mount failed: %v", errs) - return errs + return fmt.Errorf("glusterfs: mount failed: %v", errs) } diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs_test.go index 74250e59e8ef..4d7f55938a26 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs_test.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/glusterfs/glusterfs_test.go @@ -39,11 +39,8 @@ func TestCanSupport(t *testing.T) { if plug.Name() != "kubernetes.io/glusterfs" { t.Errorf("Wrong name: %s", plug.Name()) } - if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{Glusterfs: &api.GlusterfsVolumeSource{}}}}) { - t.Errorf("Expected true") - } - if !plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{Glusterfs: &api.GlusterfsVolumeSource{}}}}}) { - t.Errorf("Expected true") + if plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{}}}}) { + t.Errorf("Expected false") } if plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{}}}) { t.Errorf("Expected false") From 8fd7a7637d641a2ce9232062a8d92d3356e46b18 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 30 Oct 2015 16:58:01 -0400 Subject: [PATCH 2/5] UPSTREAM: 15562: iSCSI use global path to mount --- .../kubernetes/pkg/volume/iscsi/iscsi_util.go | 50 +++++++++++++++---- .../pkg/volume/iscsi/iscsi_util_test.go | 35 +++++++++---- 2 files changed, 67 insertions(+), 18 deletions(-) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util.go index ede41cb99bef..d9db4fd8e2f0 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util.go @@ -18,6 +18,7 @@ package iscsi import ( "errors" + "fmt" "os" "path" "strings" @@ -60,7 +61,7 @@ func getDevicePrefixRefCount(mounter mount.Interface, deviceNamePrefix string) ( // Find the number of references to the device. refCount := 0 for i := range mps { - if strings.HasPrefix(mps[i].Device, deviceNamePrefix) { + if strings.HasPrefix(mps[i].Path, deviceNamePrefix) { refCount++ } } @@ -121,7 +122,7 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskBuilder) error { } func (util *ISCSIUtil) DetachDisk(c iscsiDiskCleaner, mntPath string) error { - device, cnt, err := mount.GetDeviceNameFromMount(c.mounter, mntPath) + _, cnt, err := mount.GetDeviceNameFromMount(c.mounter, mntPath) if err != nil { glog.Errorf("iscsi detach disk: failed to get device from mnt: %s\nError: %v", mntPath, err) return err @@ -133,18 +134,19 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskCleaner, mntPath string) error { cnt-- // if device is no longer used, see if need to logout the target if cnt == 0 { - // strip -lun- from device path - ind := strings.LastIndex(device, "-lun-") - prefix := device[:(ind - 1)] + device, prefix, err := extractDeviceAndPrefix(mntPath) + if err != nil { + return err + } refCount, err := getDevicePrefixRefCount(c.mounter, prefix) if err == nil && refCount == 0 { // this portal/iqn are no longer referenced, log out // extract portal and iqn from device path - ind1 := strings.LastIndex(device, "-iscsi-") - portal := device[(len("/dev/disk/by-path/ip-")):ind1] - iqn := device[ind1+len("-iscsi-") : ind] - + portal, iqn, err := extractPortalAndIqn(device) + if err != nil { + return err + } glog.Infof("iscsi: log out target %s iqn %s", portal, iqn) out, err := c.plugin.execCommand("iscsiadm", []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"}) if err != nil { @@ -154,3 +156,33 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskCleaner, mntPath string) error { } return nil } + +func extractDeviceAndPrefix(mntPath string) (string, string, error) { + ind := strings.LastIndex(mntPath, "/") + if ind < 0 { + return "", "", fmt.Errorf("iscsi detach disk: malformatted mnt path: %s", mntPath) + } + device := mntPath[(ind + 1):] + // strip -lun- from device path + ind = strings.LastIndex(device, "-lun-") + if ind < 0 { + return "", "", fmt.Errorf("iscsi detach disk: malformatted mnt path: %s", mntPath) + } + prefix := device[:ind] + return device, prefix, nil +} + +func extractPortalAndIqn(device string) (string, string, error) { + ind1 := strings.Index(device, "-") + if ind1 < 0 { + return "", "", fmt.Errorf("iscsi detach disk: no portal in %s", device) + } + portal := device[0:ind1] + ind2 := strings.Index(device, "iqn.") + if ind2 < 0 { + return "", "", fmt.Errorf("iscsi detach disk: no iqn in %s", device) + } + ind := strings.LastIndex(device, "-lun-") + iqn := device[ind2:ind] + return portal, iqn, nil +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util_test.go index d0531a9d7a62..3b7a00d2b117 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util_test.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_util_test.go @@ -25,14 +25,14 @@ import ( func TestGetDevicePrefixRefCount(t *testing.T) { fm := &mount.FakeMounter{ MountPoints: []mount.MountPoint{ - {Device: "/dev/disk/by-path/prefix-lun-1", - Path: "/mnt/111"}, - {Device: "/dev/disk/by-path/prefix-lun-1", - Path: "/mnt/222"}, - {Device: "/dev/disk/by-path/prefix-lun-0", - Path: "/mnt/333"}, - {Device: "/dev/disk/by-path/prefix-lun-0", - Path: "/mnt/444"}, + {Device: "/dev/sdb", + Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0"}, + {Device: "/dev/sdb", + Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-1"}, + {Device: "/dev/sdb", + Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-2"}, + {Device: "/dev/sdb", + Path: "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-3"}, }, } @@ -41,7 +41,7 @@ func TestGetDevicePrefixRefCount(t *testing.T) { expectedRefs int }{ { - "/dev/disk/by-path/prefix", + "/127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00", 4, }, } @@ -52,3 +52,20 @@ func TestGetDevicePrefixRefCount(t *testing.T) { } } } + +func TestExtractDeviceAndPrefix(t *testing.T) { + devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00" + lun := "-lun-0" + device, prefix, err := extractDeviceAndPrefix("/var/lib/kubelet/plugins/kubernetes.io/iscsi/" + devicePath + lun) + if err != nil || device != (devicePath+lun) || prefix != devicePath { + t.Errorf("extractDeviceAndPrefix: expected %s and %s, got %v %s and %s", devicePath+lun, devicePath, err, device, prefix) + } +} + +func TestExtractPortalAndIqn(t *testing.T) { + devicePath := "127.0.0.1:3260-iqn.2014-12.com.example:test.tgt00-lun-0" + portal, iqn, err := extractPortalAndIqn(devicePath) + if err != nil || portal != "127.0.0.1:3260" || iqn != "iqn.2014-12.com.example:test.tgt00" { + t.Errorf("extractPortalAndIqn: got %v %s %s", err, portal, iqn) + } +} From f2ddc56b5ef2875bce6fcfa21660c5804f026de6 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 30 Oct 2015 16:58:30 -0400 Subject: [PATCH 3/5] UPSTREAM: 15555: Use default port 3260 for iSCSI --- .../src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi.go | 13 +++++++++++-- .../kubernetes/pkg/volume/iscsi/iscsi_test.go | 9 +++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi.go index 3b0714389fd4..a448da31e782 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi.go @@ -18,6 +18,7 @@ package iscsi import ( "strconv" + "strings" "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" @@ -94,16 +95,17 @@ func (plugin *iscsiPlugin) newBuilderInternal(spec *volume.Spec, podUID types.UI } lun := strconv.Itoa(iscsi.Lun) + portal := portalBuilder(iscsi.TargetPortal) return &iscsiDiskBuilder{ iscsiDisk: &iscsiDisk{ podUID: podUID, volName: spec.Name(), - portal: iscsi.TargetPortal, + portal: portal, iqn: iscsi.IQN, lun: lun, manager: manager, - mounter: mounter, + mounter: &mount.SafeFormatAndMount{mounter, exec.New()}, plugin: plugin}, fsType: iscsi.FSType, readOnly: readOnly, @@ -196,3 +198,10 @@ func (c *iscsiDiskCleaner) TearDown() error { func (c *iscsiDiskCleaner) TearDownAt(dir string) error { return diskTearDown(c.manager, *c, dir, c.mounter) } + +func portalBuilder(portal string) string { + if !strings.Contains(portal, ":") { + portal = portal + ":3260" + } + return portal +} diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_test.go index 9ee8eef97693..2025d922c755 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_test.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi_test.go @@ -250,3 +250,12 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { t.Errorf("Expected true for builder.IsReadOnly") } } + +func TestPortalBuilder(t *testing.T) { + if portal := portalBuilder("127.0.0.1"); portal != "127.0.0.1:3260" { + t.Errorf("wrong portal: %s", portal) + } + if portal := portalBuilder("127.0.0.1:3260"); portal != "127.0.0.1:3260" { + t.Errorf("wrong portal: %s", portal) + } +} From 03e50db3b5fb1a4b317eaa4bb19db2423c08f541 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 30 Oct 2015 17:01:45 -0400 Subject: [PATCH 4/5] UPSTREAM: 16032: check if /sbin/mount.nfs is present --- .../k8s.io/kubernetes/pkg/volume/nfs/nfs.go | 26 +++++++++++++++++-- .../kubernetes/pkg/volume/nfs/nfs_test.go | 12 +++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs.go index 2b55fe359c8c..d30af4b50177 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs.go @@ -19,10 +19,12 @@ package nfs import ( "fmt" "os" + "runtime" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/exec" "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/volume" @@ -66,9 +68,29 @@ func (plugin *nfsPlugin) Name() string { return nfsPluginName } +func hasNFSMount() bool { + exe := exec.New() + switch runtime.GOOS { + case "linux": + cmd1 := exe.Command("/bin/ls", "/sbin/mount.nfs") + _, err1 := cmd1.CombinedOutput() + cmd2 := exe.Command("/bin/ls", "/sbin/mount.nfs4") + _, err2 := cmd2.CombinedOutput() + return (err1 == nil || err2 == nil) + case "darwin": + cmd := exe.Command("/bin/ls", "/sbin/mount_nfs") + _, err := cmd.CombinedOutput() + return err == nil + } + return false +} + func (plugin *nfsPlugin) CanSupport(spec *volume.Spec) bool { - return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.NFS != nil) || - (spec.Volume != nil && spec.Volume.NFS != nil) + if (spec.Volume != nil && spec.Volume.NFS == nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.NFS == nil) { + return false + } + // see if /sbin/mount.nfs* is there + return hasNFSMount() } func (plugin *nfsPlugin) GetAccessModes() []api.PersistentVolumeAccessMode { diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs_test.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs_test.go index 594d6fb707c7..11f93b82bb85 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs_test.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/volume/nfs/nfs_test.go @@ -38,12 +38,16 @@ func TestCanSupport(t *testing.T) { if plug.Name() != "kubernetes.io/nfs" { t.Errorf("Wrong name: %s", plug.Name()) } - if !plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{}}}}) { + foundMount := hasNFSMount() + if plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{NFS: &api.NFSVolumeSource{}}}}) != foundMount { t.Errorf("Expected true") } - if !plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{NFS: &api.NFSVolumeSource{}}}}}) { + if plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{NFS: &api.NFSVolumeSource{}}}}}) != foundMount { t.Errorf("Expected true") } + if plug.CanSupport(&volume.Spec{PersistentVolume: &api.PersistentVolume{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{}}}}) { + t.Errorf("Expected false") + } if plug.CanSupport(&volume.Spec{Volume: &api.Volume{VolumeSource: api.VolumeSource{}}}) { t.Errorf("Expected false") } @@ -63,6 +67,10 @@ func TestGetAccessModes(t *testing.T) { } func TestRecycler(t *testing.T) { + if foundMount := hasNFSMount(); !foundMount { + // FindRecyclablePluginBySpec will test CanSupport() but mount helper is absent + return + } plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins([]volume.VolumePlugin{&nfsPlugin{nil, newMockRecycler, volume.VolumeConfig{}}}, volume.NewFakeVolumeHost("/tmp/fake", nil, nil)) From b356fb0357a6755d7957dfa17869d22a4579f423 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 30 Oct 2015 17:02:21 -0400 Subject: [PATCH 5/5] UPSTREAM: 16033: Mount returns verbose error --- .../src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go index 54e926ac45b3..18c16edf2e1f 100644 --- a/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go +++ b/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/mount/mount_linux.go @@ -99,7 +99,7 @@ func doMount(source string, target string, fstype string, options []string) erro command := exec.Command("mount", mountArgs...) output, err := command.CombinedOutput() if err != nil { - glog.Errorf("Mount failed: %v\nMounting arguments: %s %s %s %v\nOutput: %s\n", + return fmt.Errorf("Mount failed: %v\nMounting arguments: %s %s %s %v\nOutput: %s\n", err, source, target, fstype, options, string(output)) } return err @@ -130,8 +130,7 @@ func (mounter *Mounter) Unmount(target string) error { command := exec.Command("umount", target) output, err := command.CombinedOutput() if err != nil { - glog.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output)) - return err + return fmt.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output)) } return nil }