diff --git a/.changelog/10848.txt b/.changelog/10848.txt new file mode 100644 index 000000000000..d66340ff5733 --- /dev/null +++ b/.changelog/10848.txt @@ -0,0 +1,3 @@ +```release-note:improvement +csi: add flag for providing secrets as a set of key/value pairs to list snapshots +``` diff --git a/api/csi.go b/api/csi.go index 836d37d04e42..120c239fde8f 100644 --- a/api/csi.go +++ b/api/csi.go @@ -137,7 +137,7 @@ func (v *CSIVolumes) DeleteSnapshot(snap *CSISnapshot, w *WriteOptions) error { } // ListSnapshots lists external storage volume snapshots. -func (v *CSIVolumes) ListSnapshots(pluginID string, q *QueryOptions) (*CSISnapshotListResponse, *QueryMeta, error) { +func (v *CSIVolumes) ListSnapshots(pluginID string, secrets string, q *QueryOptions) (*CSISnapshotListResponse, *QueryMeta, error) { var resp *CSISnapshotListResponse qp := url.Values{} @@ -150,6 +150,9 @@ func (v *CSIVolumes) ListSnapshots(pluginID string, q *QueryOptions) (*CSISnapsh if q.PerPage != 0 { qp.Set("per_page", fmt.Sprint(q.PerPage)) } + if secrets != "" { + qp.Set("secrets", secrets) + } qm, err := v.client.query("/v1/volumes/snapshot?"+qp.Encode(), &resp, q) if err != nil { @@ -406,6 +409,7 @@ type CSISnapshotCreateResponse struct { // fields type CSISnapshotListRequest struct { PluginID string + Secrets CSISecrets QueryOptions } diff --git a/client/csi_endpoint_test.go b/client/csi_endpoint_test.go index f5bc07b44dc2..5e8382eb96e0 100644 --- a/client/csi_endpoint_test.go +++ b/client/csi_endpoint_test.go @@ -840,6 +840,10 @@ func TestCSIController_ListSnapshots(t *testing.T) { CSIControllerQuery: structs.CSIControllerQuery{ PluginID: fakePlugin.Name, }, + Secrets: map[string]string{ + "secret-key-1": "secret-val-1", + "secret-key-2": "secret-val-2", + }, StartingToken: "1", MaxEntries: 100, }, diff --git a/client/structs/csi.go b/client/structs/csi.go index d4846f7e95cc..ca7f88d41deb 100644 --- a/client/structs/csi.go +++ b/client/structs/csi.go @@ -362,6 +362,7 @@ type ClientCSIControllerListSnapshotsRequest struct { // not Nomad's own fields, for clarity when mapping between the two RPCs MaxEntries int32 StartingToken string + Secrets structs.CSISecrets CSIControllerQuery } @@ -370,6 +371,7 @@ func (req *ClientCSIControllerListSnapshotsRequest) ToCSIRequest() *csi.Controll return &csi.ControllerListSnapshotsRequest{ MaxEntries: req.MaxEntries, StartingToken: req.StartingToken, + Secrets: req.Secrets, } } diff --git a/command/agent/csi_endpoint.go b/command/agent/csi_endpoint.go index 9184bfe0794e..34e20e3bbe08 100644 --- a/command/agent/csi_endpoint.go +++ b/command/agent/csi_endpoint.go @@ -333,6 +333,19 @@ func (s *HTTPServer) csiSnapshotList(resp http.ResponseWriter, req *http.Request query := req.URL.Query() args.PluginID = query.Get("plugin_id") + querySecrets := query["secrets"] + + // Parse comma separated secrets only when provided + if len(querySecrets) >= 1 { + secrets := strings.Split(querySecrets[0], ",") + args.Secrets = make(structs.CSISecrets) + for _, raw := range secrets { + secret := strings.Split(raw, "=") + if len(secret) == 2 { + args.Secrets[secret[0]] = secret[1] + } + } + } var out structs.CSISnapshotListResponse if err := s.agent.RPC("CSIVolume.ListSnapshots", &args, &out); err != nil { diff --git a/command/volume_snapshot_list.go b/command/volume_snapshot_list.go index 65ea484ca279..2542b3d8c2a7 100644 --- a/command/volume_snapshot_list.go +++ b/command/volume_snapshot_list.go @@ -35,6 +35,8 @@ List Options: -plugin: Display only snapshots managed by a particular plugin. By default this command will query all plugins for their snapshots. + + -secrets: A set of key/value secrets to be used when listing snapshots. ` return strings.TrimSpace(helpText) } @@ -68,11 +70,13 @@ func (c *VolumeSnapshotListCommand) Name() string { return "volume snapshot list func (c *VolumeSnapshotListCommand) Run(args []string) int { var pluginID string var verbose bool + var secrets string flags := c.Meta.FlagSet(c.Name(), FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } flags.StringVar(&pluginID, "plugin", "", "") flags.BoolVar(&verbose, "verbose", false, "") + flags.StringVar(&secrets, "secrets", "", "") if err := flags.Parse(args); err != nil { c.Ui.Error(fmt.Sprintf("Error parsing arguments %s", err)) @@ -121,7 +125,7 @@ func (c *VolumeSnapshotListCommand) Run(args []string) int { q := &api.QueryOptions{PerPage: 30} // TODO: tune page size for { - resp, _, err := client.CSIVolumes().ListSnapshots(pluginID, q) + resp, _, err := client.CSIVolumes().ListSnapshots(pluginID, secrets, q) if err != nil && !errors.Is(err, io.EOF) { c.Ui.Error(fmt.Sprintf( "Error querying CSI external snapshots for plugin %q: %s", pluginID, err)) diff --git a/nomad/csi_endpoint.go b/nomad/csi_endpoint.go index 30a223cc33a9..eb6954eed2f1 100644 --- a/nomad/csi_endpoint.go +++ b/nomad/csi_endpoint.go @@ -1247,6 +1247,7 @@ func (v *CSIVolume) ListSnapshots(args *structs.CSISnapshotListRequest, reply *s cReq := &cstructs.ClientCSIControllerListSnapshotsRequest{ MaxEntries: args.PerPage, StartingToken: args.NextToken, + Secrets: args.Secrets, } cReq.PluginID = plugin.ID cResp := &cstructs.ClientCSIControllerListSnapshotsResponse{} diff --git a/nomad/csi_endpoint_test.go b/nomad/csi_endpoint_test.go index cbcd74695ce2..35cafb2696bd 100644 --- a/nomad/csi_endpoint_test.go +++ b/nomad/csi_endpoint_test.go @@ -1280,6 +1280,9 @@ func TestCSIVolumeEndpoint_ListSnapshots(t *testing.T) { // List snapshots req := &structs.CSISnapshotListRequest{ + Secrets: structs.CSISecrets{ + "secret-key-1": "secret-val-1", + }, QueryOptions: structs.QueryOptions{ Region: "global", Namespace: structs.DefaultNamespace, diff --git a/nomad/structs/csi.go b/nomad/structs/csi.go index 1c7845c0a61e..e0f35aeaca36 100644 --- a/nomad/structs/csi.go +++ b/nomad/structs/csi.go @@ -897,6 +897,7 @@ type CSISnapshotDeleteResponse struct { // fields type CSISnapshotListRequest struct { PluginID string + Secrets CSISecrets QueryOptions } diff --git a/plugins/csi/plugin.go b/plugins/csi/plugin.go index 7019127c54e7..6c85db7a8e09 100644 --- a/plugins/csi/plugin.go +++ b/plugins/csi/plugin.go @@ -756,12 +756,14 @@ func (r *ControllerDeleteSnapshotRequest) Validate() error { type ControllerListSnapshotsRequest struct { MaxEntries int32 StartingToken string + Secrets structs.CSISecrets } func (r *ControllerListSnapshotsRequest) ToCSIRepresentation() *csipbv1.ListSnapshotsRequest { return &csipbv1.ListSnapshotsRequest{ MaxEntries: r.MaxEntries, StartingToken: r.StartingToken, + Secrets: r.Secrets, } } diff --git a/website/content/api-docs/volumes.mdx b/website/content/api-docs/volumes.mdx index 3c73f8840d0f..b0175ccf9a85 100644 --- a/website/content/api-docs/volumes.mdx +++ b/website/content/api-docs/volumes.mdx @@ -714,12 +714,15 @@ The table below shows this endpoint's support for return for this request. The response will include a `NextToken` field that can be passed to the next request to fetch additional pages. +- `secrets` `(string: "")` - Specifies a list of key/value secrets for listing snapshots. + These key/value pairs are comma-separated and are passed directly to the CSI plugin. ### Sample Request ```shell-session $ curl \ - https://localhost:4646/v1/volumes/snapshot?&plugin_id=plugin-id1&per_page=2 + https://localhost:4646/v1/volumes/snapshot?plugin_id=plugin-id1&per_page=2& \ + secrets=secret-key-1=secret-value-1,secret-key-2=secret-value-2 ``` ### Sample Response diff --git a/website/content/docs/commands/volume/snapshot-list.mdx b/website/content/docs/commands/volume/snapshot-list.mdx index 177f2c510ffd..078271db716e 100644 --- a/website/content/docs/commands/volume/snapshot-list.mdx +++ b/website/content/docs/commands/volume/snapshot-list.mdx @@ -15,7 +15,7 @@ command. ## Usage ```plaintext -nomad volume snapshot list [-plugin plugin_id] +nomad volume snapshot list [-plugin plugin_id -secrets key=value] ``` The `volume snapshot list` command returns a list of snapshots along with their @@ -27,7 +27,7 @@ Nomad. @include 'general_options.mdx' -## Status Options +## List Options - `-plugin`: Display only snapshots managed by a particular [CSI plugin][csi_plugin]. By default the `snapshot list` command will query all @@ -35,6 +35,8 @@ Nomad. there is an exact match based on the provided plugin, then that specific plugin will be queried. Otherwise, a list of matching plugins will be displayed. +- `-secrets`: A list of comma separated secret key/value pairs to be passed + to the CSI driver. When ACLs are enabled, this command requires a token with the `csi-list-volumes` capability for the plugin's namespace. @@ -50,7 +52,14 @@ snap-12345 vol-abcdef 50GiB 2021-01-03T12:15:02Z true snap-67890 vol-fedcba 50GiB 2021-01-04T15:45:00Z true ``` +List volume snapshots with two secret key/value pairs: +```shell-session +$ nomad volume snapshot list -secrets key1=value1,key2=val2 +Snapshot ID External ID Size Creation Time Ready? +snap-12345 vol-abcdef 50GiB 2021-01-03T12:15:02Z true +``` + [csi]: https://github.com/container-storage-interface/spec [csi_plugin]: /docs/job-specification/csi_plugin [registered]: /docs/commands/volume/register -[csi_plugins_internals]: /docs/internals/plugins/csi#csi-plugins +[csi_plugins_internals]: /docs/internals/plugins/csi#csi-plugins \ No newline at end of file