2019-10-22 13:20:26 +00:00
package csi
import (
"context"
2020-01-24 16:20:23 +00:00
"errors"
2019-10-22 13:20:26 +00:00
"fmt"
"testing"
csipbv1 "github.com/container-storage-interface/spec/lib/go/csi"
"github.com/golang/protobuf/ptypes/wrappers"
2020-04-30 21:12:32 +00:00
"github.com/hashicorp/nomad/nomad/structs"
2019-10-22 13:20:26 +00:00
fake "github.com/hashicorp/nomad/plugins/csi/testing"
"github.com/stretchr/testify/require"
2020-05-18 12:23:17 +00:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2019-10-22 13:20:26 +00:00
)
2019-12-18 12:18:07 +00:00
func newTestClient ( ) ( * fake . IdentityClient , * fake . ControllerClient , * fake . NodeClient , CSIPlugin ) {
ic := fake . NewIdentityClient ( )
cc := fake . NewControllerClient ( )
nc := fake . NewNodeClient ( )
2019-10-22 13:20:26 +00:00
client := & client {
2019-12-16 12:31:09 +00:00
identityClient : ic ,
controllerClient : cc ,
2019-12-18 12:18:07 +00:00
nodeClient : nc ,
2019-10-22 13:20:26 +00:00
}
2019-12-18 12:18:07 +00:00
return ic , cc , nc , client
2019-10-22 13:20:26 +00:00
}
func TestClient_RPC_PluginProbe ( t * testing . T ) {
cases := [ ] struct {
Name string
ResponseErr error
ProbeResponse * csipbv1 . ProbeResponse
ExpectedResponse bool
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
ResponseErr : fmt . Errorf ( "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "some grpc error" ) ,
} ,
{
Name : "returns false for ready when the provider returns false" ,
ProbeResponse : & csipbv1 . ProbeResponse {
Ready : & wrappers . BoolValue { Value : false } ,
} ,
ExpectedResponse : false ,
} ,
{
Name : "returns true for ready when the provider returns true" ,
ProbeResponse : & csipbv1 . ProbeResponse {
Ready : & wrappers . BoolValue { Value : true } ,
} ,
ExpectedResponse : true ,
} ,
{
/ * When a SP does not return a ready value , a CO MAY treat this as ready .
We do so because example plugins rely on this behaviour . We may
re - evaluate this decision in the future . * /
Name : "returns true for ready when the provider returns a nil wrapper" ,
ProbeResponse : & csipbv1 . ProbeResponse {
Ready : nil ,
} ,
ExpectedResponse : true ,
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2019-12-18 12:18:07 +00:00
ic , _ , _ , client := newTestClient ( )
2019-10-22 13:20:26 +00:00
defer client . Close ( )
2020-05-18 12:23:17 +00:00
ic . NextErr = tc . ResponseErr
ic . NextPluginProbe = tc . ProbeResponse
2019-10-22 13:20:26 +00:00
resp , err := client . PluginProbe ( context . TODO ( ) )
2020-05-18 12:23:17 +00:00
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2019-10-22 13:20:26 +00:00
}
2020-05-18 12:23:17 +00:00
require . Equal ( t , tc . ExpectedResponse , resp )
2019-10-22 13:20:26 +00:00
} )
}
}
func TestClient_RPC_PluginInfo ( t * testing . T ) {
cases := [ ] struct {
2020-03-09 13:57:59 +00:00
Name string
ResponseErr error
InfoResponse * csipbv1 . GetPluginInfoResponse
ExpectedResponseName string
ExpectedResponseVersion string
ExpectedErr error
2019-10-22 13:20:26 +00:00
} {
{
Name : "handles underlying grpc errors" ,
ResponseErr : fmt . Errorf ( "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "some grpc error" ) ,
} ,
{
Name : "returns an error if we receive an empty `name`" ,
InfoResponse : & csipbv1 . GetPluginInfoResponse {
2020-03-09 13:57:59 +00:00
Name : "" ,
VendorVersion : "" ,
2019-10-22 13:20:26 +00:00
} ,
ExpectedErr : fmt . Errorf ( "PluginGetInfo: plugin returned empty name field" ) ,
} ,
{
Name : "returns the name when successfully retrieved and not empty" ,
InfoResponse : & csipbv1 . GetPluginInfoResponse {
2020-03-09 13:57:59 +00:00
Name : "com.hashicorp.storage" ,
VendorVersion : "1.0.1" ,
2019-10-22 13:20:26 +00:00
} ,
2020-03-09 13:57:59 +00:00
ExpectedResponseName : "com.hashicorp.storage" ,
ExpectedResponseVersion : "1.0.1" ,
2019-10-22 13:20:26 +00:00
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2019-12-18 12:18:07 +00:00
ic , _ , _ , client := newTestClient ( )
2019-10-22 13:20:26 +00:00
defer client . Close ( )
2020-05-18 12:23:17 +00:00
ic . NextErr = tc . ResponseErr
ic . NextPluginInfo = tc . InfoResponse
2019-10-22 13:20:26 +00:00
2020-03-09 13:57:59 +00:00
name , version , err := client . PluginGetInfo ( context . TODO ( ) )
2020-05-18 12:23:17 +00:00
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2019-10-22 13:20:26 +00:00
}
2020-05-18 12:23:17 +00:00
require . Equal ( t , tc . ExpectedResponseName , name )
require . Equal ( t , tc . ExpectedResponseVersion , version )
2019-10-22 13:20:26 +00:00
} )
}
}
func TestClient_RPC_PluginGetCapabilities ( t * testing . T ) {
cases := [ ] struct {
Name string
ResponseErr error
Response * csipbv1 . GetPluginCapabilitiesResponse
ExpectedResponse * PluginCapabilitySet
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
ResponseErr : fmt . Errorf ( "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "some grpc error" ) ,
} ,
{
Name : "HasControllerService is true when it's part of the response" ,
Response : & csipbv1 . GetPluginCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . PluginCapability {
{
Type : & csipbv1 . PluginCapability_Service_ {
Service : & csipbv1 . PluginCapability_Service {
Type : csipbv1 . PluginCapability_Service_CONTROLLER_SERVICE ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & PluginCapabilitySet { hasControllerService : true } ,
} ,
{
Name : "HasTopologies is true when it's part of the response" ,
Response : & csipbv1 . GetPluginCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . PluginCapability {
{
Type : & csipbv1 . PluginCapability_Service_ {
Service : & csipbv1 . PluginCapability_Service {
Type : csipbv1 . PluginCapability_Service_VOLUME_ACCESSIBILITY_CONSTRAINTS ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & PluginCapabilitySet { hasTopologies : true } ,
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2019-12-18 12:18:07 +00:00
ic , _ , _ , client := newTestClient ( )
2019-10-22 13:20:26 +00:00
defer client . Close ( )
2020-05-18 12:23:17 +00:00
ic . NextErr = tc . ResponseErr
ic . NextPluginCapabilities = tc . Response
2019-10-22 13:20:26 +00:00
resp , err := client . PluginGetCapabilities ( context . TODO ( ) )
2020-05-18 12:23:17 +00:00
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2019-10-22 13:20:26 +00:00
}
2020-05-18 12:23:17 +00:00
require . Equal ( t , tc . ExpectedResponse , resp )
2019-10-22 13:20:26 +00:00
} )
}
2019-12-16 12:31:09 +00:00
}
2019-10-22 13:20:26 +00:00
2019-12-18 10:57:55 +00:00
func TestClient_RPC_ControllerGetCapabilities ( t * testing . T ) {
cases := [ ] struct {
Name string
ResponseErr error
Response * csipbv1 . ControllerGetCapabilitiesResponse
ExpectedResponse * ControllerCapabilitySet
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
ResponseErr : fmt . Errorf ( "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "some grpc error" ) ,
} ,
{
Name : "ignores unknown capabilities" ,
Response : & csipbv1 . ControllerGetCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . ControllerServiceCapability {
{
Type : & csipbv1 . ControllerServiceCapability_Rpc {
Rpc : & csipbv1 . ControllerServiceCapability_RPC {
Type : csipbv1 . ControllerServiceCapability_RPC_GET_CAPACITY ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & ControllerCapabilitySet { } ,
} ,
{
Name : "detects list volumes capabilities" ,
Response : & csipbv1 . ControllerGetCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . ControllerServiceCapability {
{
Type : & csipbv1 . ControllerServiceCapability_Rpc {
Rpc : & csipbv1 . ControllerServiceCapability_RPC {
Type : csipbv1 . ControllerServiceCapability_RPC_LIST_VOLUMES ,
} ,
} ,
} ,
{
Type : & csipbv1 . ControllerServiceCapability_Rpc {
Rpc : & csipbv1 . ControllerServiceCapability_RPC {
Type : csipbv1 . ControllerServiceCapability_RPC_LIST_VOLUMES_PUBLISHED_NODES ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & ControllerCapabilitySet {
HasListVolumes : true ,
HasListVolumesPublishedNodes : true ,
} ,
} ,
{
Name : "detects publish capabilities" ,
Response : & csipbv1 . ControllerGetCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . ControllerServiceCapability {
{
Type : & csipbv1 . ControllerServiceCapability_Rpc {
Rpc : & csipbv1 . ControllerServiceCapability_RPC {
Type : csipbv1 . ControllerServiceCapability_RPC_PUBLISH_READONLY ,
} ,
} ,
} ,
{
Type : & csipbv1 . ControllerServiceCapability_Rpc {
Rpc : & csipbv1 . ControllerServiceCapability_RPC {
Type : csipbv1 . ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & ControllerCapabilitySet {
HasPublishUnpublishVolume : true ,
HasPublishReadonly : true ,
} ,
} ,
}
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2019-12-18 12:18:07 +00:00
_ , cc , _ , client := newTestClient ( )
2019-12-18 10:57:55 +00:00
defer client . Close ( )
cc . NextErr = tc . ResponseErr
cc . NextCapabilitiesResponse = tc . Response
resp , err := client . ControllerGetCapabilities ( context . TODO ( ) )
if tc . ExpectedErr != nil {
2020-05-18 12:23:17 +00:00
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2019-12-18 10:57:55 +00:00
}
require . Equal ( t , tc . ExpectedResponse , resp )
} )
}
}
2019-12-18 12:18:07 +00:00
func TestClient_RPC_NodeGetCapabilities ( t * testing . T ) {
cases := [ ] struct {
Name string
ResponseErr error
Response * csipbv1 . NodeGetCapabilitiesResponse
ExpectedResponse * NodeCapabilitySet
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
ResponseErr : fmt . Errorf ( "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "some grpc error" ) ,
} ,
{
Name : "ignores unknown capabilities" ,
Response : & csipbv1 . NodeGetCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . NodeServiceCapability {
{
Type : & csipbv1 . NodeServiceCapability_Rpc {
Rpc : & csipbv1 . NodeServiceCapability_RPC {
Type : csipbv1 . NodeServiceCapability_RPC_EXPAND_VOLUME ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & NodeCapabilitySet { } ,
} ,
{
Name : "detects stage volumes capability" ,
Response : & csipbv1 . NodeGetCapabilitiesResponse {
Capabilities : [ ] * csipbv1 . NodeServiceCapability {
{
Type : & csipbv1 . NodeServiceCapability_Rpc {
Rpc : & csipbv1 . NodeServiceCapability_RPC {
Type : csipbv1 . NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME ,
} ,
} ,
} ,
} ,
} ,
ExpectedResponse : & NodeCapabilitySet {
HasStageUnstageVolume : true ,
} ,
} ,
}
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
_ , _ , nc , client := newTestClient ( )
defer client . Close ( )
nc . NextErr = tc . ResponseErr
nc . NextCapabilitiesResponse = tc . Response
resp , err := client . NodeGetCapabilities ( context . TODO ( ) )
if tc . ExpectedErr != nil {
2020-05-18 12:23:17 +00:00
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2019-12-18 12:18:07 +00:00
}
require . Equal ( t , tc . ExpectedResponse , resp )
} )
}
}
2019-12-16 12:31:09 +00:00
func TestClient_RPC_ControllerPublishVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
2020-02-10 16:45:06 +00:00
Request * ControllerPublishVolumeRequest
2019-12-16 12:31:09 +00:00
ResponseErr error
Response * csipbv1 . ControllerPublishVolumeResponse
ExpectedResponse * ControllerPublishVolumeResponse
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
2020-05-18 12:23:17 +00:00
Request : & ControllerPublishVolumeRequest { ExternalID : "vol" , NodeID : "node" } ,
ResponseErr : status . Errorf ( codes . Internal , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "controller plugin returned an internal error, check the plugin allocation logs for more information: rpc error: code = Internal desc = some grpc error" ) ,
2019-12-16 12:31:09 +00:00
} ,
2020-02-10 16:45:06 +00:00
{
2020-05-18 12:23:17 +00:00
Name : "handles missing NodeID" ,
Request : & ControllerPublishVolumeRequest { ExternalID : "vol" } ,
2020-02-10 16:45:06 +00:00
Response : & csipbv1 . ControllerPublishVolumeResponse { } ,
ExpectedErr : fmt . Errorf ( "missing NodeID" ) ,
} ,
2019-12-16 12:31:09 +00:00
{
2020-05-18 12:23:17 +00:00
Name : "handles PublishContext == nil" ,
Request : & ControllerPublishVolumeRequest {
ExternalID : "vol" , NodeID : "node" } ,
2019-12-16 12:31:09 +00:00
Response : & csipbv1 . ControllerPublishVolumeResponse { } ,
ExpectedResponse : & ControllerPublishVolumeResponse { } ,
} ,
{
2020-05-18 12:23:17 +00:00
Name : "handles PublishContext != nil" ,
2020-05-14 15:56:07 +00:00
Request : & ControllerPublishVolumeRequest { ExternalID : "vol" , NodeID : "node" } ,
2019-12-16 12:31:09 +00:00
Response : & csipbv1 . ControllerPublishVolumeResponse {
PublishContext : map [ string ] string {
"com.hashicorp/nomad-node-id" : "foobar" ,
"com.plugin/device" : "/dev/sdc1" ,
} ,
} ,
ExpectedResponse : & ControllerPublishVolumeResponse {
PublishContext : map [ string ] string {
"com.hashicorp/nomad-node-id" : "foobar" ,
"com.plugin/device" : "/dev/sdc1" ,
} ,
} ,
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2019-12-18 12:18:07 +00:00
_ , cc , _ , client := newTestClient ( )
2019-12-16 12:31:09 +00:00
defer client . Close ( )
2020-05-18 12:23:17 +00:00
cc . NextErr = tc . ResponseErr
cc . NextPublishVolumeResponse = tc . Response
2019-12-16 12:31:09 +00:00
2020-05-18 12:23:17 +00:00
resp , err := client . ControllerPublishVolume ( context . TODO ( ) , tc . Request )
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2020-02-10 16:45:06 +00:00
}
2020-05-18 12:23:17 +00:00
require . Equal ( t , tc . ExpectedResponse , resp )
2020-02-10 16:45:06 +00:00
} )
}
}
func TestClient_RPC_ControllerUnpublishVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
Request * ControllerUnpublishVolumeRequest
ResponseErr error
Response * csipbv1 . ControllerUnpublishVolumeResponse
ExpectedResponse * ControllerUnpublishVolumeResponse
ExpectedErr error
} {
{
2020-05-18 12:23:17 +00:00
Name : "handles underlying grpc errors" ,
Request : & ControllerUnpublishVolumeRequest { ExternalID : "vol" , NodeID : "node" } ,
ResponseErr : status . Errorf ( codes . Internal , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "controller plugin returned an internal error, check the plugin allocation logs for more information: rpc error: code = Internal desc = some grpc error" ) ,
2020-02-10 16:45:06 +00:00
} ,
{
2020-05-18 12:23:17 +00:00
Name : "handles missing NodeID" ,
Request : & ControllerUnpublishVolumeRequest { ExternalID : "vol" } ,
2020-02-10 16:45:06 +00:00
ExpectedErr : fmt . Errorf ( "missing NodeID" ) ,
ExpectedResponse : nil ,
} ,
{
2020-05-18 12:23:17 +00:00
Name : "handles successful response" ,
2020-05-14 15:56:07 +00:00
Request : & ControllerUnpublishVolumeRequest { ExternalID : "vol" , NodeID : "node" } ,
2020-02-10 16:45:06 +00:00
ExpectedResponse : & ControllerUnpublishVolumeResponse { } ,
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2020-02-10 16:45:06 +00:00
_ , cc , _ , client := newTestClient ( )
defer client . Close ( )
2020-05-18 12:23:17 +00:00
cc . NextErr = tc . ResponseErr
cc . NextUnpublishVolumeResponse = tc . Response
2020-02-10 16:45:06 +00:00
2020-05-18 12:23:17 +00:00
resp , err := client . ControllerUnpublishVolume ( context . TODO ( ) , tc . Request )
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2019-12-16 12:31:09 +00:00
}
2020-05-18 12:23:17 +00:00
require . Equal ( t , tc . ExpectedResponse , resp )
2019-12-16 12:31:09 +00:00
} )
}
2019-10-22 13:20:26 +00:00
}
2020-01-23 11:24:37 +00:00
2020-04-30 21:12:32 +00:00
func TestClient_RPC_ControllerValidateVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
2020-10-08 16:53:24 +00:00
AccessType VolumeAccessType
AccessMode VolumeAccessMode
2020-04-30 21:12:32 +00:00
ResponseErr error
Response * csipbv1 . ValidateVolumeCapabilitiesResponse
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
2020-10-08 16:53:24 +00:00
AccessType : VolumeAccessTypeMount ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
2020-05-18 12:23:17 +00:00
ResponseErr : status . Errorf ( codes . Internal , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "controller plugin returned an internal error, check the plugin allocation logs for more information: rpc error: code = Internal desc = some grpc error" ) ,
2020-04-30 21:12:32 +00:00
} ,
{
2020-10-08 16:53:24 +00:00
Name : "handles success empty capabilities" ,
AccessType : VolumeAccessTypeMount ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
2020-04-30 21:12:32 +00:00
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse { } ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
{
2020-10-08 16:53:24 +00:00
Name : "handles success exact match MountVolume" ,
AccessType : VolumeAccessTypeMount ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
2020-04-30 21:12:32 +00:00
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeContext : map [ string ] string { } ,
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
2020-05-05 19:13:07 +00:00
AccessType : & csipbv1 . VolumeCapability_Mount {
Mount : & csipbv1 . VolumeCapability_MountVolume {
FsType : "ext4" ,
MountFlags : [ ] string { "errors=remount-ro" , "noatime" } ,
} ,
2020-04-30 21:12:32 +00:00
} ,
AccessMode : & csipbv1 . VolumeCapability_AccessMode {
Mode : csipbv1 . VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
2020-10-08 16:53:24 +00:00
{
Name : "handles success exact match BlockVolume" ,
AccessType : VolumeAccessTypeBlock ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
AccessType : & csipbv1 . VolumeCapability_Block {
Block : & csipbv1 . VolumeCapability_BlockVolume { } ,
} ,
AccessMode : & csipbv1 . VolumeCapability_AccessMode {
Mode : csipbv1 . VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
2020-04-30 21:12:32 +00:00
{
2020-10-08 16:53:24 +00:00
Name : "handles failure AccessMode mismatch" ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
2020-04-30 21:12:32 +00:00
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeContext : map [ string ] string { } ,
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
AccessType : & csipbv1 . VolumeCapability_Block {
Block : & csipbv1 . VolumeCapability_BlockVolume { } ,
} ,
AccessMode : & csipbv1 . VolumeCapability_AccessMode {
Mode : csipbv1 . VolumeCapability_AccessMode_SINGLE_NODE_WRITER ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
2020-05-18 12:23:17 +00:00
// this is a multierror
2020-10-08 16:53:24 +00:00
ExpectedErr : fmt . Errorf ( "volume capability validation failed: 1 error occurred:\n\t* requested access mode MULTI_NODE_MULTI_WRITER, got SINGLE_NODE_WRITER\n\n" ) ,
2020-04-30 21:12:32 +00:00
} ,
2020-05-18 12:23:17 +00:00
2020-05-05 19:13:07 +00:00
{
2020-10-08 16:53:24 +00:00
Name : "handles failure MountFlags mismatch" ,
AccessType : VolumeAccessTypeMount ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
2020-05-05 19:13:07 +00:00
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeContext : map [ string ] string { } ,
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
AccessType : & csipbv1 . VolumeCapability_Mount {
Mount : & csipbv1 . VolumeCapability_MountVolume {
FsType : "ext4" ,
MountFlags : [ ] string { } ,
} ,
} ,
AccessMode : & csipbv1 . VolumeCapability_AccessMode {
Mode : csipbv1 . VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
2020-05-18 12:23:17 +00:00
// this is a multierror
ExpectedErr : fmt . Errorf ( "volume capability validation failed: 1 error occurred:\n\t* requested mount flags did not match available capabilities\n\n" ) ,
2020-05-05 19:13:07 +00:00
} ,
2020-10-08 16:53:24 +00:00
{
Name : "handles failure MountFlags with Block" ,
AccessType : VolumeAccessTypeBlock ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeContext : map [ string ] string { } ,
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
AccessType : & csipbv1 . VolumeCapability_Mount {
Mount : & csipbv1 . VolumeCapability_MountVolume {
FsType : "ext4" ,
MountFlags : [ ] string { } ,
} ,
} ,
AccessMode : & csipbv1 . VolumeCapability_AccessMode {
Mode : csipbv1 . VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
// this is a multierror
ExpectedErr : fmt . Errorf ( "volume capability validation failed: 1 error occurred:\n\t* 'file-system' access type was not requested but was validated by the controller\n\n" ) ,
} ,
{
Name : "handles success incomplete no AccessType" ,
AccessType : VolumeAccessTypeMount ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
AccessMode : & csipbv1 . VolumeCapability_AccessMode {
Mode : csipbv1 . VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
{
Name : "handles success incomplete no AccessMode" ,
AccessType : VolumeAccessTypeBlock ,
AccessMode : VolumeAccessModeMultiNodeMultiWriter ,
Response : & csipbv1 . ValidateVolumeCapabilitiesResponse {
Confirmed : & csipbv1 . ValidateVolumeCapabilitiesResponse_Confirmed {
VolumeCapabilities : [ ] * csipbv1 . VolumeCapability {
{
AccessType : & csipbv1 . VolumeCapability_Block {
Block : & csipbv1 . VolumeCapability_BlockVolume { } ,
} ,
} ,
} ,
} ,
} ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
2020-04-30 21:12:32 +00:00
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2020-04-30 21:12:32 +00:00
_ , cc , _ , client := newTestClient ( )
defer client . Close ( )
requestedCaps := & VolumeCapability {
2020-10-08 16:53:24 +00:00
AccessType : tc . AccessType ,
AccessMode : tc . AccessMode ,
2020-04-30 21:12:32 +00:00
MountVolume : & structs . CSIMountOptions { // should be ignored
FSType : "ext4" ,
MountFlags : [ ] string { "noatime" , "errors=remount-ro" } ,
} ,
}
2020-05-15 12:16:01 +00:00
req := & ControllerValidateVolumeRequest {
ExternalID : "volumeID" ,
Secrets : structs . CSISecrets { } ,
Capabilities : requestedCaps ,
Parameters : map [ string ] string { } ,
Context : map [ string ] string { } ,
}
2020-05-18 12:23:17 +00:00
cc . NextValidateVolumeCapabilitiesResponse = tc . Response
cc . NextErr = tc . ResponseErr
2020-04-30 21:12:32 +00:00
2020-05-15 12:16:01 +00:00
err := client . ControllerValidateCapabilities ( context . TODO ( ) , req )
2020-05-18 12:23:17 +00:00
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2020-04-30 21:12:32 +00:00
} else {
2020-05-18 12:23:17 +00:00
require . NoError ( t , err , tc . Name )
2020-04-30 21:12:32 +00:00
}
} )
}
}
2020-01-23 11:24:37 +00:00
func TestClient_RPC_NodeStageVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
ResponseErr error
Response * csipbv1 . NodeStageVolumeResponse
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
2020-05-18 12:23:17 +00:00
ResponseErr : status . Errorf ( codes . AlreadyExists , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "volume \"foo\" is already staged to \"/path\" but with incompatible capabilities for this request: rpc error: code = AlreadyExists desc = some grpc error" ) ,
2020-01-23 11:24:37 +00:00
} ,
{
Name : "handles success" ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2020-01-23 11:24:37 +00:00
_ , _ , nc , client := newTestClient ( )
defer client . Close ( )
2020-05-18 12:23:17 +00:00
nc . NextErr = tc . ResponseErr
nc . NextStageVolumeResponse = tc . Response
2020-01-23 11:24:37 +00:00
2020-06-22 17:54:32 +00:00
err := client . NodeStageVolume ( context . TODO ( ) , & NodeStageVolumeRequest {
ExternalID : "foo" ,
StagingTargetPath : "/path" ,
VolumeCapability : & VolumeCapability { } ,
} )
2020-05-18 12:23:17 +00:00
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2020-01-23 11:24:37 +00:00
} else {
require . Nil ( t , err )
}
} )
}
}
2020-01-24 13:34:04 +00:00
func TestClient_RPC_NodeUnstageVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
ResponseErr error
Response * csipbv1 . NodeUnstageVolumeResponse
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
2020-05-18 12:23:17 +00:00
ResponseErr : status . Errorf ( codes . Internal , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "node plugin returned an internal error, check the plugin allocation logs for more information: rpc error: code = Internal desc = some grpc error" ) ,
2020-01-24 13:34:04 +00:00
} ,
{
Name : "handles success" ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2020-01-24 13:34:04 +00:00
_ , _ , nc , client := newTestClient ( )
defer client . Close ( )
2020-05-18 12:23:17 +00:00
nc . NextErr = tc . ResponseErr
nc . NextUnstageVolumeResponse = tc . Response
2020-01-24 13:34:04 +00:00
err := client . NodeUnstageVolume ( context . TODO ( ) , "foo" , "/foo" )
2020-05-18 12:23:17 +00:00
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2020-01-24 13:34:04 +00:00
} else {
require . Nil ( t , err )
}
} )
}
}
2020-01-24 16:20:23 +00:00
func TestClient_RPC_NodePublishVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
Request * NodePublishVolumeRequest
ResponseErr error
Response * csipbv1 . NodePublishVolumeResponse
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
Request : & NodePublishVolumeRequest {
2020-05-14 15:56:07 +00:00
ExternalID : "foo" ,
2020-01-24 16:20:23 +00:00
TargetPath : "/dev/null" ,
VolumeCapability : & VolumeCapability { } ,
} ,
2020-05-18 12:23:17 +00:00
ResponseErr : status . Errorf ( codes . Internal , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "node plugin returned an internal error, check the plugin allocation logs for more information: rpc error: code = Internal desc = some grpc error" ) ,
2020-01-24 16:20:23 +00:00
} ,
{
Name : "handles success" ,
Request : & NodePublishVolumeRequest {
2020-05-14 15:56:07 +00:00
ExternalID : "foo" ,
2020-01-24 16:20:23 +00:00
TargetPath : "/dev/null" ,
VolumeCapability : & VolumeCapability { } ,
} ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
{
Name : "Performs validation of the publish volume request" ,
Request : & NodePublishVolumeRequest {
2020-05-14 15:56:07 +00:00
ExternalID : "" ,
2020-01-24 16:20:23 +00:00
} ,
ResponseErr : nil ,
2020-05-18 12:23:17 +00:00
ExpectedErr : errors . New ( "validation error: missing volume ID" ) ,
2020-01-24 16:20:23 +00:00
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2020-01-24 16:20:23 +00:00
_ , _ , nc , client := newTestClient ( )
defer client . Close ( )
2020-05-18 12:23:17 +00:00
nc . NextErr = tc . ResponseErr
nc . NextPublishVolumeResponse = tc . Response
2020-01-24 16:20:23 +00:00
2020-05-18 12:23:17 +00:00
err := client . NodePublishVolume ( context . TODO ( ) , tc . Request )
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2020-01-24 16:20:23 +00:00
} else {
require . Nil ( t , err )
}
} )
}
}
2020-01-27 10:17:10 +00:00
func TestClient_RPC_NodeUnpublishVolume ( t * testing . T ) {
cases := [ ] struct {
Name string
2020-05-14 15:56:07 +00:00
ExternalID string
2020-01-27 10:17:10 +00:00
TargetPath string
ResponseErr error
Response * csipbv1 . NodeUnpublishVolumeResponse
ExpectedErr error
} {
{
Name : "handles underlying grpc errors" ,
2020-05-14 15:56:07 +00:00
ExternalID : "foo" ,
2020-01-27 10:17:10 +00:00
TargetPath : "/dev/null" ,
2020-05-18 12:23:17 +00:00
ResponseErr : status . Errorf ( codes . Internal , "some grpc error" ) ,
ExpectedErr : fmt . Errorf ( "node plugin returned an internal error, check the plugin allocation logs for more information: rpc error: code = Internal desc = some grpc error" ) ,
2020-01-27 10:17:10 +00:00
} ,
{
Name : "handles success" ,
2020-05-14 15:56:07 +00:00
ExternalID : "foo" ,
2020-01-27 10:17:10 +00:00
TargetPath : "/dev/null" ,
ResponseErr : nil ,
ExpectedErr : nil ,
} ,
{
2020-05-14 15:56:07 +00:00
Name : "Performs validation of the request args - ExternalID" ,
2020-01-27 10:17:10 +00:00
ResponseErr : nil ,
2020-05-18 12:23:17 +00:00
ExpectedErr : errors . New ( "missing volumeID" ) ,
2020-01-27 10:17:10 +00:00
} ,
{
Name : "Performs validation of the request args - TargetPath" ,
2020-05-14 15:56:07 +00:00
ExternalID : "foo" ,
2020-01-27 10:17:10 +00:00
ResponseErr : nil ,
2020-05-18 12:23:17 +00:00
ExpectedErr : errors . New ( "missing targetPath" ) ,
2020-01-27 10:17:10 +00:00
} ,
}
2020-05-18 12:23:17 +00:00
for _ , tc := range cases {
t . Run ( tc . Name , func ( t * testing . T ) {
2020-01-27 10:17:10 +00:00
_ , _ , nc , client := newTestClient ( )
defer client . Close ( )
2020-05-18 12:23:17 +00:00
nc . NextErr = tc . ResponseErr
nc . NextUnpublishVolumeResponse = tc . Response
2020-01-27 10:17:10 +00:00
2020-05-18 12:23:17 +00:00
err := client . NodeUnpublishVolume ( context . TODO ( ) , tc . ExternalID , tc . TargetPath )
if tc . ExpectedErr != nil {
require . EqualError ( t , err , tc . ExpectedErr . Error ( ) )
2020-01-27 10:17:10 +00:00
} else {
require . Nil ( t , err )
}
} )
}
}