2019-10-22 13:20:26 +00:00
|
|
|
package csi
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
csipbv1 "github.com/container-storage-interface/spec/lib/go/csi"
|
|
|
|
"github.com/golang/protobuf/ptypes/wrappers"
|
|
|
|
fake "github.com/hashicorp/nomad/plugins/csi/testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2019-12-16 12:31:09 +00:00
|
|
|
func newTestClient() (*fake.IdentityClient, *fake.ControllerClient, CSIPlugin) {
|
2019-10-22 13:20:26 +00:00
|
|
|
ic := &fake.IdentityClient{}
|
2019-12-16 12:31:09 +00:00
|
|
|
cc := &fake.ControllerClient{}
|
2019-10-22 13:20:26 +00:00
|
|
|
client := &client{
|
2019-12-16 12:31:09 +00:00
|
|
|
identityClient: ic,
|
|
|
|
controllerClient: cc,
|
2019-10-22 13:20:26 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 12:31:09 +00:00
|
|
|
return ic, cc, 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,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.Name, func(t *testing.T) {
|
2019-12-16 12:31:09 +00:00
|
|
|
ic, _, client := newTestClient()
|
2019-10-22 13:20:26 +00:00
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
ic.NextErr = c.ResponseErr
|
|
|
|
ic.NextPluginProbe = c.ProbeResponse
|
|
|
|
|
|
|
|
resp, err := client.PluginProbe(context.TODO())
|
|
|
|
if c.ExpectedErr != nil {
|
|
|
|
require.Error(t, c.ExpectedErr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, c.ExpectedResponse, resp)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestClient_RPC_PluginInfo(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Name string
|
|
|
|
ResponseErr error
|
|
|
|
InfoResponse *csipbv1.GetPluginInfoResponse
|
|
|
|
ExpectedResponse string
|
|
|
|
ExpectedErr error
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
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{
|
|
|
|
Name: "",
|
|
|
|
},
|
|
|
|
ExpectedErr: fmt.Errorf("PluginGetInfo: plugin returned empty name field"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "returns the name when successfully retrieved and not empty",
|
|
|
|
InfoResponse: &csipbv1.GetPluginInfoResponse{
|
|
|
|
Name: "com.hashicorp.storage",
|
|
|
|
},
|
|
|
|
ExpectedResponse: "com.hashicorp.storage",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.Name, func(t *testing.T) {
|
2019-12-16 12:31:09 +00:00
|
|
|
ic, _, client := newTestClient()
|
2019-10-22 13:20:26 +00:00
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
ic.NextErr = c.ResponseErr
|
|
|
|
ic.NextPluginInfo = c.InfoResponse
|
|
|
|
|
|
|
|
resp, err := client.PluginGetInfo(context.TODO())
|
|
|
|
if c.ExpectedErr != nil {
|
|
|
|
require.Error(t, c.ExpectedErr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, c.ExpectedResponse, resp)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.Name, func(t *testing.T) {
|
2019-12-16 12:31:09 +00:00
|
|
|
ic, _, client := newTestClient()
|
2019-10-22 13:20:26 +00:00
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
ic.NextErr = c.ResponseErr
|
|
|
|
ic.NextPluginCapabilities = c.Response
|
|
|
|
|
|
|
|
resp, err := client.PluginGetCapabilities(context.TODO())
|
|
|
|
if c.ExpectedErr != nil {
|
|
|
|
require.Error(t, c.ExpectedErr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, c.ExpectedResponse, resp)
|
|
|
|
})
|
|
|
|
}
|
2019-12-16 12:31:09 +00:00
|
|
|
}
|
2019-10-22 13:20:26 +00:00
|
|
|
|
2019-12-16 12:31:09 +00:00
|
|
|
func TestClient_RPC_ControllerPublishVolume(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Name string
|
|
|
|
ResponseErr error
|
|
|
|
Response *csipbv1.ControllerPublishVolumeResponse
|
|
|
|
ExpectedResponse *ControllerPublishVolumeResponse
|
|
|
|
ExpectedErr error
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Name: "handles underlying grpc errors",
|
|
|
|
ResponseErr: fmt.Errorf("some grpc error"),
|
|
|
|
ExpectedErr: fmt.Errorf("some grpc error"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Handles PublishContext == nil",
|
|
|
|
Response: &csipbv1.ControllerPublishVolumeResponse{},
|
|
|
|
ExpectedResponse: &ControllerPublishVolumeResponse{},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Handles PublishContext != nil",
|
|
|
|
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",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
t.Run(c.Name, func(t *testing.T) {
|
|
|
|
_, cc, client := newTestClient()
|
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
cc.NextErr = c.ResponseErr
|
|
|
|
cc.NextPublishVolumeResponse = c.Response
|
|
|
|
|
|
|
|
resp, err := client.ControllerPublishVolume(context.TODO(), &ControllerPublishVolumeRequest{})
|
|
|
|
if c.ExpectedErr != nil {
|
|
|
|
require.Error(t, c.ExpectedErr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, c.ExpectedResponse, resp)
|
|
|
|
})
|
|
|
|
}
|
2019-10-22 13:20:26 +00:00
|
|
|
}
|