mirror of
https://github.com/ceph/go-ceph
synced 2024-12-18 12:25:22 +00:00
66ffbfa633
Recent changes to ceph check that a directory exists in cephfs before it will allow an NFS export of the dir to be created. It was a pretty shaky practice of ours to create NFS exports without dirs backing them anyhow. This change ensures the dirs exist before the test cases are run and clean them up afterwards. Signed-off-by: John Mulligan <jmulligan@redhat.com>
351 lines
8.1 KiB
Go
351 lines
8.1 KiB
Go
//go:build !(nautilus || octopus)
|
|
// +build !nautilus,!octopus
|
|
|
|
package nfs
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
tsuite "github.com/stretchr/testify/suite"
|
|
|
|
"github.com/ceph/go-ceph/cephfs"
|
|
"github.com/ceph/go-ceph/internal/admintest"
|
|
"github.com/ceph/go-ceph/internal/commands"
|
|
"github.com/ceph/go-ceph/rados"
|
|
)
|
|
|
|
var (
|
|
radosConnector = admintest.NewConnector()
|
|
usableDirNames = []string{"january", "february", "march", "sept"}
|
|
)
|
|
|
|
func TestNFSAdmin(t *testing.T) {
|
|
tsuite.Run(t, new(NFSAdminSuite))
|
|
}
|
|
|
|
// NFSAdminSuite is a suite of tests for the nfs admin package.
|
|
// A suite is used because creating/managing NFS has certain expectations for
|
|
// the cluster that we may need to mock, especially when running in the
|
|
// standard go-ceph test container. Using a suite allows us to have suite
|
|
// setup/validate the environment and the tests can largely be ignorant of the
|
|
// environment.
|
|
type NFSAdminSuite struct {
|
|
tsuite.Suite
|
|
|
|
fileSystemName string
|
|
clusterID string
|
|
mockConfig bool
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) SetupSuite() {
|
|
suite.fileSystemName = "cephfs"
|
|
suite.clusterID = "goceph"
|
|
suite.mockConfig = true
|
|
|
|
mock := os.Getenv("GO_CEPH_TEST_MOCK_NFS")
|
|
if ok, err := strconv.ParseBool(mock); err == nil {
|
|
suite.mockConfig = ok
|
|
}
|
|
|
|
if suite.mockConfig {
|
|
suite.setupMockNFSConfig()
|
|
}
|
|
suite.setupDirs()
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) TearDownSuite() {
|
|
suite.removeDirs()
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) setupMockNFSConfig() {
|
|
require := suite.Require()
|
|
conn := radosConnector.GetConn(suite.T())
|
|
|
|
err := conn.MakePool(".nfs")
|
|
if err != nil && !errors.Is(err, rados.ErrObjectExists) {
|
|
suite.T().Fatalf("failed to make pool: %v", err)
|
|
}
|
|
|
|
ioctx, err := conn.OpenIOContext(".nfs")
|
|
require.NoError(err)
|
|
defer ioctx.Destroy()
|
|
|
|
ioctx.SetNamespace(suite.clusterID)
|
|
|
|
err = ioctx.Create(
|
|
fmt.Sprintf("conf-nfs.%s", suite.clusterID),
|
|
rados.CreateIdempotent)
|
|
require.NoError(err)
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) setupDirs() {
|
|
require := suite.Require()
|
|
conn := radosConnector.GetConn(suite.T())
|
|
fs, err := cephfs.CreateFromRados(conn)
|
|
require.NoError(err)
|
|
defer func() {
|
|
require.NoError(fs.Release())
|
|
}()
|
|
err = fs.Mount()
|
|
require.NoError(err)
|
|
defer func() {
|
|
require.NoError(fs.Unmount())
|
|
}()
|
|
|
|
// establish a "random" list of dir names to work with
|
|
for _, name := range usableDirNames {
|
|
err = fs.MakeDir(name, 0777)
|
|
require.NoError(err, "failed to make dir "+name)
|
|
}
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) removeDirs() {
|
|
require := suite.Require()
|
|
conn := radosConnector.GetConn(suite.T())
|
|
fs, err := cephfs.CreateFromRados(conn)
|
|
require.NoError(err)
|
|
defer func() {
|
|
require.NoError(fs.Release())
|
|
}()
|
|
err = fs.Mount()
|
|
require.NoError(err)
|
|
defer func() {
|
|
require.NoError(fs.Unmount())
|
|
}()
|
|
|
|
// establish a "random" list of dir names to work with
|
|
for _, name := range usableDirNames {
|
|
err = fs.RemoveDir(name)
|
|
require.NoError(err, "failed to remove dir "+name)
|
|
}
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) TestCreateDeleteCephFSExport() {
|
|
require := suite.Require()
|
|
ra := radosConnector.Get(suite.T())
|
|
nfsa := NewFromConn(ra)
|
|
|
|
res, err := nfsa.CreateCephFSExport(CephFSExportSpec{
|
|
FileSystemName: suite.fileSystemName,
|
|
ClusterID: suite.clusterID,
|
|
PseudoPath: "/cheese",
|
|
})
|
|
require.NoError(err)
|
|
require.Equal("/cheese", res.Bind)
|
|
|
|
err = nfsa.RemoveExport(suite.clusterID, "/cheese")
|
|
require.NoError(err)
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) TestListDetailedExports() {
|
|
require := suite.Require()
|
|
ra := radosConnector.Get(suite.T())
|
|
nfsa := NewFromConn(ra)
|
|
|
|
_, err := nfsa.CreateCephFSExport(CephFSExportSpec{
|
|
FileSystemName: suite.fileSystemName,
|
|
ClusterID: suite.clusterID,
|
|
PseudoPath: "/01",
|
|
Path: "/january",
|
|
})
|
|
require.NoError(err)
|
|
|
|
defer func() {
|
|
err = nfsa.RemoveExport(suite.clusterID, "/01")
|
|
require.NoError(err)
|
|
}()
|
|
|
|
_, err = nfsa.CreateCephFSExport(CephFSExportSpec{
|
|
FileSystemName: suite.fileSystemName,
|
|
ClusterID: suite.clusterID,
|
|
PseudoPath: "/02",
|
|
Path: "/february",
|
|
})
|
|
require.NoError(err)
|
|
|
|
defer func() {
|
|
err = nfsa.RemoveExport(suite.clusterID, "/02")
|
|
require.NoError(err)
|
|
}()
|
|
|
|
l, err := nfsa.ListDetailedExports(suite.clusterID)
|
|
require.NoError(err)
|
|
require.Len(l, 2)
|
|
var e1, e2 ExportInfo
|
|
for _, e := range l {
|
|
if e.PseudoPath == "/01" {
|
|
e1 = e
|
|
}
|
|
if e.PseudoPath == "/02" {
|
|
e2 = e
|
|
}
|
|
}
|
|
require.Equal(e1.PseudoPath, "/01")
|
|
require.Equal(e2.PseudoPath, "/02")
|
|
}
|
|
|
|
func (suite *NFSAdminSuite) TestExportInfo() {
|
|
require := suite.Require()
|
|
ra := radosConnector.Get(suite.T())
|
|
nfsa := NewFromConn(ra)
|
|
|
|
_, err := nfsa.CreateCephFSExport(CephFSExportSpec{
|
|
FileSystemName: suite.fileSystemName,
|
|
ClusterID: suite.clusterID,
|
|
PseudoPath: "/03",
|
|
Path: "/march",
|
|
})
|
|
require.NoError(err)
|
|
|
|
defer func() {
|
|
err = nfsa.RemoveExport(suite.clusterID, "/03")
|
|
require.NoError(err)
|
|
}()
|
|
|
|
e1, err := nfsa.ExportInfo(suite.clusterID, "/03")
|
|
require.NoError(err)
|
|
require.Equal(e1.PseudoPath, "/03")
|
|
|
|
_, err = nfsa.ExportInfo(suite.clusterID, "/88")
|
|
require.Error(err)
|
|
}
|
|
|
|
const resultExport1 = `{
|
|
"bind": "/cheese",
|
|
"fs": "cephfs",
|
|
"path": "/",
|
|
"cluster": "foobar",
|
|
"mode": "RW"
|
|
}
|
|
`
|
|
|
|
func TestParseExportResult(t *testing.T) {
|
|
t.Run("resultExport", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(resultExport1), "", nil)
|
|
e, err := parseExportResult(r)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, e.Bind, "/cheese")
|
|
})
|
|
t.Run("errorSet", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(""), "", errors.New("beep"))
|
|
_, err := parseExportResult(r)
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("statusSet", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(""), "boo", nil)
|
|
_, err := parseExportResult(r)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// # ceph nfs export ls --cluster-id foobar --detailed
|
|
const exportList1 = `[
|
|
{
|
|
"export_id": 1,
|
|
"path": "/",
|
|
"cluster_id": "foobar",
|
|
"pseudo": "/cheese",
|
|
"access_type": "RW",
|
|
"squash": "none",
|
|
"security_label": true,
|
|
"protocols": [
|
|
4
|
|
],
|
|
"transports": [
|
|
"TCP"
|
|
],
|
|
"fsal": {
|
|
"name": "CEPH",
|
|
"user_id": "nfs.foobar.1",
|
|
"fs_name": "cephfs"
|
|
},
|
|
"clients": []
|
|
}
|
|
]`
|
|
|
|
func TestParseExportsList(t *testing.T) {
|
|
t.Run("exportList1", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(exportList1), "", nil)
|
|
l, err := parseExportsList(r)
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, l, 1) {
|
|
e := l[0]
|
|
assert.Equal(t, e.Path, "/")
|
|
assert.Equal(t, e.PseudoPath, "/cheese")
|
|
if assert.Len(t, e.Protocols, 1) {
|
|
assert.Equal(t, e.Protocols[0], 4)
|
|
}
|
|
if assert.Len(t, e.Transports, 1) {
|
|
assert.Equal(t, e.Transports[0], "TCP")
|
|
}
|
|
assert.Equal(t, e.FSAL.Name, "CEPH")
|
|
}
|
|
})
|
|
t.Run("errorSet", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(""), "", errors.New("beep"))
|
|
_, err := parseExportsList(r)
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("statusSet", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(""), "boo", nil)
|
|
_, err := parseExportsList(r)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// # ceph nfs export info --cluster-id foobar --pseudo-path /cheese
|
|
const exportInfo1 = `{
|
|
"export_id": 1,
|
|
"path": "/",
|
|
"cluster_id": "foobar",
|
|
"pseudo": "/cheese",
|
|
"access_type": "RW",
|
|
"squash": "none",
|
|
"security_label": true,
|
|
"protocols": [
|
|
4
|
|
],
|
|
"transports": [
|
|
"TCP"
|
|
],
|
|
"fsal": {
|
|
"name": "CEPH",
|
|
"user_id": "nfs.foobar.1",
|
|
"fs_name": "cephfs"
|
|
},
|
|
"clients": []
|
|
}
|
|
`
|
|
|
|
func TestParseExportInfo(t *testing.T) {
|
|
t.Run("exportInfo1", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(exportInfo1), "", nil)
|
|
e, err := parseExportInfo(r)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, e.Path, "/")
|
|
assert.Equal(t, e.PseudoPath, "/cheese")
|
|
if assert.Len(t, e.Protocols, 1) {
|
|
assert.Equal(t, e.Protocols[0], 4)
|
|
}
|
|
if assert.Len(t, e.Transports, 1) {
|
|
assert.Equal(t, e.Transports[0], "TCP")
|
|
}
|
|
assert.Equal(t, e.FSAL.Name, "CEPH")
|
|
})
|
|
t.Run("errorSet", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(""), "", errors.New("beep"))
|
|
_, err := parseExportInfo(r)
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("statusSet", func(t *testing.T) {
|
|
r := commands.NewResponse([]byte(""), "boo", nil)
|
|
_, err := parseExportInfo(r)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|