From 79299391162a36142f2da7858e5a516465677491 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Mon, 3 Feb 2025 11:29:43 -0500 Subject: [PATCH] volume delete: allow prefix for ID (#24997) The `volume delete` command doesn't allow using a prefix for the volume ID for either CSI or dynamic host volumes. Use a prefix search and wildcard namespace as we do for other CLI commands. Ref: https://hashicorp.atlassian.net/browse/NET-12057 --- .changelog/24997.txt | 3 ++ command/volume_delete.go | 47 +++++++++++++++++++++++++++++- command/volume_delete_host_test.go | 12 ++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 .changelog/24997.txt diff --git a/.changelog/24997.txt b/.changelog/24997.txt new file mode 100644 index 000000000..58c7a4ce4 --- /dev/null +++ b/.changelog/24997.txt @@ -0,0 +1,3 @@ +```release-note:improvement +csi: Accept ID prefixes and wildcard namespace for the volume delete command +``` diff --git a/command/volume_delete.go b/command/volume_delete.go index 23a82dbe0..e23e02f6f 100644 --- a/command/volume_delete.go +++ b/command/volume_delete.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/api/contexts" + "github.com/hashicorp/nomad/helper" flaghelper "github.com/hashicorp/nomad/helper/flags" "github.com/posener/complete" ) @@ -136,7 +137,31 @@ func (c *VolumeDeleteCommand) deleteCSIVolume(client *api.Client, volID string, } } - err := client.CSIVolumes().DeleteOpts(&api.CSIVolumeDeleteRequest{ + // get a CSI volume that matches the given prefix or a list of all matches + // if an exact match is not found. + stub, possible, err := getByPrefix[api.CSIVolumeListStub]("volumes", client.CSIVolumes().List, + func(vol *api.CSIVolumeListStub, prefix string) bool { return vol.ID == prefix }, + &api.QueryOptions{ + Prefix: volID, + Namespace: c.namespace, + }) + if err != nil { + c.Ui.Error(fmt.Sprintf("Could not find existing volume to delete: %s", err)) + return 1 + } + if len(possible) > 0 { + out, err := csiFormatVolumes(possible, false, "") + if err != nil { + c.Ui.Error(fmt.Sprintf("Error formatting: %s", err)) + return 1 + } + c.Ui.Error(fmt.Sprintf("Prefix matched multiple volumes\n\n%s", out)) + return 1 + } + volID = stub.ID + c.namespace = stub.Namespace + + err = client.CSIVolumes().DeleteOpts(&api.CSIVolumeDeleteRequest{ ExternalVolumeID: volID, Secrets: secrets, }, nil) @@ -150,6 +175,26 @@ func (c *VolumeDeleteCommand) deleteCSIVolume(client *api.Client, volID string, } func (c *VolumeDeleteCommand) deleteHostVolume(client *api.Client, volID string) int { + + if !helper.IsUUID(volID) { + stub, possible, err := getHostVolumeByPrefix(client, volID, c.namespace) + if err != nil { + c.Ui.Error(fmt.Sprintf("Could not find existing volume to delete: %s", err)) + return 1 + } + if len(possible) > 0 { + out, err := formatHostVolumes(possible, formatOpts{short: true}) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error formatting: %s", err)) + return 1 + } + c.Ui.Error(fmt.Sprintf("Prefix matched multiple volumes\n\n%s", out)) + return 1 + } + volID = stub.ID + c.namespace = stub.Namespace + } + _, err := client.HostVolumes().Delete(&api.HostVolumeDeleteRequest{ID: volID}, nil) if err != nil { c.Ui.Error(fmt.Sprintf("Error deleting volume: %s", err)) diff --git a/command/volume_delete_host_test.go b/command/volume_delete_host_test.go index 7215ebb69..87fd2900d 100644 --- a/command/volume_delete_host_test.go +++ b/command/volume_delete_host_test.go @@ -83,11 +83,17 @@ capability { must.StrContains(t, ui.ErrorWriter.String(), "no such volume") ui.ErrorWriter.Reset() - // fix the namespace - args = []string{"-address", url, "-type", "host", "-namespace", "prod", id} + // missing the namespace, but use a prefix + args = []string{"-address", url, "-type", "host", id[:12]} + code = cmd.Run(args) + must.Eq(t, 1, code) + must.StrContains(t, ui.ErrorWriter.String(), "no volumes with prefix") + ui.ErrorWriter.Reset() + + // fix the namespace, and use a prefix + args = []string{"-address", url, "-type", "host", "-namespace", "prod", id[:12]} code = cmd.Run(args) must.Eq(t, 0, code, must.Sprintf("got error: %s", ui.ErrorWriter.String())) out = ui.OutputWriter.String() must.StrContains(t, out, fmt.Sprintf("Successfully deleted volume %q!", id)) - }