csi-driver-nfs/pkg/nfs/nodeserver_test.go
2023-01-08 02:44:35 +00:00

347 lines
9.6 KiB
Go

/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nfs
import (
"context"
"errors"
"os"
"reflect"
"strings"
"testing"
"github.com/container-storage-interface/spec/lib/go/csi"
"github.com/kubernetes-csi/csi-driver-nfs/test/utils/testutil"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
targetTest = "./target_test"
)
func TestNodePublishVolume(t *testing.T) {
ns, err := getTestNodeServer()
if err != nil {
t.Fatalf(err.Error())
}
params := map[string]string{
"server": "server",
"share": "share",
mountPermissionsField: "0755",
}
paramsWithMetadata := map[string]string{
"server": "server",
"share": "share",
pvcNameKey: "pvcname",
pvcNamespaceKey: "pvcnamespace",
pvNameKey: "pvname",
}
paramsWithZeroPermissions := map[string]string{
"server": "server",
"share": "share",
mountPermissionsField: "0",
}
invalidParams := map[string]string{
"server": "server",
"share": "share",
mountPermissionsField: "07ab",
}
volumeCap := csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}
alreadyMountedTarget := testutil.GetWorkDirPath("false_is_likely_exist_target", t)
targetTest := testutil.GetWorkDirPath("target_test", t)
tests := []struct {
desc string
setup func()
req csi.NodePublishVolumeRequest
skipOnWindows bool
expectedErr error
cleanup func()
}{
{
desc: "[Error] Volume capabilities missing",
req: csi.NodePublishVolumeRequest{},
expectedErr: status.Error(codes.InvalidArgument, "Volume capability missing in request"),
},
{
desc: "[Error] Volume ID missing",
req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap}},
expectedErr: status.Error(codes.InvalidArgument, "Volume ID missing in request"),
},
{
desc: "[Error] Target path missing",
req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1"},
expectedErr: status.Error(codes.InvalidArgument, "Target path not provided"),
},
{
desc: "[Success] Stage target path missing",
req: csi.NodePublishVolumeRequest{
VolumeContext: params,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest},
expectedErr: nil,
},
{
desc: "[Success] Valid request read only",
req: csi.NodePublishVolumeRequest{
VolumeContext: params,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest,
Readonly: true},
expectedErr: nil,
},
{
desc: "[Success] Valid request already mounted",
req: csi.NodePublishVolumeRequest{
VolumeContext: params,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: alreadyMountedTarget,
Readonly: true},
expectedErr: nil,
},
{
desc: "[Success] Valid request",
req: csi.NodePublishVolumeRequest{
VolumeContext: params,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest,
Readonly: true},
expectedErr: nil,
},
{
desc: "[Success] Valid request with pv/pvc metadata",
req: csi.NodePublishVolumeRequest{
VolumeContext: paramsWithMetadata,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest,
Readonly: true},
expectedErr: nil,
},
{
desc: "[Success] Valid request with 0 mountPermissions",
req: csi.NodePublishVolumeRequest{
VolumeContext: paramsWithZeroPermissions,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest,
Readonly: true},
expectedErr: nil,
},
{
desc: "[Error] invalid mountPermissions",
req: csi.NodePublishVolumeRequest{
VolumeContext: invalidParams,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest,
Readonly: true},
expectedErr: status.Error(codes.InvalidArgument, "invalid mountPermissions 07ab"),
},
}
// setup
_ = makeDir(alreadyMountedTarget)
_ = makeDir(targetTest)
for _, tc := range tests {
if tc.setup != nil {
tc.setup()
}
_, err := ns.NodePublishVolume(context.Background(), &tc.req)
if !reflect.DeepEqual(err, tc.expectedErr) {
t.Errorf("Desc:%v\nUnexpected error: %v\nExpected: %v", tc.desc, err, tc.expectedErr)
}
if tc.cleanup != nil {
tc.cleanup()
}
}
// Clean up
err = os.RemoveAll(targetTest)
assert.NoError(t, err)
err = os.RemoveAll(alreadyMountedTarget)
assert.NoError(t, err)
}
func TestNodeUnpublishVolume(t *testing.T) {
ns, err := getTestNodeServer()
if err != nil {
t.Fatalf(err.Error())
}
errorTarget := testutil.GetWorkDirPath("error_is_likely_target", t)
targetTest := testutil.GetWorkDirPath("target_test", t)
targetFile := testutil.GetWorkDirPath("abc.go", t)
tests := []struct {
desc string
setup func()
req csi.NodeUnpublishVolumeRequest
expectedErr error
cleanup func()
}{
{
desc: "[Error] Volume ID missing",
req: csi.NodeUnpublishVolumeRequest{TargetPath: targetTest},
expectedErr: status.Error(codes.InvalidArgument, "Volume ID missing in request"),
},
{
desc: "[Error] Target missing",
req: csi.NodeUnpublishVolumeRequest{VolumeId: "vol_1"},
expectedErr: status.Error(codes.InvalidArgument, "Target path missing in request"),
},
{
desc: "[Success] Volume not mounted",
req: csi.NodeUnpublishVolumeRequest{TargetPath: targetFile, VolumeId: "vol_1"},
},
}
// Setup
_ = makeDir(errorTarget)
for _, tc := range tests {
if tc.setup != nil {
tc.setup()
}
_, err := ns.NodeUnpublishVolume(context.Background(), &tc.req)
if !reflect.DeepEqual(err, tc.expectedErr) {
if err == nil || tc.expectedErr == nil || !strings.Contains(err.Error(), tc.expectedErr.Error()) {
t.Errorf("Desc:%v\nUnexpected error: %v\nExpected: %v", tc.desc, err, tc.expectedErr)
}
}
if tc.cleanup != nil {
tc.cleanup()
}
}
// Clean up
err = os.RemoveAll(errorTarget)
assert.NoError(t, err)
}
func TestNodeGetInfo(t *testing.T) {
ns, err := getTestNodeServer()
if err != nil {
t.Fatalf(err.Error())
}
// Test valid request
req := csi.NodeGetInfoRequest{}
resp, err := ns.NodeGetInfo(context.Background(), &req)
assert.NoError(t, err)
assert.Equal(t, resp.GetNodeId(), fakeNodeID)
}
func TestNodeGetCapabilities(t *testing.T) {
ns, err := getTestNodeServer()
if err != nil {
t.Fatalf(err.Error())
}
capType := &csi.NodeServiceCapability_Rpc{
Rpc: &csi.NodeServiceCapability_RPC{
Type: csi.NodeServiceCapability_RPC_UNKNOWN,
},
}
capList := []*csi.NodeServiceCapability{{
Type: capType,
}}
ns.Driver.nscap = capList
// Test valid request
req := csi.NodeGetCapabilitiesRequest{}
resp, err := ns.NodeGetCapabilities(context.Background(), &req)
assert.NotNil(t, resp)
assert.Equal(t, resp.Capabilities[0].GetType(), capType)
assert.NoError(t, err)
}
func getTestNodeServer() (NodeServer, error) {
d := NewEmptyDriver("")
mounter, err := NewFakeMounter()
if err != nil {
return NodeServer{}, errors.New("failed to get fake mounter")
}
return NodeServer{
Driver: d,
mounter: mounter,
}, nil
}
func TestNodeGetVolumeStats(t *testing.T) {
nonexistedPath := "/not/a/real/directory"
fakePath := "/tmp/fake-volume-path"
tests := []struct {
desc string
req csi.NodeGetVolumeStatsRequest
expectedErr error
}{
{
desc: "[Error] Volume ID missing",
req: csi.NodeGetVolumeStatsRequest{VolumePath: targetTest},
expectedErr: status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty"),
},
{
desc: "[Error] VolumePath missing",
req: csi.NodeGetVolumeStatsRequest{VolumeId: "vol_1"},
expectedErr: status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty"),
},
{
desc: "[Error] Incorrect volume path",
req: csi.NodeGetVolumeStatsRequest{VolumePath: nonexistedPath, VolumeId: "vol_1"},
expectedErr: status.Errorf(codes.NotFound, "path /not/a/real/directory does not exist"),
},
{
desc: "[Success] Standard success",
req: csi.NodeGetVolumeStatsRequest{VolumePath: fakePath, VolumeId: "vol_1"},
expectedErr: nil,
},
}
// Setup
_ = makeDir(fakePath)
ns, err := getTestNodeServer()
if err != nil {
t.Fatalf(err.Error())
}
for _, test := range tests {
_, err := ns.NodeGetVolumeStats(context.Background(), &test.req)
if !reflect.DeepEqual(err, test.expectedErr) {
t.Errorf("desc: %v, expected error: %v, actual error: %v", test.desc, test.expectedErr, err)
}
}
// Clean up
err = os.RemoveAll(fakePath)
assert.NoError(t, err)
}