Skip to content

Commit 79a0396

Browse files
torirevillajwebster7emmahardison
committed
CSI Volume Group Snapshots ONTAP-SAN
Added support for v1beta1 CSI Volume Group Snapshots starting in Kubernetes 1.32. Co-authored-by: joe webster <[email protected]> Co-authored-by: emmahardison <[email protected]>
1 parent b18fef8 commit 79a0396

File tree

79 files changed

+6512
-1663
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+6512
-1663
lines changed

cli/api/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ type MultipleSnapshotResponse struct {
5656
Items []storage.SnapshotExternal `json:"items"`
5757
}
5858

59+
type MultipleGroupSnapshotResponse struct {
60+
Items []storage.GroupSnapshotExternal `json:"items"`
61+
}
62+
5963
type Version struct {
6064
Version string `json:"version"`
6165
MajorVersion uint `json:"majorVersion"`

cli/cmd/delete_groupsnapshot.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/netapp/trident/cli/api"
10+
"github.com/netapp/trident/storage"
11+
"github.com/netapp/trident/utils/errors"
12+
)
13+
14+
var allGroupSnapshots bool
15+
16+
func init() {
17+
deleteCmd.AddCommand(deleteGroupSnapshotCmd)
18+
deleteGroupSnapshotCmd.Flags().BoolVarP(&allGroupSnapshots, "all", "a", false, "Delete all group snapshots")
19+
}
20+
21+
var deleteGroupSnapshotCmd = &cobra.Command{
22+
Use: "groupsnapshot <group snapshot name>...",
23+
Short: "Delete one or more group snapshots from Trident",
24+
Aliases: []string{"gs", "gsnap", "groupsnapshots"},
25+
RunE: func(cmd *cobra.Command, args []string) error {
26+
if OperatingMode == ModeTunnel {
27+
command := []string{"delete", "groupsnapshot"}
28+
29+
if allGroupSnapshots {
30+
command = append(command, "--all")
31+
}
32+
33+
out, err := TunnelCommand(append(command, args...))
34+
printOutput(cmd, out, err)
35+
return err
36+
}
37+
return groupSnapshotDelete(args...)
38+
},
39+
}
40+
41+
func groupSnapshotDelete(groupSnapshotIDs ...string) error {
42+
// The --all switch cannot be used with specific snapshot IDs.
43+
if allGroupSnapshots && len(groupSnapshotIDs) > 0 {
44+
return errors.InvalidInputError("cannot use --all switch with individual group snapshots")
45+
} else if len(groupSnapshotIDs) == 0 && !allGroupSnapshots {
46+
return errors.InvalidInputError("must specify at least one group snapshot name or use --all switch")
47+
}
48+
49+
if allGroupSnapshots {
50+
var err error
51+
groupSnapshotIDs, err = GetGroupSnapshots()
52+
if err != nil {
53+
return err
54+
}
55+
}
56+
57+
for _, groupSnapshotID := range groupSnapshotIDs {
58+
if err := DeleteGroupSnapshot(groupSnapshotID); err != nil {
59+
return err
60+
}
61+
}
62+
return nil
63+
}
64+
65+
func DeleteGroupSnapshot(groupID string) error {
66+
if _, err := storage.ConvertGroupSnapshotID(groupID); err != nil {
67+
return errors.InvalidInputError(fmt.Sprintf("invalid group snapshot ID %s: %v", groupID, err))
68+
}
69+
70+
url := BaseURL() + "/groupsnapshot/" + groupID
71+
response, responseBody, err := api.InvokeRESTAPI("DELETE", url, nil)
72+
if err != nil {
73+
return err
74+
}
75+
76+
if response.StatusCode != http.StatusOK {
77+
errorMessage := fmt.Sprintf("could not delete group snapshot %s: %v", groupID,
78+
GetErrorFromHTTPResponse(response, responseBody))
79+
switch response.StatusCode {
80+
case http.StatusNotFound:
81+
return errors.NotFoundError(errorMessage)
82+
default:
83+
return errors.New(errorMessage)
84+
}
85+
}
86+
87+
return nil
88+
}

