open-nomad/client/pluginmanager/csimanager/fingerprint_test.go
Danielle Lancashire 1a10433b97 csi: Add VolumeManager (#6920)
This changeset is some pre-requisite boilerplate that is required for
introducing CSI volume management for client nodes.

It extracts out fingerprinting logic from the csi instance manager.
This change is to facilitate reusing the csimanager to also manage the
node-local CSI functionality, as it is the easiest place for us to
guaruntee health checking and to provide additional visibility into the
running operations through the fingerprinter mechanism and goroutine.

It also introduces the VolumeMounter interface that will be used to
manage staging/publishing unstaging/unpublishing of volumes on the host.
2020-03-23 13:58:29 -04:00

278 lines
6.9 KiB
Go

package csimanager
import (
"context"
"errors"
"testing"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/csi"
"github.com/stretchr/testify/require"
)
func TestBuildBasicFingerprint_Node(t *testing.T) {
tt := []struct {
Name string
Capabilities *csi.PluginCapabilitySet
CapabilitiesErr error
CapabilitiesCallCount int64
NodeInfo *csi.NodeGetInfoResponse
NodeInfoErr error
NodeInfoCallCount int64
ExpectedCSIInfo *structs.CSIInfo
ExpectedErr error
}{
{
Name: "Minimal successful response",
Capabilities: &csi.PluginCapabilitySet{},
CapabilitiesCallCount: 1,
NodeInfo: &csi.NodeGetInfoResponse{
NodeID: "foobar",
MaxVolumes: 5,
AccessibleTopology: nil,
},
NodeInfoCallCount: 1,
ExpectedCSIInfo: &structs.CSIInfo{
PluginID: "test-plugin",
Healthy: false,
HealthDescription: "initial fingerprint not completed",
NodeInfo: &structs.CSINodeInfo{
ID: "foobar",
MaxVolumes: 5,
},
},
},
{
Name: "Successful response with capabilities and topologies",
Capabilities: csi.NewTestPluginCapabilitySet(true, false),
CapabilitiesCallCount: 1,
NodeInfo: &csi.NodeGetInfoResponse{
NodeID: "foobar",
MaxVolumes: 5,
AccessibleTopology: &csi.Topology{
Segments: map[string]string{
"com.hashicorp.nomad/node-id": "foobar",
},
},
},
NodeInfoCallCount: 1,
ExpectedCSIInfo: &structs.CSIInfo{
PluginID: "test-plugin",
Healthy: false,
HealthDescription: "initial fingerprint not completed",
RequiresTopologies: true,
NodeInfo: &structs.CSINodeInfo{
ID: "foobar",
MaxVolumes: 5,
AccessibleTopology: &structs.CSITopology{
Segments: map[string]string{
"com.hashicorp.nomad/node-id": "foobar",
},
},
},
},
},
{
Name: "PluginGetCapabilities Failed",
CapabilitiesErr: errors.New("request failed"),
CapabilitiesCallCount: 1,
NodeInfoCallCount: 0,
ExpectedCSIInfo: &structs.CSIInfo{
PluginID: "test-plugin",
Healthy: false,
HealthDescription: "initial fingerprint not completed",
NodeInfo: &structs.CSINodeInfo{},
},
ExpectedErr: errors.New("request failed"),
},
{
Name: "NodeGetInfo Failed",
Capabilities: &csi.PluginCapabilitySet{},
CapabilitiesCallCount: 1,
NodeInfoErr: errors.New("request failed"),
NodeInfoCallCount: 1,
ExpectedCSIInfo: &structs.CSIInfo{
PluginID: "test-plugin",
Healthy: false,
HealthDescription: "initial fingerprint not completed",
NodeInfo: &structs.CSINodeInfo{},
},
ExpectedErr: errors.New("request failed"),
},
}
for _, test := range tt {
t.Run(test.Name, func(t *testing.T) {
client, im := setupTestNodeInstanceManager(t)
client.NextPluginGetCapabilitiesResponse = test.Capabilities
client.NextPluginGetCapabilitiesErr = test.CapabilitiesErr
client.NextNodeGetInfoResponse = test.NodeInfo
client.NextNodeGetInfoErr = test.NodeInfoErr
info, err := im.fp.buildBasicFingerprint(context.TODO())
require.Equal(t, test.ExpectedCSIInfo, info)
require.Equal(t, test.ExpectedErr, err)
require.Equal(t, test.CapabilitiesCallCount, client.PluginGetCapabilitiesCallCount)
require.Equal(t, test.NodeInfoCallCount, client.NodeGetInfoCallCount)
})
}
}
func TestBuildControllerFingerprint(t *testing.T) {
tt := []struct {
Name string
Capabilities *csi.ControllerCapabilitySet
CapabilitiesErr error
CapabilitiesCallCount int64
ProbeResponse bool
ProbeErr error
ProbeCallCount int64
ExpectedControllerInfo *structs.CSIControllerInfo
ExpectedErr error
}{
{
Name: "Minimal successful response",
Capabilities: &csi.ControllerCapabilitySet{},
CapabilitiesCallCount: 1,
ProbeResponse: true,
ProbeCallCount: 1,
ExpectedControllerInfo: &structs.CSIControllerInfo{},
},
{
Name: "Successful response with capabilities",
Capabilities: &csi.ControllerCapabilitySet{
HasListVolumes: true,
},
CapabilitiesCallCount: 1,
ProbeResponse: true,
ProbeCallCount: 1,
ExpectedControllerInfo: &structs.CSIControllerInfo{
SupportsListVolumes: true,
},
},
{
Name: "ControllerGetCapabilities Failed",
CapabilitiesErr: errors.New("request failed"),
CapabilitiesCallCount: 1,
ProbeResponse: true,
ProbeCallCount: 1,
ExpectedControllerInfo: &structs.CSIControllerInfo{},
ExpectedErr: errors.New("request failed"),
},
}
for _, test := range tt {
t.Run(test.Name, func(t *testing.T) {
client, im := setupTestNodeInstanceManager(t)
client.NextControllerGetCapabilitiesResponse = test.Capabilities
client.NextControllerGetCapabilitiesErr = test.CapabilitiesErr
client.NextPluginProbeResponse = test.ProbeResponse
client.NextPluginProbeErr = test.ProbeErr
info, err := im.fp.buildControllerFingerprint(context.TODO(), &structs.CSIInfo{ControllerInfo: &structs.CSIControllerInfo{}})
require.Equal(t, test.ExpectedControllerInfo, info.ControllerInfo)
require.Equal(t, test.ExpectedErr, err)
require.Equal(t, test.CapabilitiesCallCount, client.ControllerGetCapabilitiesCallCount)
require.Equal(t, test.ProbeCallCount, client.PluginProbeCallCount)
})
}
}
func TestBuildNodeFingerprint(t *testing.T) {
tt := []struct {
Name string
Capabilities *csi.NodeCapabilitySet
CapabilitiesErr error
CapabilitiesCallCount int64
ExpectedCSINodeInfo *structs.CSINodeInfo
ExpectedErr error
}{
{
Name: "Minimal successful response",
Capabilities: &csi.NodeCapabilitySet{},
CapabilitiesCallCount: 1,
ExpectedCSINodeInfo: &structs.CSINodeInfo{
RequiresNodeStageVolume: false,
},
},
{
Name: "Successful response with capabilities and topologies",
Capabilities: &csi.NodeCapabilitySet{
HasStageUnstageVolume: true,
},
CapabilitiesCallCount: 1,
ExpectedCSINodeInfo: &structs.CSINodeInfo{
RequiresNodeStageVolume: true,
},
},
{
Name: "NodeGetCapabilities Failed",
CapabilitiesErr: errors.New("request failed"),
CapabilitiesCallCount: 1,
ExpectedCSINodeInfo: &structs.CSINodeInfo{},
ExpectedErr: errors.New("request failed"),
},
}
for _, test := range tt {
t.Run(test.Name, func(t *testing.T) {
client, im := setupTestNodeInstanceManager(t)
client.NextNodeGetCapabilitiesResponse = test.Capabilities
client.NextNodeGetCapabilitiesErr = test.CapabilitiesErr
info, err := im.fp.buildNodeFingerprint(context.TODO(), &structs.CSIInfo{NodeInfo: &structs.CSINodeInfo{}})
require.Equal(t, test.ExpectedCSINodeInfo, info.NodeInfo)
require.Equal(t, test.ExpectedErr, err)
require.Equal(t, test.CapabilitiesCallCount, client.NodeGetCapabilitiesCallCount)
})
}
}