diff --git a/cmd/rebindVsc.go b/cmd/rebindVsc.go index 60d76b4..7af9f84 100644 --- a/cmd/rebindVsc.go +++ b/cmd/rebindVsc.go @@ -28,17 +28,17 @@ func init() { rootCmd.AddCommand(rebindVscCmd) } -func rebindVsc(vscName string) error { +func rebindVsc(oldVSCName string) error { config := lib.LoadConfig() // Get the VolumeSnapshotContent object - vscObject, err := lib.GetVolumeSnapshotContentObject(vscName) + oldVSCObject, err := lib.GetVolumeSnapshotContentObject(oldVSCName) if err != nil { return fmt.Errorf("failed to get VolumeSnapshotContent object: %w", err) } // Create a restored VSC from the existing VSC - restoredVSC, err := lib.CreatePreProvisionedVSC(vscObject, config.VSRand) + restoredVSC, err := lib.CreatePreProvisionedVSC(oldVSCObject, config.VSRand) if err != nil { return fmt.Errorf("failed to create restored VolumeSnapshotContent: %w", err) } @@ -71,6 +71,28 @@ func rebindVsc(vscName string) error { return fmt.Errorf("failed to create VolumeSnapshot: %w", err) } - fmt.Printf("Successfully rebound VolumeSnapshotContent '%s' to new VolumeSnapshot '%s' in namespace '%s'\n", vscName, vsName, namespace) + fmt.Printf("Successfully rebound old VolumeSnapshotContent '%s' to new VolumeSnapshot '%s' in namespace '%s'\n", oldVSCName, vsName, namespace) + + // Delete the old VolumeSnapshotContent after making sure its deletionPolicy is 'Retain' + fmt.Printf("Attempting to delete old VolumeSnapshotContent '%s'...\n", oldVSCName) + + deletionPolicy, ok := oldVSCObject["spec"].(map[string]interface{})["deletionPolicy"].(string) + if !ok { + return fmt.Errorf("deletionPolicy not found in old VolumeSnapshotContent") + } + + if deletionPolicy != "Retain" { + return fmt.Errorf("deletionPolicy is not 'Retain' in old VolumeSnapshotContent, refusing to delete") + } + + fmt.Printf("Old VolumeSnapshotContent '%s' has deletionPolicy 'Retain' set and is thus safe to delete! Deleting...\n", oldVSCName) + + err = lib.DeleteVolumeSnapshotContent(oldVSCName) + if err != nil { + return fmt.Errorf("failed to delete old VolumeSnapshotContent: %w", err) + } + + fmt.Printf("Successfully deleted old VolumeSnapshotContent '%s'\n", oldVSCName) + return nil } diff --git a/go.mod b/go.mod index 2d6c37c..ddc1dc0 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.23 require ( github.com/davecgh/go-spew v1.1.1 + github.com/google/uuid v1.6.0 github.com/pmezard/go-difflib v1.0.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index ad2992b..712fc8d 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/internal/lib/vsc.go b/internal/lib/vsc.go index bc72d1a..f34a97c 100644 --- a/internal/lib/vsc.go +++ b/internal/lib/vsc.go @@ -7,6 +7,8 @@ import ( "log" "os/exec" "strings" + + "github.com/google/uuid" ) func GetVolumeSnapshotContentName(namespace, volumeSnapshotName string) (string, error) { @@ -165,7 +167,7 @@ func CreatePreProvisionedVSC(vscObject map[string]interface{}, postfix string) ( metadata := make(map[string]interface{}) originalMetadata := vscObject["metadata"].(map[string]interface{}) - newVSCName := originalMetadata["name"].(string) + "-" + postfix + newVSCName := "restoredvsc-" + uuid.New().String() metadata["name"] = newVSCName if labels, ok := originalMetadata["labels"]; ok { @@ -208,7 +210,12 @@ func CreatePreProvisionedVSC(vscObject map[string]interface{}, postfix string) ( // Set volumeSnapshotRef using the original values originalVolumeSnapshotRef := originalSpec["volumeSnapshotRef"].(map[string]interface{}) - newVSName := originalVolumeSnapshotRef["name"].(string) + "-" + postfix + // Get original name and remove any existing postfix (everything after last dash) + originalName := originalVolumeSnapshotRef["name"].(string) + if lastDashIndex := strings.LastIndex(originalName, "-"); lastDashIndex != -1 { + originalName = originalName[:lastDashIndex] + } + newVSName := originalName + "-" + postfix spec["volumeSnapshotRef"] = map[string]interface{}{ "name": newVSName, @@ -250,6 +257,15 @@ func CreatePreProvisionedVSC(vscObject map[string]interface{}, postfix string) ( return createdVSC, nil } +func DeleteVolumeSnapshotContent(volumeSnapshotContentName string) error { + deleteCmd := exec.Command("kubectl", "delete", "volumesnapshotcontent", volumeSnapshotContentName) + output, err := deleteCmd.CombinedOutput() + if err != nil { + return fmt.Errorf("failed to delete VolumeSnapshotContent: %w, output: %s", err, output) + } + return nil +} + // func deepCopy(src, dst map[string]interface{}) { // for k, v := range src { // switch v := v.(type) {