Skip to content

Commit

Permalink
CSI ListSnapshots secrets implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Grant Griffiths <ggriffiths@purestorage.com>
  • Loading branch information
Grant Griffiths committed Jul 28, 2021
1 parent e5a4dde commit d67ecea
Show file tree
Hide file tree
Showing 12 changed files with 53 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .changelog/10848.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
csi: add flag for providing secrets as a set of key/value pairs to list snapshots
```
6 changes: 5 additions & 1 deletion api/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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 {
Expand Down Expand Up @@ -406,6 +409,7 @@ type CSISnapshotCreateResponse struct {
// fields
type CSISnapshotListRequest struct {
PluginID string
Secrets CSISecrets
QueryOptions
}

Expand Down
4 changes: 4 additions & 0 deletions client/csi_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
2 changes: 2 additions & 0 deletions client/structs/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -370,6 +371,7 @@ func (req *ClientCSIControllerListSnapshotsRequest) ToCSIRequest() *csi.Controll
return &csi.ControllerListSnapshotsRequest{
MaxEntries: req.MaxEntries,
StartingToken: req.StartingToken,
Secrets: req.Secrets,
}
}

Expand Down
13 changes: 13 additions & 0 deletions command/agent/csi_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion command/volume_snapshot_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down
1 change: 1 addition & 0 deletions nomad/csi_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down
3 changes: 3 additions & 0 deletions nomad/csi_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions nomad/structs/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@ type CSISnapshotDeleteResponse struct {
// fields
type CSISnapshotListRequest struct {
PluginID string
Secrets CSISecrets
QueryOptions
}

Expand Down
2 changes: 2 additions & 0 deletions plugins/csi/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down
3 changes: 2 additions & 1 deletion website/content/api-docs/volumes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,8 @@ The table below shows this endpoint's support for

```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
Expand Down
15 changes: 12 additions & 3 deletions website/content/docs/commands/volume/snapshot-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,14 +27,16 @@ 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
plugins for their snapshots. This flag accepts a plugin ID or prefix. If
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 secrets 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.
Expand All @@ -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

0 comments on commit d67ecea

Please sign in to comment.