CSI ListSnapshots secrets implementation
Signed-off-by: Grant Griffiths <ggriffiths@purestorage.com>
This commit is contained in:
parent
5c45d74f3c
commit
fecbbaee22
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
csi: add flag for providing secrets as a set of key/value pairs to list snapshots
|
||||||
|
```
|
|
@ -137,7 +137,7 @@ func (v *CSIVolumes) DeleteSnapshot(snap *CSISnapshot, w *WriteOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListSnapshots lists external storage volume snapshots.
|
// 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
|
var resp *CSISnapshotListResponse
|
||||||
|
|
||||||
qp := url.Values{}
|
qp := url.Values{}
|
||||||
|
@ -150,6 +150,9 @@ func (v *CSIVolumes) ListSnapshots(pluginID string, q *QueryOptions) (*CSISnapsh
|
||||||
if q.PerPage != 0 {
|
if q.PerPage != 0 {
|
||||||
qp.Set("per_page", fmt.Sprint(q.PerPage))
|
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)
|
qm, err := v.client.query("/v1/volumes/snapshot?"+qp.Encode(), &resp, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -406,6 +409,7 @@ type CSISnapshotCreateResponse struct {
|
||||||
// fields
|
// fields
|
||||||
type CSISnapshotListRequest struct {
|
type CSISnapshotListRequest struct {
|
||||||
PluginID string
|
PluginID string
|
||||||
|
Secrets CSISecrets
|
||||||
QueryOptions
|
QueryOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -840,6 +840,10 @@ func TestCSIController_ListSnapshots(t *testing.T) {
|
||||||
CSIControllerQuery: structs.CSIControllerQuery{
|
CSIControllerQuery: structs.CSIControllerQuery{
|
||||||
PluginID: fakePlugin.Name,
|
PluginID: fakePlugin.Name,
|
||||||
},
|
},
|
||||||
|
Secrets: map[string]string{
|
||||||
|
"secret-key-1": "secret-val-1",
|
||||||
|
"secret-key-2": "secret-val-2",
|
||||||
|
},
|
||||||
StartingToken: "1",
|
StartingToken: "1",
|
||||||
MaxEntries: 100,
|
MaxEntries: 100,
|
||||||
},
|
},
|
||||||
|
|
|
@ -362,6 +362,7 @@ type ClientCSIControllerListSnapshotsRequest struct {
|
||||||
// not Nomad's own fields, for clarity when mapping between the two RPCs
|
// not Nomad's own fields, for clarity when mapping between the two RPCs
|
||||||
MaxEntries int32
|
MaxEntries int32
|
||||||
StartingToken string
|
StartingToken string
|
||||||
|
Secrets structs.CSISecrets
|
||||||
|
|
||||||
CSIControllerQuery
|
CSIControllerQuery
|
||||||
}
|
}
|
||||||
|
@ -370,6 +371,7 @@ func (req *ClientCSIControllerListSnapshotsRequest) ToCSIRequest() *csi.Controll
|
||||||
return &csi.ControllerListSnapshotsRequest{
|
return &csi.ControllerListSnapshotsRequest{
|
||||||
MaxEntries: req.MaxEntries,
|
MaxEntries: req.MaxEntries,
|
||||||
StartingToken: req.StartingToken,
|
StartingToken: req.StartingToken,
|
||||||
|
Secrets: req.Secrets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -333,6 +333,19 @@ func (s *HTTPServer) csiSnapshotList(resp http.ResponseWriter, req *http.Request
|
||||||
|
|
||||||
query := req.URL.Query()
|
query := req.URL.Query()
|
||||||
args.PluginID = query.Get("plugin_id")
|
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
|
var out structs.CSISnapshotListResponse
|
||||||
if err := s.agent.RPC("CSIVolume.ListSnapshots", &args, &out); err != nil {
|
if err := s.agent.RPC("CSIVolume.ListSnapshots", &args, &out); err != nil {
|
||||||
|
|
|
@ -35,6 +35,8 @@ List Options:
|
||||||
|
|
||||||
-plugin: Display only snapshots managed by a particular plugin. By default
|
-plugin: Display only snapshots managed by a particular plugin. By default
|
||||||
this command will query all plugins for their snapshots.
|
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)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
@ -68,11 +70,13 @@ func (c *VolumeSnapshotListCommand) Name() string { return "volume snapshot list
|
||||||
func (c *VolumeSnapshotListCommand) Run(args []string) int {
|
func (c *VolumeSnapshotListCommand) Run(args []string) int {
|
||||||
var pluginID string
|
var pluginID string
|
||||||
var verbose bool
|
var verbose bool
|
||||||
|
var secrets string
|
||||||
|
|
||||||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
||||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||||
flags.StringVar(&pluginID, "plugin", "", "")
|
flags.StringVar(&pluginID, "plugin", "", "")
|
||||||
flags.BoolVar(&verbose, "verbose", false, "")
|
flags.BoolVar(&verbose, "verbose", false, "")
|
||||||
|
flags.StringVar(&secrets, "secrets", "", "")
|
||||||
|
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error parsing arguments %s", err))
|
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
|
q := &api.QueryOptions{PerPage: 30} // TODO: tune page size
|
||||||
|
|
||||||
for {
|
for {
|
||||||
resp, _, err := client.CSIVolumes().ListSnapshots(pluginID, q)
|
resp, _, err := client.CSIVolumes().ListSnapshots(pluginID, secrets, q)
|
||||||
if err != nil && !errors.Is(err, io.EOF) {
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"Error querying CSI external snapshots for plugin %q: %s", pluginID, err))
|
"Error querying CSI external snapshots for plugin %q: %s", pluginID, err))
|
||||||
|
|
|
@ -1247,6 +1247,7 @@ func (v *CSIVolume) ListSnapshots(args *structs.CSISnapshotListRequest, reply *s
|
||||||
cReq := &cstructs.ClientCSIControllerListSnapshotsRequest{
|
cReq := &cstructs.ClientCSIControllerListSnapshotsRequest{
|
||||||
MaxEntries: args.PerPage,
|
MaxEntries: args.PerPage,
|
||||||
StartingToken: args.NextToken,
|
StartingToken: args.NextToken,
|
||||||
|
Secrets: args.Secrets,
|
||||||
}
|
}
|
||||||
cReq.PluginID = plugin.ID
|
cReq.PluginID = plugin.ID
|
||||||
cResp := &cstructs.ClientCSIControllerListSnapshotsResponse{}
|
cResp := &cstructs.ClientCSIControllerListSnapshotsResponse{}
|
||||||
|
|
|
@ -1280,6 +1280,9 @@ func TestCSIVolumeEndpoint_ListSnapshots(t *testing.T) {
|
||||||
// List snapshots
|
// List snapshots
|
||||||
|
|
||||||
req := &structs.CSISnapshotListRequest{
|
req := &structs.CSISnapshotListRequest{
|
||||||
|
Secrets: structs.CSISecrets{
|
||||||
|
"secret-key-1": "secret-val-1",
|
||||||
|
},
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Region: "global",
|
Region: "global",
|
||||||
Namespace: structs.DefaultNamespace,
|
Namespace: structs.DefaultNamespace,
|
||||||
|
|
|
@ -897,6 +897,7 @@ type CSISnapshotDeleteResponse struct {
|
||||||
// fields
|
// fields
|
||||||
type CSISnapshotListRequest struct {
|
type CSISnapshotListRequest struct {
|
||||||
PluginID string
|
PluginID string
|
||||||
|
Secrets CSISecrets
|
||||||
QueryOptions
|
QueryOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -756,12 +756,14 @@ func (r *ControllerDeleteSnapshotRequest) Validate() error {
|
||||||
type ControllerListSnapshotsRequest struct {
|
type ControllerListSnapshotsRequest struct {
|
||||||
MaxEntries int32
|
MaxEntries int32
|
||||||
StartingToken string
|
StartingToken string
|
||||||
|
Secrets structs.CSISecrets
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ControllerListSnapshotsRequest) ToCSIRepresentation() *csipbv1.ListSnapshotsRequest {
|
func (r *ControllerListSnapshotsRequest) ToCSIRepresentation() *csipbv1.ListSnapshotsRequest {
|
||||||
return &csipbv1.ListSnapshotsRequest{
|
return &csipbv1.ListSnapshotsRequest{
|
||||||
MaxEntries: r.MaxEntries,
|
MaxEntries: r.MaxEntries,
|
||||||
StartingToken: r.StartingToken,
|
StartingToken: r.StartingToken,
|
||||||
|
Secrets: r.Secrets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
return for this request. The response will include a `NextToken` field that
|
||||||
can be passed to the next request to fetch additional pages.
|
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
|
### Sample Request
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ curl \
|
$ 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
|
### Sample Response
|
||||||
|
|
|
@ -15,7 +15,7 @@ command.
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```plaintext
|
```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
|
The `volume snapshot list` command returns a list of snapshots along with their
|
||||||
|
@ -27,7 +27,7 @@ Nomad.
|
||||||
|
|
||||||
@include 'general_options.mdx'
|
@include 'general_options.mdx'
|
||||||
|
|
||||||
## Status Options
|
## List Options
|
||||||
|
|
||||||
- `-plugin`: Display only snapshots managed by a particular [CSI
|
- `-plugin`: Display only snapshots managed by a particular [CSI
|
||||||
plugin][csi_plugin]. By default the `snapshot list` command will query all
|
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
|
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
|
plugin will be queried. Otherwise, a list of matching plugins will be
|
||||||
displayed.
|
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
|
When ACLs are enabled, this command requires a token with the
|
||||||
`csi-list-volumes` capability for the plugin's namespace.
|
`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
|
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]: https://github.com/container-storage-interface/spec
|
||||||
[csi_plugin]: /docs/job-specification/csi_plugin
|
[csi_plugin]: /docs/job-specification/csi_plugin
|
||||||
[registered]: /docs/commands/volume/register
|
[registered]: /docs/commands/volume/register
|
||||||
[csi_plugins_internals]: /docs/internals/plugins/csi#csi-plugins
|
[csi_plugins_internals]: /docs/internals/plugins/csi#csi-plugins
|
Loading…
Reference in New Issue