From 50b2637779d0a821ddd6b91624a6289d1d99e1ca Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 19:50:52 +0100 Subject: [PATCH 1/9] feat: add `SnapshotName.Delete()` --- proxmox/snapshot.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index 35243947..f5a4f26b 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -68,16 +68,9 @@ func UpdateSnapshotDescription(c *Client, vmr *VmRef, snapshot SnapshotName, des return c.Put(map[string]interface{}{"description": description}, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/"+string(snapshot)+"/config") } +// Deletes a snapshot, same as SnapshotName.Delete() func DeleteSnapshot(c *Client, vmr *VmRef, snapshot SnapshotName) (exitStatus string, err error) { - err = c.CheckVmRef(vmr) - if err != nil { - return - } - err = snapshot.Validate() - if err != nil { - return - } - return c.DeleteWithTask("/nodes/" + vmr.node + "/" + vmr.vmType + "/" + strconv.Itoa(vmr.vmId) + "/snapshot/" + string(snapshot)) + return snapshot.Delete(c, vmr) } func RollbackSnapshot(c *Client, vmr *VmRef, snapshot string) (exitStatus string, err error) { @@ -155,6 +148,18 @@ const ( SnapshotName_Error_StartNoLetter string = "SnapshotName must start with a letter" ) +func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err error) { + err = c.CheckVmRef(vmr) + if err != nil { + return + } + err = snap.Validate() + if err != nil { + return + } + return c.DeleteWithTask("/nodes/" + vmr.node + "/" + vmr.vmType + "/" + strconv.Itoa(vmr.vmId) + "/snapshot/" + string(snap)) +} + func (name SnapshotName) Validate() error { regex, _ := regexp.Compile(`^([a-zA-Z])([a-z]|[A-Z]|[0-9]|_|-){2,39}$`) if !regex.Match([]byte(name)) { From 9739d3430056f8df81c12ba53e95acbf72b1f50a Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 19:56:30 +0100 Subject: [PATCH 2/9] feat: add `SnapshotName.Rollback()` --- proxmox/snapshot.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index f5a4f26b..147fcdf7 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -73,12 +73,9 @@ func DeleteSnapshot(c *Client, vmr *VmRef, snapshot SnapshotName) (exitStatus st return snapshot.Delete(c, vmr) } -func RollbackSnapshot(c *Client, vmr *VmRef, snapshot string) (exitStatus string, err error) { - err = c.CheckVmRef(vmr) - if err != nil { - return - } - return c.PostWithTask(nil, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/"+snapshot+"/rollback") +// Rollback to a snapshot, same as SnapshotName.Rollback() +func RollbackSnapshot(c *Client, vmr *VmRef, snapshot SnapshotName) (exitStatus string, err error) { + return snapshot.Rollback(c, vmr) } // Used for formatting the output when retrieving snapshots @@ -148,6 +145,7 @@ const ( SnapshotName_Error_StartNoLetter string = "SnapshotName must start with a letter" ) +// Deletes the specified snapshot func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err error) { err = c.CheckVmRef(vmr) if err != nil { @@ -160,6 +158,15 @@ func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err e return c.DeleteWithTask("/nodes/" + vmr.node + "/" + vmr.vmType + "/" + strconv.Itoa(vmr.vmId) + "/snapshot/" + string(snap)) } +// Rollback to the specified snapshot +func (snap SnapshotName) Rollback(c *Client, vmr *VmRef) (exitStatus string, err error) { + err = c.CheckVmRef(vmr) + if err != nil { + return + } + return c.PostWithTask(nil, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.FormatInt(int64(vmr.vmId), 10)+"/snapshot/"+string(snap)+"/rollback") +} + func (name SnapshotName) Validate() error { regex, _ := regexp.Compile(`^([a-zA-Z])([a-z]|[A-Z]|[0-9]|_|-){2,39}$`) if !regex.Match([]byte(name)) { From 8c93ee1ec56e622b0247d0755319873a8e6e026d Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:01:45 +0100 Subject: [PATCH 3/9] fix: incorrect types --- cli/command/guest/guest-rollback.go | 2 +- proxmox/client.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/command/guest/guest-rollback.go b/cli/command/guest/guest-rollback.go index 989bbc22..916c1204 100644 --- a/cli/command/guest/guest-rollback.go +++ b/cli/command/guest/guest-rollback.go @@ -15,7 +15,7 @@ var guest_rollbackCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) (err error) { vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) snapName := cli.RequiredIDset(args, 1, "SnapshotName") - _, err = proxmox.RollbackSnapshot(cli.NewClient(), vmr, snapName) + _, err = proxmox.RollbackSnapshot(cli.NewClient(), vmr, proxmox.SnapshotName(snapName)) if err == nil { fmt.Fprintf(GuestCmd.OutOrStdout(), "Guest with id (%d) has been rolled back to snapshot (%s)\n", vmr.VmId(), snapName) } diff --git a/proxmox/client.go b/proxmox/client.go index ce9c5321..906c1b97 100644 --- a/proxmox/client.go +++ b/proxmox/client.go @@ -760,7 +760,7 @@ func (c *Client) ListQemuSnapshot(vmr *VmRef) (taskResponse map[string]interface // DEPRECATED superseded by RollbackSnapshot() func (c *Client) RollbackQemuVm(vmr *VmRef, snapshot string) (exitStatus string, err error) { - return RollbackSnapshot(c, vmr, snapshot) + return RollbackSnapshot(c, vmr, SnapshotName(snapshot)) } // DEPRECATED SetVmConfig - send config options From eee8b3e0428b5f008817d41ae97ff35d3234f309 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:10:37 +0100 Subject: [PATCH 4/9] feat: add unsafe create for snapshots --- cli/command/create/create-snapshot.go | 2 +- proxmox/snapshot.go | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cli/command/create/create-snapshot.go b/cli/command/create/create-snapshot.go index e187fa68..a0fb057e 100644 --- a/cli/command/create/create-snapshot.go +++ b/cli/command/create/create-snapshot.go @@ -29,7 +29,7 @@ var ( if err != nil { return } - err = config.CreateSnapshot(client, vmr) + err = config.Create(client, vmr) if err != nil { return } diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index 147fcdf7..2602c7b3 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -23,7 +23,8 @@ func (config ConfigSnapshot) mapToApiValues() map[string]interface{} { } } -func (config ConfigSnapshot) CreateSnapshot(c *Client, vmr *VmRef) (err error) { +// Creates a snapshot and validates the input +func (config ConfigSnapshot) Create(c *Client, vmr *VmRef) (err error) { err = c.CheckVmRef(vmr) if err != nil { return @@ -32,13 +33,23 @@ func (config ConfigSnapshot) CreateSnapshot(c *Client, vmr *VmRef) (err error) { if err != nil { return } + return config.Create_Unsafe(c, vmr) +} + +// Create a snapshot without validating the input, use ConfigSnapshot.Create() to validate the input. +func (config ConfigSnapshot) Create_Unsafe(c *Client, vmr *VmRef) error { params := config.mapToApiValues() - _, err = c.PostWithTask(params, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/") + _, err := c.PostWithTask(params, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/") if err != nil { params, _ := json.Marshal(¶ms) return fmt.Errorf("error creating Snapshot: %v, (params: %v)", err, string(params)) } - return + return nil +} + +// deprecated use ConfigSnapshot.Create() instead +func (config ConfigSnapshot) CreateSnapshot(c *Client, vmr *VmRef) error { + return config.Create(c, vmr) } func (config ConfigSnapshot) Validate() error { From 8943618d6d658a5c388e02465b32a9528880e11b Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:19:31 +0100 Subject: [PATCH 5/9] feat: add `unsafe` functions --- proxmox/snapshot.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index 2602c7b3..fefb6e2c 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -156,7 +156,7 @@ const ( SnapshotName_Error_StartNoLetter string = "SnapshotName must start with a letter" ) -// Deletes the specified snapshot +// Deletes the specified snapshot, validates the input func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err error) { err = c.CheckVmRef(vmr) if err != nil { @@ -166,15 +166,25 @@ func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err e if err != nil { return } + return snap.Delete_Unsafe(c, vmr) +} + +// Deletes the specified snapshot without validating the input, use SnapshotName.Delete() to validate the input. +func (snap SnapshotName) Delete_Unsafe(c *Client, vmr *VmRef) (exitStatus string, err error) { return c.DeleteWithTask("/nodes/" + vmr.node + "/" + vmr.vmType + "/" + strconv.Itoa(vmr.vmId) + "/snapshot/" + string(snap)) } -// Rollback to the specified snapshot +// Rollback to the specified snapshot, validates the input func (snap SnapshotName) Rollback(c *Client, vmr *VmRef) (exitStatus string, err error) { err = c.CheckVmRef(vmr) if err != nil { return } + return snap.Rollback_Unsafe(c, vmr) +} + +// Rollback to the specified snapshot without validating the input, use SnapshotName.Rollback() to validate the input. +func (snap SnapshotName) Rollback_Unsafe(c *Client, vmr *VmRef) (exitStatus string, err error) { return c.PostWithTask(nil, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.FormatInt(int64(vmr.vmId), 10)+"/snapshot/"+string(snap)+"/rollback") } From 89988786c74b6825eb4dad3281d669002d6866ad Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:29:33 +0100 Subject: [PATCH 6/9] fix: add `SnapshotName.UpdateDescription()` --- proxmox/snapshot.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index fefb6e2c..a77a6727 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -66,17 +66,9 @@ func ListSnapshots(c *Client, vmr *VmRef) (rawSnapshots, error) { return c.GetItemConfigInterfaceArray("/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/", "Guest", "SNAPSHOTS") } -// Can only be used to update the description of an already existing snapshot +// Updates the description of the specified snapshot, same as SnapshotName.UpdateDescription() func UpdateSnapshotDescription(c *Client, vmr *VmRef, snapshot SnapshotName, description string) (err error) { - err = c.CheckVmRef(vmr) - if err != nil { - return - } - err = snapshot.Validate() - if err != nil { - return - } - return c.Put(map[string]interface{}{"description": description}, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/"+string(snapshot)+"/config") + return snapshot.UpdateDescription(c, vmr, description) } // Deletes a snapshot, same as SnapshotName.Delete() @@ -166,6 +158,7 @@ func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err e if err != nil { return } + // TODO check if snapshot exists return snap.Delete_Unsafe(c, vmr) } @@ -180,6 +173,7 @@ func (snap SnapshotName) Rollback(c *Client, vmr *VmRef) (exitStatus string, err if err != nil { return } + // TODO check if snapshot exists return snap.Rollback_Unsafe(c, vmr) } @@ -188,6 +182,25 @@ func (snap SnapshotName) Rollback_Unsafe(c *Client, vmr *VmRef) (exitStatus stri return c.PostWithTask(nil, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.FormatInt(int64(vmr.vmId), 10)+"/snapshot/"+string(snap)+"/rollback") } +// Updates the description of the specified snapshot, validates the input +func (snap SnapshotName) UpdateDescription(c *Client, vmr *VmRef, description string) (err error) { + err = c.CheckVmRef(vmr) + if err != nil { + return + } + err = snap.Validate() + if err != nil { + return + } + // TODO check if snapshot exists + return snap.UpdateDescription_Unsafe(c, vmr, description) +} + +// Updates the description of the specified snapshot without validating the input, use SnapshotName.UpdateDescription() to validate the input. +func (snap SnapshotName) UpdateDescription_Unsafe(c *Client, vmr *VmRef, description string) error { + return c.Put(map[string]interface{}{"description": description}, "/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/"+string(snap)+"/config") +} + func (name SnapshotName) Validate() error { regex, _ := regexp.Compile(`^([a-zA-Z])([a-z]|[A-Z]|[0-9]|_|-){2,39}$`) if !regex.Match([]byte(name)) { From 62814ec3ca43cf60f23a6a85339eccf743441a13 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:31:41 +0100 Subject: [PATCH 7/9] fix: snapshot name not validated --- proxmox/snapshot.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index a77a6727..704ed02d 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -173,6 +173,9 @@ func (snap SnapshotName) Rollback(c *Client, vmr *VmRef) (exitStatus string, err if err != nil { return } + if err = snap.Validate(); err != nil { + return + } // TODO check if snapshot exists return snap.Rollback_Unsafe(c, vmr) } From bb6e5765703ecd9142583b63946ffd215d103d64 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:35:11 +0100 Subject: [PATCH 8/9] refactor: inline errors --- proxmox/snapshot.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/proxmox/snapshot.go b/proxmox/snapshot.go index 704ed02d..e78ea87c 100644 --- a/proxmox/snapshot.go +++ b/proxmox/snapshot.go @@ -25,12 +25,10 @@ func (config ConfigSnapshot) mapToApiValues() map[string]interface{} { // Creates a snapshot and validates the input func (config ConfigSnapshot) Create(c *Client, vmr *VmRef) (err error) { - err = c.CheckVmRef(vmr) - if err != nil { + if err = c.CheckVmRef(vmr); err != nil { return } - err = config.Validate() - if err != nil { + if err = config.Validate(); err != nil { return } return config.Create_Unsafe(c, vmr) @@ -59,8 +57,7 @@ func (config ConfigSnapshot) Validate() error { type rawSnapshots []interface{} func ListSnapshots(c *Client, vmr *VmRef) (rawSnapshots, error) { - err := c.CheckVmRef(vmr) - if err != nil { + if err := c.CheckVmRef(vmr); err != nil { return nil, err } return c.GetItemConfigInterfaceArray("/nodes/"+vmr.node+"/"+vmr.vmType+"/"+strconv.Itoa(vmr.vmId)+"/snapshot/", "Guest", "SNAPSHOTS") @@ -150,12 +147,10 @@ const ( // Deletes the specified snapshot, validates the input func (snap SnapshotName) Delete(c *Client, vmr *VmRef) (exitStatus string, err error) { - err = c.CheckVmRef(vmr) - if err != nil { + if err = c.CheckVmRef(vmr); err != nil { return } - err = snap.Validate() - if err != nil { + if err = snap.Validate(); err != nil { return } // TODO check if snapshot exists @@ -169,8 +164,7 @@ func (snap SnapshotName) Delete_Unsafe(c *Client, vmr *VmRef) (exitStatus string // Rollback to the specified snapshot, validates the input func (snap SnapshotName) Rollback(c *Client, vmr *VmRef) (exitStatus string, err error) { - err = c.CheckVmRef(vmr) - if err != nil { + if err = c.CheckVmRef(vmr); err != nil { return } if err = snap.Validate(); err != nil { @@ -187,12 +181,10 @@ func (snap SnapshotName) Rollback_Unsafe(c *Client, vmr *VmRef) (exitStatus stri // Updates the description of the specified snapshot, validates the input func (snap SnapshotName) UpdateDescription(c *Client, vmr *VmRef, description string) (err error) { - err = c.CheckVmRef(vmr) - if err != nil { + if err = c.CheckVmRef(vmr); err != nil { return } - err = snap.Validate() - if err != nil { + if err = snap.Validate(); err != nil { return } // TODO check if snapshot exists From c3aa49177d2100cfcc83552b830094b49c138fc8 Mon Sep 17 00:00:00 2001 From: Tinyblargon <76069640+Tinyblargon@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:40:46 +0100 Subject: [PATCH 9/9] refactor: use functions of `SnapshotName` --- cli/command/delete/delete-snapshot.go | 2 +- cli/command/guest/guest-rollback.go | 2 +- cli/command/update/update-snapshotdescription.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/command/delete/delete-snapshot.go b/cli/command/delete/delete-snapshot.go index 7fd7e00b..274c6017 100644 --- a/cli/command/delete/delete-snapshot.go +++ b/cli/command/delete/delete-snapshot.go @@ -14,7 +14,7 @@ var ( RunE: func(cmd *cobra.Command, args []string) (err error) { id := cli.ValidateIntIDset(args, "GuestID") snapName := cli.RequiredIDset(args, 1, "SnapshotName") - _, err = proxmox.DeleteSnapshot(cli.NewClient(), proxmox.NewVmRef(id), proxmox.SnapshotName(snapName)) + _, err = proxmox.SnapshotName(snapName).Delete(cli.NewClient(), proxmox.NewVmRef(id)) if err != nil { return } diff --git a/cli/command/guest/guest-rollback.go b/cli/command/guest/guest-rollback.go index 916c1204..39a826b1 100644 --- a/cli/command/guest/guest-rollback.go +++ b/cli/command/guest/guest-rollback.go @@ -15,7 +15,7 @@ var guest_rollbackCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) (err error) { vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) snapName := cli.RequiredIDset(args, 1, "SnapshotName") - _, err = proxmox.RollbackSnapshot(cli.NewClient(), vmr, proxmox.SnapshotName(snapName)) + _, err = proxmox.SnapshotName(snapName).Rollback(cli.NewClient(), vmr) if err == nil { fmt.Fprintf(GuestCmd.OutOrStdout(), "Guest with id (%d) has been rolled back to snapshot (%s)\n", vmr.VmId(), snapName) } diff --git a/cli/command/update/update-snapshotdescription.go b/cli/command/update/update-snapshotdescription.go index adb400d7..48283cdd 100644 --- a/cli/command/update/update-snapshotdescription.go +++ b/cli/command/update/update-snapshotdescription.go @@ -14,7 +14,7 @@ var update_snapshotCmd = &cobra.Command{ id := cli.ValidateIntIDset(args, "GuestID") snapName := cli.RequiredIDset(args, 1, "SnapshotName") des := cli.OptionalIDset(args, 2) - err = proxmox.UpdateSnapshotDescription(cli.NewClient(), proxmox.NewVmRef(id), proxmox.SnapshotName(snapName), des) + err = proxmox.SnapshotName(snapName).UpdateDescription(cli.NewClient(), proxmox.NewVmRef(id), des) if err != nil { return }