Skip to content

Commit

Permalink
Add a mounter that uses google's safe_format_and_mount.
Browse files Browse the repository at this point in the history
  • Loading branch information
brendandburns committed Feb 3, 2015
1 parent 6ba8b7d commit ac21ac2
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 5 deletions.
29 changes: 29 additions & 0 deletions pkg/kubelet/volume/gce_pd/gce_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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<partition>[0-9][0-9]*)?"
Expand Down Expand Up @@ -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
}
63 changes: 63 additions & 0 deletions pkg/kubelet/volume/gce_pd/gce_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}
}
8 changes: 4 additions & 4 deletions pkg/util/mount/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/util/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit ac21ac2

Please sign in to comment.