cli/cmd/delete_snapshot.go

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,94 +8,126 @@ import (
88
"strings"
99

1010
"github.com/spf13/cobra"
11+
"github.com/spf13/pflag"
1112

1213
"github.com/netapp/trident/cli/api"
14+
"github.com/netapp/trident/pkg/convert"
1315
"github.com/netapp/trident/utils/errors"
1416
)
1517

1618
var (
1719
allSnapshots bool
1820
allSnapshotsInVolume string
21+
allSnapshotsInGroup string
22+
23+
// deleteSnapshotFlagValues is used to store the REST API URL parameters for the 'delete snapshot' command.
24+
deleteSnapshotFlagValues map[string]string
1925
)
2026

2127
func init() {
2228
deleteCmd.AddCommand(deleteSnapshotCmd)
23-
deleteSnapshotCmd.Flags().BoolVar(&allSnapshots, "all", false, "Delete all snapshots")
29+
deleteSnapshotCmd.Flags().BoolVarP(&allSnapshots, "all", "a", false, "Delete all snapshots")
2430
deleteSnapshotCmd.Flags().StringVar(&allSnapshotsInVolume, "volume", "", "Delete all snapshots in volume")
31+
deleteSnapshotCmd.Flags().StringVar(&allSnapshotsInGroup, "group", "", "Delete all snapshots in a group")
32+
33+
// Only one of these flags may be specified at a time.
34+
deleteSnapshotCmd.MarkFlagsMutuallyExclusive("volume", "group", "all")
2535
}
2636

