23be116da0
The `nomad volume deregister` command currently returns an error if the volume has any claims, but in cases where the claims can't be dropped because of plugin errors, providing a `-force` flag gives the operator an escape hatch. If the volume has no allocations or if they are all terminal, this flag deletes the volume from the state store, immediately and implicitly dropping all claims without further CSI RPCs. Note that this will not also unmount/detach the volume, which we'll make the responsibility of a separate `nomad volume detach` command.
102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
package api
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestCSIVolumes_CRUD fails because of a combination of removing the job to plugin creation
|
|
// pathway and checking for plugin existence (but not yet health) at registration time.
|
|
// There are two possible solutions:
|
|
// 1. Expose the test server RPC server and force a Node.Update to fingerprint a plugin
|
|
// 2. Build and deploy a dummy CSI plugin via a job, and have it really fingerprint
|
|
func TestCSIVolumes_CRUD(t *testing.T) {
|
|
t.Parallel()
|
|
c, s, root := makeACLClient(t, nil, nil)
|
|
defer s.Stop()
|
|
v := c.CSIVolumes()
|
|
|
|
// Successful empty result
|
|
vols, qm, err := v.List(nil)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, 0, qm.LastIndex)
|
|
require.Equal(t, 0, len(vols))
|
|
|
|
// FIXME we're bailing out here until one of the fixes is available
|
|
return
|
|
|
|
// Authorized QueryOpts. Use the root token to just bypass ACL details
|
|
opts := &QueryOptions{
|
|
Region: "global",
|
|
Namespace: "default",
|
|
AuthToken: root.SecretID,
|
|
}
|
|
|
|
wpts := &WriteOptions{
|
|
Region: "global",
|
|
Namespace: "default",
|
|
AuthToken: root.SecretID,
|
|
}
|
|
|
|
// Create node plugins
|
|
nodes, _, err := c.Nodes().List(nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(nodes))
|
|
|
|
nodeStub := nodes[0]
|
|
node, _, err := c.Nodes().Info(nodeStub.ID, nil)
|
|
require.NoError(t, err)
|
|
node.CSINodePlugins = map[string]*CSIInfo{
|
|
"foo": {
|
|
PluginID: "foo",
|
|
Healthy: true,
|
|
RequiresControllerPlugin: false,
|
|
RequiresTopologies: false,
|
|
NodeInfo: &CSINodeInfo{
|
|
ID: nodeStub.ID,
|
|
MaxVolumes: 200,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Register a volume
|
|
// This id is here as a string to avoid importing helper, which causes the lint
|
|
// rule that checks that the api package is isolated to fail
|
|
id := "DEADBEEF-31B5-8F78-7986-DD404FDA0CD1"
|
|
_, err = v.Register(&CSIVolume{
|
|
ID: id,
|
|
Namespace: "default",
|
|
PluginID: "foo",
|
|
AccessMode: CSIVolumeAccessModeMultiNodeSingleWriter,
|
|
AttachmentMode: CSIVolumeAttachmentModeFilesystem,
|
|
Topologies: []*CSITopology{{Segments: map[string]string{"foo": "bar"}}},
|
|
}, wpts)
|
|
require.NoError(t, err)
|
|
|
|
// Successful result with volumes
|
|
vols, qm, err = v.List(opts)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, 0, qm.LastIndex)
|
|
require.Equal(t, 1, len(vols))
|
|
|
|
// Successful info query
|
|
vol, qm, err := v.Info(id, opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "bar", vol.Topologies[0].Segments["foo"])
|
|
|
|
// Deregister the volume
|
|
err = v.Deregister(id, true, wpts)
|
|
require.NoError(t, err)
|
|
|
|
// Successful empty result
|
|
vols, qm, err = v.List(nil)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, 0, qm.LastIndex)
|
|
require.Equal(t, 0, len(vols))
|
|
|
|
// Failed info query
|
|
vol, qm, err = v.Info(id, opts)
|
|
require.Error(t, err, "missing")
|
|
}
|