From ac21ac24fa4d27775f2e75c67ddfcdbc6205a151 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Thu, 15 Jan 2015 21:30:13 -0800 Subject: [PATCH] Add a mounter that uses google's safe_format_and_mount. --- pkg/kubelet/volume/gce_pd/gce_util.go | 29 ++++++++++ pkg/kubelet/volume/gce_pd/gce_util_test.go | 63 ++++++++++++++++++++++ pkg/util/mount/linux.go | 8 +-- pkg/util/mount/mount.go | 2 +- 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/pkg/kubelet/volume/gce_pd/gce_util.go b/pkg/kubelet/volume/gce_pd/gce_util.go index cec0212faf6..3d94a76a96a 100644 --- a/pkg/kubelet/volume/gce_pd/gce_util.go +++ b/pkg/kubelet/volume/gce_pd/gce_util.go @@ -28,7 +28,9 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/gce" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/mount" + "github.com/golang/glog" ) const partitionRegex = "[a-z][a-z]*(?P[0-9][0-9]*)?" @@ -139,3 +141,30 @@ func (util *GCEDiskUtil) DetachDisk(pd *gcePersistentDisk, devicePath string) er } return nil } + +// safe_format_and_mount is a utility script on GCE VMs that probes a persistent disk, and if +// necessary formats it before mounting it. +// This eliminates the necesisty to format a PD before it is used with a Pod on GCE. +// TODO: port this script into Go and use it for all Linux platforms +type gceSafeFormatAndMount struct { + mount.Mounter + runner exec.Interface +} + +// uses /usr/share/google/safe_format_and_mount to optionally mount, and format a disk +func (mounter *gceSafeFormatAndMount) Mount(source string, target string, fstype string, flags uintptr, data string) error { + args := []string{} + // ext4 is the default for safe_format_and_mount + if len(fstype) > 0 && fstype != "ext4" { + args = append(args, "-m", fmt.Sprintf("mkfs.%s", fstype)) + } + args = append(args, source, target) + // TODO: Accept other options here? + glog.V(5).Infof("exec-ing: /usr/share/google/safe_format_and_mount %v", args) + cmd := mounter.runner.Command("/usr/share/google/safe_format_and_mount", args...) + dataOut, err := cmd.CombinedOutput() + if err != nil { + glog.V(5).Infof("error running /usr/share/google/safe_format_and_mount\n%s", string(dataOut)) + } + return err +} diff --git a/pkg/kubelet/volume/gce_pd/gce_util_test.go b/pkg/kubelet/volume/gce_pd/gce_util_test.go index 1770e76308a..9f3d721284e 100644 --- a/pkg/kubelet/volume/gce_pd/gce_util_test.go +++ b/pkg/kubelet/volume/gce_pd/gce_util_test.go @@ -17,7 +17,10 @@ limitations under the License. package gce_pd import ( + "fmt" "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" ) func TestGetDeviceName(t *testing.T) { @@ -53,3 +56,63 @@ func TestGetDeviceName(t *testing.T) { } } } + +func TestSafeFormatAndMount(t *testing.T) { + tests := []struct { + fstype string + expectedArgs []string + err error + }{ + { + fstype: "ext4", + expectedArgs: []string{"/dev/foo", "/mnt/bar"}, + }, + { + fstype: "vfat", + expectedArgs: []string{"-m", "mkfs.vfat", "/dev/foo", "/mnt/bar"}, + }, + { + err: fmt.Errorf("test error"), + }, + } + for _, test := range tests { + + var cmdOut string + var argsOut []string + fake := exec.FakeExec{ + CommandScript: []exec.FakeCommandAction{ + func(cmd string, args ...string) exec.Cmd { + cmdOut = cmd + argsOut = args + fake := exec.FakeCmd{ + CombinedOutputScript: []exec.FakeCombinedOutputAction{ + func() ([]byte, error) { return []byte{}, test.err }, + }, + } + return exec.InitFakeCmd(&fake, cmd, args...) + }, + }, + } + + mounter := gceSafeFormatAndMount{ + runner: &fake, + } + + err := mounter.Mount("/dev/foo", "/mnt/bar", test.fstype, 0, "") + if test.err == nil && err != nil { + t.Errorf("unexpected error: %v", err) + } + if test.err != nil { + if err == nil { + t.Errorf("unexpected non-error") + } + return + } + if cmdOut != "/usr/share/google/safe_format_and_mount" { + t.Errorf("unexpected command: %s", cmdOut) + } + if len(argsOut) != len(test.expectedArgs) { + t.Errorf("unexpected args: %v, expected: %v", argsOut, test.expectedArgs) + } + } +} diff --git a/pkg/util/mount/linux.go b/pkg/util/mount/linux.go index 9d8e75effd0..24e219f9427 100644 --- a/pkg/util/mount/linux.go +++ b/pkg/util/mount/linux.go @@ -34,23 +34,23 @@ import ( const FlagBind = syscall.MS_BIND const FlagReadOnly = syscall.MS_RDONLY -type mounter struct{} +type Mounter struct{} // Wraps syscall.Mount() -func (mounter *mounter) Mount(source string, target string, fstype string, flags uintptr, data string) error { +func (mounter *Mounter) Mount(source string, target string, fstype string, flags uintptr, data string) error { glog.V(5).Infof("Mounting %s %s %s %d %s", source, target, fstype, flags, data) return syscall.Mount(source, target, fstype, flags, data) } // Wraps syscall.Unmount() -func (mounter *mounter) Unmount(target string, flags int) error { +func (mounter *Mounter) Unmount(target string, flags int) error { return syscall.Unmount(target, flags) } // How many times to retry for a consistent read of /proc/mounts. const maxListTries = 3 -func (mounter *mounter) List() ([]MountPoint, error) { +func (*Mounter) List() ([]MountPoint, error) { hash1, err := readProcMounts(nil) if err != nil { return nil, err diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index c00f859d87f..d5cd7a65df8 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -37,7 +37,7 @@ type Interface interface { // New returns a mount.Interface for the current system. func New() Interface { - return &mounter{} + return &Mounter{} } // This represents a single line in /proc/mounts or /etc/fstab.