2737
var deleteSnapshotCmd = &cobra.Command{
2838
Use: "snapshot <volume/snapshot> [<volume/snapshot>...]",
2939
Short: "Delete one or more volume snapshots from Trident",
3040
Aliases: []string{"s", "snap", "snapshots"},
3141
RunE: func(cmd *cobra.Command, args []string) error {
42+
// Build a map of URL parameters for the 'delete snapshot' command.
43+
deleteSnapshotFlagValues = make(map[string]string)
44+
cmd.Flags().Visit(func(flag *pflag.Flag) {
45+
// Store the flag name and value in deleteSnapshotURLVar for use in REST API calls.
46+
deleteSnapshotFlagValues[flag.Name] = flag.Value.String()
47+
})
48+
3249
if OperatingMode == ModeTunnel {
3350
command := []string{"delete", "snapshot"}
34-
if allSnapshotsInVolume != "" {
35-
command = append(command, "--volume", allSnapshotsInVolume)
51+
52+
volume, ok := deleteSnapshotFlagValues["volume"]
53+
if ok && volume != "" {
54+
command = append(command, "--volume", volume)
3655
}
37-
if allSnapshots {
56+
57+
group, ok := deleteSnapshotFlagValues["group"]
58+
if ok && group != "" {
59+
// If a group is specified, we delete all snapshots in that group.
60+
// This typically isn't recommended, but we'll allow it for exceptional cases where
61+
// the group snapshot has been lost and the admin needs to clean up.
62+
command = append(command, "--group", group)
63+
}
64+
65+
all, ok := deleteSnapshotFlagValues["all"]
66+
if ok && convert.ToBool(all) {
3867
command = append(command, "--all")
3968
}
69+
70+
// Fail if a mix of flags is used that doesn't make sense. This should be caught by the CLI framework,
71+
// but in the case it's not, check here.
72+
if volume != "" && group != "" {
73+
return errors.InvalidInputError("cannot specify both --volume and --group flags")
74+
} else if volume != "" && convert.ToBool(all) {
75+
return errors.InvalidInputError("cannot specify --volume flag and --all flag")
76+
} else if group != "" && convert.ToBool(all) {
77+
return errors.InvalidInputError("cannot specify --group flag and --all flag")
78+
}
79+
4080
out, err := TunnelCommand(append(command, args...))
4181
printOutput(cmd, out, err)
4282
return err
43-
} else {
44-
return snapshotDelete(args)
4583
}
84+
85+
return snapshotDelete(args)
4686
},
4787
}
4888

4989
func snapshotDelete(snapshotIDs []string) error {
50-
var err error
51-
52-
if allSnapshotsInVolume != "" {
53-
// Make sure --volume isn't being used along with specific snapshots
54-
if len(snapshotIDs) > 0 {
55-
return errors.New("cannot use --volume switch and specify individual snapshots")
56-
}
90+
// The --all switch cannot be used with specific snapshot IDs.
91+
if all, _ := deleteSnapshotFlagValues["all"]; convert.ToBool(all) && len(snapshotIDs) > 0 {
92+
return errors.InvalidInputError("cannot use --all switch with individual snapshots")
93+
}
5794

58-
// Get list of snapshot IDs in the specified volume so we can delete them all
59-
snapshotIDs, err = GetSnapshots(allSnapshotsInVolume)
60-
if err != nil {
61-
return err
62-
}
95+
// The --volume switch cannot be used with specific snapshot IDs.
96+
if volume, _ := deleteSnapshotFlagValues["volume"]; volume != "" && len(snapshotIDs) > 0 {
97+
return errors.InvalidInputError("cannot use --volume switch with individual snapshots")
98+
}
6399

64-
} else if allSnapshots {
65-
// Make sure --all isn't being used along with specific snapshots
66-
if len(snapshotIDs) > 0 {
67-
return errors.New("cannot use --all switch and specify individual snapshots")
68-
}
100+
// The --group switch cannot be used with specific snapshot IDs.
101+
if group, _ := deleteSnapshotFlagValues["group"]; group != "" && len(snapshotIDs) > 0 {
102+
return errors.InvalidInputError("cannot use --group switch with individual snapshots")
103+
}
69104

70-
// Get list of snapshot IDs so we can delete them all
71-
snapshotIDs, err = GetSnapshots("")
105+
// If some snapshots were specified, we'll delete those.
106+
var err error
107+
if len(snapshotIDs) == 0 {
108+
snapshotIDs, err = GetSnapshots(buildSnapshotURL(deleteSnapshotFlagValues))
72109
if err != nil {
73110
return err
74111
}
75-
} else {
76-
// Not using --all or --volume, each snapshot must have volume specified
77-
if len(snapshotIDs) == 0 {
78-
return errors.New("volume/snapshot not specified")
79-
}
80-
for _, snapshotID := range snapshotIDs {
81-
if !strings.ContainsRune(snapshotID, '/') {
82-
return errors.InvalidInputError(fmt.Sprintf("invalid snapshot ID: %s; Please use the format "+
83-
"<volume name>/<snapshot name>", snapshotID))
84-
}
85-
}
86112
}
87113

114+
var errs error
88115
for _, snapshotID := range snapshotIDs {
116+
// Each snapshot must have volume specified.
117+
if !strings.ContainsRune(snapshotID, '/') {
118+
return errors.InvalidInputError(fmt.Sprintf("invalid snapshot ID: %s; Please use the format "+
119+
"<volume name>/<snapshot name>", snapshotID))
120+
}
121+
89122
url := BaseURL() + "/snapshot/" + snapshotID
90123

91124
response, responseBody, err := api.InvokeRESTAPI("DELETE", url, nil)
92125
if err != nil {
93126
return err
94127
} else if response.StatusCode != http.StatusOK {
95-
return fmt.Errorf("could not delete snapshot %s: %v", snapshotID,
96-
GetErrorFromHTTPResponse(response, responseBody))
128+
errs = errors.Join(errs, GetErrorFromHTTPResponse(response, responseBody))
97129
}
98130
}
99131

100-
return nil
132+
return errs
101133
}

0 commit comments

Comments
 (0)