diff --git a/.changelog/12194.txt b/.changelog/12194.txt new file mode 100644 index 000000000..330a5d8cc --- /dev/null +++ b/.changelog/12194.txt @@ -0,0 +1,3 @@ +```release-note:bug +csi: Fixed a bug where `plugin status` commands could choose the incorrect plugin if a plugin with a name that matched the same prefix existed. +``` diff --git a/command/plugin_status_csi.go b/command/plugin_status_csi.go index b3a6ac36b..cdf8e03c5 100644 --- a/command/plugin_status_csi.go +++ b/command/plugin_status_csi.go @@ -37,6 +37,29 @@ func (c *PluginStatusCommand) csiStatus(client *api.Client, id string) int { return 0 } + // filter by plugin if a plugin ID was passed + plugs, _, err := client.CSIPlugins().List(&api.QueryOptions{Prefix: id}) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error querying CSI plugins: %s", err)) + return 1 + } + if len(plugs) == 0 { + c.Ui.Error(fmt.Sprintf("No plugins(s) with prefix or ID %q found", id)) + return 1 + } + if len(plugs) > 1 { + if id != plugs[0].ID { + out, err := c.csiFormatPlugins(plugs) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error formatting: %s", err)) + return 1 + } + c.Ui.Error(fmt.Sprintf("Prefix matched multiple plugins\n\n%s", out)) + return 1 + } + } + id = plugs[0].ID + // Lookup matched a single plugin plug, _, err := client.CSIPlugins().Info(id, nil) if err != nil { diff --git a/nomad/csi_endpoint.go b/nomad/csi_endpoint.go index 66083452f..ed0f95131 100644 --- a/nomad/csi_endpoint.go +++ b/nomad/csi_endpoint.go @@ -1294,10 +1294,20 @@ func (v *CSIPlugin) List(args *structs.CSIPluginListRequest, reply *structs.CSIP queryOpts: &args.QueryOptions, queryMeta: &reply.QueryMeta, run: func(ws memdb.WatchSet, state *state.StateStore) error { - // Query all plugins - iter, err := state.CSIPlugins(ws) - if err != nil { - return err + + var iter memdb.ResultIterator + var err error + if args.Prefix != "" { + iter, err = state.CSIPluginsByIDPrefix(ws, args.Prefix) + if err != nil { + return err + } + } else { + // Query all plugins + iter, err = state.CSIPlugins(ws) + if err != nil { + return err + } } // Collect results diff --git a/nomad/csi_endpoint_test.go b/nomad/csi_endpoint_test.go index 4b8275064..4c846d549 100644 --- a/nomad/csi_endpoint_test.go +++ b/nomad/csi_endpoint_test.go @@ -1100,6 +1100,7 @@ func TestCSIVolumeEndpoint_ListExternal(t *testing.T) { // List external volumes; note that none of these exist in the state store req := &structs.CSIVolumeExternalListRequest{ + PluginID: "minnie", QueryOptions: structs.QueryOptions{ Region: "global", Namespace: structs.DefaultNamespace, @@ -1371,8 +1372,8 @@ func TestCSIVolumeEndpoint_ListSnapshots(t *testing.T) { require.NoError(t, state.UpsertNode(structs.MsgTypeTestSetup, index, node)) // List snapshots - req := &structs.CSISnapshotListRequest{ + PluginID: "minnie", Secrets: structs.CSISecrets{ "secret-key-1": "secret-val-1", }, diff --git a/nomad/state/state_store.go b/nomad/state/state_store.go index 4cc1902aa..8ebbc6bc2 100644 --- a/nomad/state/state_store.go +++ b/nomad/state/state_store.go @@ -2696,7 +2696,7 @@ func (s *StateStore) CSIPluginByID(ws memdb.WatchSet, id string) (*structs.CSIPl // CSIPluginByIDTxn returns a named CSIPlugin func (s *StateStore) CSIPluginByIDTxn(txn Txn, ws memdb.WatchSet, id string) (*structs.CSIPlugin, error) { - watchCh, obj, err := txn.FirstWatch("csi_plugins", "id_prefix", id) + watchCh, obj, err := txn.FirstWatch("csi_plugins", "id", id) if err != nil { return nil, fmt.Errorf("csi_plugin lookup failed: %s %v", id, err) }