Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSI: Add secrets flag support for delete volume #11245

Merged
merged 2 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/11245.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 delete a volume
```
20 changes: 20 additions & 0 deletions api/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func (v *CSIVolumes) Create(vol *CSIVolume, w *WriteOptions) ([]*CSIVolume, *Wri
return resp.Volumes, meta, err
}

// DEPRECATED: will be removed in Nomad 1.4.0
// Delete deletes a CSI volume from an external storage provider. The ID
// passed as an argument here is for the storage provider's ID, so a volume
// that's already been deregistered can be deleted.
Expand All @@ -107,6 +108,19 @@ func (v *CSIVolumes) Delete(externalVolID string, w *WriteOptions) error {
return err
}

// DeleteOpts deletes a CSI volume from an external storage
// provider. The ID passed in the request is for the storage
// provider's ID, so a volume that's already been deregistered can be
// deleted.
func (v *CSIVolumes) DeleteOpts(req *CSIVolumeDeleteRequest, w *WriteOptions) error {
if w == nil {
w = &WriteOptions{}
}
w.SetHeadersFromCSISecrets(req.Secrets)
_, err := v.client.delete(fmt.Sprintf("/v1/volume/csi/%v/delete", url.PathEscape(req.ExternalVolumeID)), nil, w)
return err
}

// Detach causes Nomad to attempt to detach a CSI volume from a client
// node. This is used in the case that the node is temporarily lost and the
// allocations are unable to drop their claims automatically.
Expand Down Expand Up @@ -422,6 +436,12 @@ type CSIVolumeDeregisterRequest struct {
WriteRequest
}

type CSIVolumeDeleteRequest struct {
ExternalVolumeID string
Secrets CSISecrets
WriteRequest
}

// CSISnapshot is the storage provider's view of a volume snapshot
type CSISnapshot struct {
ID string // storage provider's ID
Expand Down
4 changes: 2 additions & 2 deletions command/agent/csi_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ func (s *HTTPServer) csiVolumeDelete(id string, resp http.ResponseWriter, req *h
return nil, CodedError(405, ErrInvalidMethod)
}

secrets := parseCSISecrets(req)
args := structs.CSIVolumeDeleteRequest{
VolumeIDs: []string{id},
Secrets: secrets,
}
s.parseWriteRequest(req, &args.WriteRequest)

Expand Down Expand Up @@ -329,10 +331,8 @@ func (s *HTTPServer) csiSnapshotList(resp http.ResponseWriter, req *http.Request

query := req.URL.Query()
args.PluginID = query.Get("plugin_id")

secrets := parseCSISecrets(req)
args.Secrets = secrets

var out structs.CSISnapshotListResponse
if err := s.agent.RPC("CSIVolume.ListSnapshots", &args, &out); err != nil {
return nil, err
Expand Down
30 changes: 27 additions & 3 deletions command/volume_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import (
"fmt"
"strings"

"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/api/contexts"
flaghelper "github.com/hashicorp/nomad/helper/flags"
"github.com/posener/complete"
)

type VolumeDeleteCommand struct {
Meta
Secrets string
}

func (c *VolumeDeleteCommand) Help() string {
Expand All @@ -30,6 +33,11 @@ General Options:

` + generalOptionsUsage(usageOptsDefault) + `

Delete Options:

-secret
Secrets to pass to the plugin to delete the snapshot. Accepts multiple
flags in the form -secret key=value
`
return strings.TrimSpace(helpText)
}
Expand Down Expand Up @@ -68,8 +76,10 @@ func (c *VolumeDeleteCommand) Synopsis() string {
func (c *VolumeDeleteCommand) Name() string { return "volume delete" }

func (c *VolumeDeleteCommand) Run(args []string) int {
var secretsArgs flaghelper.StringFlag
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.Var(&secretsArgs, "secret", "secrets for snapshot, ex. -secret key=value")

if err := flags.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing arguments %s", err))
Expand All @@ -78,8 +88,8 @@ func (c *VolumeDeleteCommand) Run(args []string) int {

// Check that we get exactly two arguments
args = flags.Args()
if l := len(args); l != 1 {
c.Ui.Error("This command takes one argument: <vol id>")
if l := len(args); l < 1 {
c.Ui.Error("This command takes at least one argument: <vol id>")
tgross marked this conversation as resolved.
Show resolved Hide resolved
c.Ui.Error(commandErrorText(c))
return 1
}
Expand All @@ -92,7 +102,21 @@ func (c *VolumeDeleteCommand) Run(args []string) int {
return 1
}

err = client.CSIVolumes().Delete(volID, nil)
secrets := api.CSISecrets{}
for _, kv := range secretsArgs {
s := strings.Split(kv, "=")
if len(s) == 2 {
secrets[s[0]] = s[1]
} else {
c.Ui.Error("Secret must be in the format: -secret key=value")
return 1
}
}

err = client.CSIVolumes().DeleteOpts(&api.CSIVolumeDeleteRequest{
ExternalVolumeID: volID,
Secrets: secrets,
}, nil)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error deleting volume: %s", err))
return 1
Expand Down
12 changes: 9 additions & 3 deletions nomad/csi_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ func (v *CSIVolume) Delete(args *structs.CSIVolumeDeleteRequest, reply *structs.
// NOTE: deleting the volume in the external storage provider can't be
// made atomic with deregistration. We can't delete a volume that's
// not registered because we need to be able to lookup its plugin.
err = v.deleteVolume(vol, plugin)
err = v.deleteVolume(vol, plugin, args.Secrets)
if err != nil {
return err
}
Expand All @@ -1072,12 +1072,18 @@ func (v *CSIVolume) Delete(args *structs.CSIVolumeDeleteRequest, reply *structs.
return nil
}

func (v *CSIVolume) deleteVolume(vol *structs.CSIVolume, plugin *structs.CSIPlugin) error {
func (v *CSIVolume) deleteVolume(vol *structs.CSIVolume, plugin *structs.CSIPlugin, querySecrets structs.CSISecrets) error {
// Combine volume and query secrets into one map.
// Query secrets override any secrets stored with the volume.
combinedSecrets := vol.Secrets
for k, v := range querySecrets {
combinedSecrets[k] = v
}

method := "ClientCSI.ControllerDeleteVolume"
cReq := &cstructs.ClientCSIControllerDeleteVolumeRequest{
ExternalVolumeID: vol.ExternalID,
Secrets: vol.Secrets,
Secrets: combinedSecrets,
}
cReq.PluginID = plugin.ID
cResp := &cstructs.ClientCSIControllerDeleteVolumeResponse{}
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 @@ -1183,6 +1183,9 @@ func TestCSIVolumeEndpoint_Delete(t *testing.T) {
Region: "global",
Namespace: ns,
},
Secrets: structs.CSISecrets{
"secret-key-1": "secret-val-1",
},
}
resp1 := &structs.CSIVolumeCreateResponse{}
err = msgpackrpc.CallWithCodec(codec, "CSIVolume.Delete", req1, resp1)
Expand Down
1 change: 1 addition & 0 deletions nomad/structs/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,7 @@ type CSIVolumeCreateResponse struct {

type CSIVolumeDeleteRequest struct {
VolumeIDs []string
Secrets CSISecrets
WriteRequest
}

Expand Down
26 changes: 19 additions & 7 deletions website/content/api-docs/volumes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -468,22 +468,26 @@ The table below shows this endpoint's support for
| ---------------- | ---------------------------- |
| `NO` | `namespace:csi-write-volume` |

This endpoint accepts a `X-Nomad-CSI-Secrets` header to set secrets
for deleting the volume as comma-separated key-value pairs (see the
example below). These secrets will be merged with any secrets already
stored when the CSI volume was created.

### Parameters

- `:volume_id` `(string: <required>)` - Specifies the ID of the
volume. This must be the full ID. This is specified as part of the
path.


### Sample Request

```shell-session
$ curl \
--request DELETE \
-H "X-Nomad-CSI-Secrets: secret-key-1=value-1,secret-key-2=value-2" \
https://localhost:4646/v1/volume/csi/volume-id1/delete
```


## Detach Volume

This endpoint detaches an external volume from a Nomad client node. It is an
Expand Down Expand Up @@ -676,6 +680,11 @@ The table below shows this endpoint's support for
| ---------------- | ---------------------------- |
| `NO` | `namespace:csi-write-volume` |

This endpoint accepts a `X-Nomad-CSI-Secrets` header to set secrets
for deleting the snapshot as comma-separated key-value pairs (see the
example below). These secrets will be merged with any secrets already
stored when the CSI snapshot was created.

### Parameters

- `plugin_id` `(string: <required>)` - Specifies the prefix of a CSI plugin ID
Expand All @@ -691,6 +700,7 @@ The table below shows this endpoint's support for
```shell-session
$ curl \
--request DELETE \
-H "X-Nomad-CSI-Secrets: secret-key-1=value-1,secret-key-2=value-2" \
https://localhost:4646/v1/volumes/snapshot
```

Expand All @@ -713,6 +723,11 @@ The table below shows this endpoint's support for
| ---------------- | --------------------------- |
| `YES` | `namespace:csi-list-volume` |

This endpoint accepts a `X-Nomad-CSI-Secrets` header to set secrets
for deleting the snapshot as comma-separated key-value pairs (see the
example below). These secrets will be merged with any secrets already
stored when the CSI snapshot was created.

### Parameters

- `plugin_id` `(string: <required>)` - Specifies the prefix of a CSI plugin ID
Expand All @@ -728,15 +743,12 @@ 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& \
secrets=secret-key-1=secret-value-1,secret-key-2=secret-value-2
-H "X-Nomad-CSI-Secrets: secret-key-1=value-1,secret-key-2=value-2" \
https://localhost:4646/v1/volumes/snapshot?plugin_id=plugin-id1&per_page=2
```

### Sample Response
Expand Down
5 changes: 5 additions & 0 deletions website/content/docs/commands/volume/delete.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ When ACLs are enabled, this command requires a token with the
[csi_plugins_internals]: /docs/internals/plugins/csi#csi-plugins
[deregistered]: /docs/commands/volume/deregister
[registered]: /docs/commands/volume/register

## Delete Options

- `-secret`: Secrets to pass to the plugin to delete the
snapshot. Accepts multiple flags in the form `-secret key=value`