go-ceph/cephfs/cephfs_test.go

358 lines
8.2 KiB
Go

package cephfs
import (
"encoding/json"
"fmt"
"os"
"syscall"
"testing"
"time"
"github.com/ceph/go-ceph/rados"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
CephMountTest = "/tmp/ceph/mds/mnt/"
)
func TestCreateMount(t *testing.T) {
mount := fsConnect(t)
mount, err := CreateMount()
assert.NoError(t, err)
assert.NotNil(t, mount)
}
func fsConnect(t *testing.T) *MountInfo {
mount, err := CreateMount()
require.NoError(t, err)
require.NotNil(t, mount)
err = mount.ReadDefaultConfigFile()
require.NoError(t, err)
timeout := time.After(time.Second * 5)
ch := make(chan error)
go func(mount *MountInfo) {
ch <- mount.Mount()
}(mount)
select {
case err = <-ch:
case <-timeout:
err = fmt.Errorf("timed out waiting for connect")
}
require.NoError(t, err)
return mount
}
func TestMountRoot(t *testing.T) {
fsConnect(t)
}
func TestSyncFs(t *testing.T) {
mount := fsConnect(t)
err := mount.SyncFs()
assert.NoError(t, err)
}
func TestChangeDir(t *testing.T) {
mount := fsConnect(t)
dir1 := mount.CurrentDir()
assert.NotNil(t, dir1)
err := mount.MakeDir("/asdf", 0755)
assert.NoError(t, err)
err = mount.ChangeDir("/asdf")
assert.NoError(t, err)
dir2 := mount.CurrentDir()
assert.NotNil(t, dir2)
assert.NotEqual(t, dir1, dir2)
assert.Equal(t, dir1, "/")
assert.Equal(t, dir2, "/asdf")
}
func TestRemoveDir(t *testing.T) {
dirname := "one"
mount := fsConnect(t)
err := mount.MakeDir(dirname, 0755)
assert.NoError(t, err)
err = mount.SyncFs()
assert.NoError(t, err)
// os.Stat the actual mounted location to verify Makedir/RemoveDir
_, err = os.Stat(CephMountTest + dirname)
assert.NoError(t, err)
err = mount.RemoveDir(dirname)
assert.NoError(t, err)
_, err = os.Stat(CephMountTest + dirname)
assert.EqualError(t, err,
fmt.Sprintf("stat %s: no such file or directory", CephMountTest+dirname))
}
func TestUnmountMount(t *testing.T) {
t.Run("neverMounted", func(t *testing.T) {
mount, err := CreateMount()
require.NoError(t, err)
require.NotNil(t, mount)
assert.False(t, mount.IsMounted())
})
t.Run("mountUnmount", func(t *testing.T) {
mount := fsConnect(t)
assert.True(t, mount.IsMounted())
err := mount.Unmount()
assert.NoError(t, err)
assert.False(t, mount.IsMounted())
})
}
func TestReleaseMount(t *testing.T) {
mount, err := CreateMount()
assert.NoError(t, err)
require.NotNil(t, mount)
err = mount.Release()
assert.NoError(t, err)
}
func TestChmodDir(t *testing.T) {
dirname := "two"
var stats_before uint32 = 0755
var stats_after uint32 = 0700
mount := fsConnect(t)
err := mount.MakeDir(dirname, stats_before)
assert.NoError(t, err)
err = mount.SyncFs()
assert.NoError(t, err)
// os.Stat the actual mounted location to verify Makedir/RemoveDir
stats, err := os.Stat(CephMountTest + dirname)
require.NoError(t, err)
assert.Equal(t, uint32(stats.Mode().Perm()), stats_before)
err = mount.Chmod(dirname, stats_after)
assert.NoError(t, err)
stats, err = os.Stat(CephMountTest + dirname)
assert.Equal(t, uint32(stats.Mode().Perm()), stats_after)
}
// Not cross-platform, go's os does not specifiy Sys return type
func TestChown(t *testing.T) {
dirname := "three"
// dockerfile creates bob user account
var bob uint32 = 1010
var root uint32
mount := fsConnect(t)
err := mount.MakeDir(dirname, 0755)
assert.NoError(t, err)
err = mount.SyncFs()
assert.NoError(t, err)
// os.Stat the actual mounted location to verify Makedir/RemoveDir
stats, err := os.Stat(CephMountTest + dirname)
require.NoError(t, err)
assert.Equal(t, uint32(stats.Sys().(*syscall.Stat_t).Uid), root)
assert.Equal(t, uint32(stats.Sys().(*syscall.Stat_t).Gid), root)
err = mount.Chown(dirname, bob, bob)
assert.NoError(t, err)
stats, err = os.Stat(CephMountTest + dirname)
assert.NoError(t, err)
assert.Equal(t, uint32(stats.Sys().(*syscall.Stat_t).Uid), bob)
assert.Equal(t, uint32(stats.Sys().(*syscall.Stat_t).Gid), bob)
}
func TestCephFSError(t *testing.T) {
err := getError(0)
assert.NoError(t, err)
err = getError(-5) // IO error
assert.Error(t, err)
assert.Equal(t, err.Error(), "cephfs: ret=5, Input/output error")
err = getError(345) // no such errno
assert.Error(t, err)
assert.Equal(t, err.Error(), "cephfs: ret=345")
}
func radosConnect(t *testing.T) *rados.Conn {
conn, err := rados.NewConn()
require.NoError(t, err)
err = conn.ReadDefaultConfigFile()
require.NoError(t, err)
timeout := time.After(time.Second * 5)
ch := make(chan error)
go func(conn *rados.Conn) {
ch <- conn.Connect()
}(conn)
select {
case err = <-ch:
case <-timeout:
err = fmt.Errorf("timed out waiting for connect")
}
require.NoError(t, err)
return conn
}
func TestCreateFromRados(t *testing.T) {
conn := radosConnect(t)
mount, err := CreateFromRados(conn)
assert.NoError(t, err)
assert.NotNil(t, mount)
}
func TestCreateMountWithId(t *testing.T) {
mount, err := CreateMountWithId("bobolink")
assert.NoError(t, err)
assert.NotNil(t, mount)
err = mount.ReadDefaultConfigFile()
assert.NoError(t, err)
err = mount.Mount()
assert.NoError(t, err)
// verify the custom entity_id is visible in the 'session ls' output
// of mds.
cmd := []byte(`{"prefix": "session ls"}`)
buf, info, err := mount.MdsCommand(
"Z", // TODO: fix hard-coded name mds (from ci container script)
[][]byte{cmd})
assert.NoError(t, err)
assert.NotEqual(t, "", string(buf))
assert.Equal(t, "", string(info))
assert.Contains(t, string(buf), `"bobolink"`)
}
func TestMdsCommand(t *testing.T) {
mount := fsConnect(t)
cmd := []byte(`{"prefix": "client ls"}`)
buf, info, err := mount.MdsCommand(
"Z", // TODO: fix hard-coded name mds (from ci container script)
[][]byte{cmd})
assert.NoError(t, err)
assert.NotEqual(t, "", string(buf))
assert.Equal(t, "", string(info))
assert.Contains(t, string(buf), "ceph_version")
// response should also be valid json
var j []interface{}
err = json.Unmarshal(buf, &j)
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(j), 1)
}
func TestMdsCommandError(t *testing.T) {
mount := fsConnect(t)
cmd := []byte("iAMinValId~~~")
buf, info, err := mount.MdsCommand(
"Z", // TODO: fix hard-coded name mds (from ci container script)
[][]byte{cmd})
assert.Error(t, err)
assert.Equal(t, "", string(buf))
assert.NotEqual(t, "", string(info))
assert.Contains(t, string(info), "unparseable JSON")
}
func TestMountWithRoot(t *testing.T) {
bMount := fsConnect(t)
defer func() {
assert.NoError(t, bMount.Unmount())
assert.NoError(t, bMount.Release())
}()
dir1 := "/test-mount-with-root"
err := bMount.MakeDir(dir1, 0755)
assert.NoError(t, err)
defer bMount.RemoveDir(dir1)
sub1 := "/i.was.here"
dir2 := dir1 + sub1
err = bMount.MakeDir(dir2, 0755)
assert.NoError(t, err)
defer bMount.RemoveDir(dir2)
t.Run("withRoot", func(t *testing.T) {
mount, err := CreateMount()
require.NoError(t, err)
require.NotNil(t, mount)
defer func() {
assert.NoError(t, mount.Unmount())
assert.NoError(t, mount.Release())
}()
err = mount.ReadDefaultConfigFile()
require.NoError(t, err)
err = mount.MountWithRoot(dir1)
assert.NoError(t, err)
err = mount.ChangeDir(sub1)
assert.NoError(t, err)
})
t.Run("badRoot", func(t *testing.T) {
mount, err := CreateMount()
require.NoError(t, err)
require.NotNil(t, mount)
defer func() {
assert.NoError(t, mount.Release())
}()
err = mount.ReadDefaultConfigFile()
require.NoError(t, err)
err = mount.MountWithRoot("/i-yam-what-i-yam")
assert.Error(t, err)
})
}
func TestGetSetConfigOption(t *testing.T) {
// we don't need an active connection for this, just the handle
mount, err := CreateMount()
require.NoError(t, err)
require.NotNil(t, mount)
err = mount.SetConfigOption("__dne__", "value")
assert.Error(t, err)
_, err = mount.GetConfigOption("__dne__")
assert.Error(t, err)
origVal, err := mount.GetConfigOption("log_file")
assert.NoError(t, err)
err = mount.SetConfigOption("log_file", "/dev/null")
assert.NoError(t, err)
currVal, err := mount.GetConfigOption("log_file")
assert.NoError(t, err)
assert.Equal(t, "/dev/null", currVal)
err = mount.SetConfigOption("log_file", origVal)
assert.NoError(t, err)
currVal, err = mount.GetConfigOption("log_file")
assert.NoError(t, err)
assert.Equal(t, origVal, currVal)
}