From f4d741568219a8d245b673253f4c8576cb5db6f0 Mon Sep 17 00:00:00 2001
From: Florent Gluck <florent.gluck@hesge.ch>
Date: Fri, 29 Jul 2022 14:07:06 +0200
Subject: [PATCH] tpldel now support regexes Slight change to the output: uses
 | as a separator when several fields are printed out (e.g.: VM name | vm ID |
 state)

---
 README.md                                  |  7 ++-
 src/client_cli/cmdTemplate/helper.go       | 55 +++++++++++++++-------
 src/client_cli/cmdTemplate/templateDel.go  | 31 ++++++++----
 src/client_cli/cmdTemplate/templateList.go |  2 +-
 src/client_cli/cmdVM/helper.go             |  9 ++++
 src/client_cli/cmdVM/vmCreate.go           |  7 ++-
 src/client_cli/cmdVM/vmDel.go              |  6 +--
 src/client_cli/cmdVM/vmDelAccess.go        |  6 +--
 src/client_cli/cmdVM/vmEdit.go             |  6 +--
 src/client_cli/cmdVM/vmSetAccess.go        |  6 +--
 src/client_cli/cmdVM/vmStart.go            |  6 +--
 src/client_cli/cmdVM/vmStop.go             |  6 +--
 12 files changed, 99 insertions(+), 48 deletions(-)

diff --git a/README.md b/README.md
index e0bd622..de96a67 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ List of supported Commands:
     vmdelaccess         Delete a user's VM access in one or more VMs (regex matching).
     tpllist             List available templates (regex matching).
     tplcreate           Create a template, either from an existing VM or from a .qcow file.
-    tpldel              Delete one or more templates.
+    tpldel              Delete one or more templates (regex matching).
 ```
 
 When attached to a VM's desktop (`vmattach` command), ctrl+F12 toggles between fullscreen/non-fullscreen modes.
@@ -293,6 +293,11 @@ Delete template `8ae56a30-3195-4aea-960d-abb45c47f99e`:
 nexus-client tpldel 8ae56a30-3195-4aea-960d-abb45c47f99e
 ```
 
+Delete all templates matching the "test" pattern:
+```
+nexus-client tpldel test
+```
+
 ## nexus-client-exam
 
 ### Compiling nexus-client-exam
diff --git a/src/client_cli/cmdTemplate/helper.go b/src/client_cli/cmdTemplate/helper.go
index 9fbd514..394f685 100644
--- a/src/client_cli/cmdTemplate/helper.go
+++ b/src/client_cli/cmdTemplate/helper.go
@@ -1,8 +1,9 @@
 package cmdTemplate
 
 import (
-	"strings"
+	"errors"
 	"regexp"
+	"strings"
 	"encoding/json"
 	"nexus-client/cmd"
 	u "nexus-client/utils"
@@ -30,11 +31,11 @@ func (tpl *Template)String() string {
 	return string(output)
 }
 
-func printUsage(c cmd.Command) {
+func printUsage(c cmd.Command, action string) {
 	u.PrintlnErr(c.GetDesc())
 	u.PrintlnErr("Usage: ",c.GetName()," [ID ...] [regex ...]")
-	const usage string = `Only templates matching the specified IDs or regexes will be listed.
-Any number of IDs or regexes can be specified.
+	u.PrintlnErr("Only templates matching the specified IDs or regexes will be "+action+".")
+	const usage string = `Any number of IDs or regexes can be specified.
 The regex only matches the templates's name and is case-insensitive.
 Regex examples:
 ""    -> matches any templates
@@ -43,6 +44,27 @@ Regex examples:
 	u.PrintlnErr(usage)
 }
 
+// Prints a list of filtered templates for a given route.
+// Return 0 if everything went well or 1 in case of failure.
+func printFilteredTemplates(c cmd.Command, args []string, route string) int {
+	if len(args) == 0 {
+		c.PrintUsage()
+		return 1
+	}
+
+	templates, err := getFilteredTemplates(route, args)
+	if err != nil {
+		u.PrintlnErr("Error: "+err.Error())
+		return 1
+	}
+
+	for _, template := range templates {
+		u.Println(template.Name+" | "+template.ID.String()+" | "+template.Access)
+	}
+
+	return 0
+}
+
 // Returns a list of filtered templates for a given route.
 // Filters are based on patterns describing IDs or regexes.
 // Patterns can either be:
@@ -55,10 +77,9 @@ Regex examples:
 //   ""    -> matches everything
 //   "."   -> matches everything
 //   "bla" -> matches any template name containing "bla"
-func printFilteredTemplates(c cmd.Command, patterns []string, route string) int {
+func getFilteredTemplates(route string, patterns []string) ([]Template, error) {
 	if len(patterns) < 1 {
-		c.PrintUsage()
-		return 1
+		return nil, errors.New("At least one ID or regex must be specified")
 	}
 
 	client := g.GetInstance().Client
@@ -78,22 +99,22 @@ func printFilteredTemplates(c cmd.Command, patterns []string, route string) int
 
 	resp, err := client.R().Get(host+route)
 	if err != nil {
-		u.PrintlnErr("Error: "+err.Error())
-		return 1
+		return nil, err
 	}
 
+	templatesList := []Template{}
+
 	if resp.IsSuccess() {
 		templates, err := getTemplates(resp)
 		if err != nil {
-			u.PrintlnErr("Error: "+err.Error())
-			return 1
+			return nil, err
 		}
 
 		for _, template := range templates {
 			found := false
 			for _, id := range ids {
 				if id == template.ID.String() {
-					u.Println(template.String())
+					templatesList = append(templatesList, template)
 					found = true
 					break
 				}
@@ -104,22 +125,22 @@ func printFilteredTemplates(c cmd.Command, patterns []string, route string) int
 			for _, regex := range regexes {
 				match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(template.Name))
 				if err != nil {
-					u.PrintlnErr("Error matching \""+regex+"\": "+err.Error())
+					return nil, errors.New("Error matching \""+regex+"\": "+err.Error())
 				} else {
 					if match {
-						u.Println(template.String())
+						templatesList = append(templatesList, template)
 						break
 					}
 				}
 			}
 		}
-		return 0
+		return templatesList, nil
 	} else {
-		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
-		return 1
+		return nil, errors.New("Error: "+resp.Status()+": "+resp.String())
 	}
 }
 
+// Retrieves all templates (no filtering).
 func getTemplates(resp *resty.Response) ([]Template, error) {
 	templates := []Template{}
 	if err := json.Unmarshal(resp.Body(), &templates); err != nil {
diff --git a/src/client_cli/cmdTemplate/templateDel.go b/src/client_cli/cmdTemplate/templateDel.go
index 9f1713b..0c70aa8 100644
--- a/src/client_cli/cmdTemplate/templateDel.go
+++ b/src/client_cli/cmdTemplate/templateDel.go
@@ -14,12 +14,11 @@ func (cmd *Del)GetName() string {
 }
  
 func (cmd *Del)GetDesc() string {
-	return "Delete one or more templates."
+	return "Delete one or more templates (regex matching)."
 }
 
 func (cmd *Del)PrintUsage() {
-	u.PrintlnErr(cmd.GetDesc())
-	u.PrintlnErr("Usage: "+cmd.Name+" id [id ...]")
+	printUsage(cmd, "deleted")
 }
 
 func (cmd *Del)Run(args []string) int {
@@ -32,23 +31,35 @@ func (cmd *Del)Run(args []string) int {
 		return 1
 	}
 
+	templates, err := getFilteredTemplates("/templates", args)
+	if err != nil {
+		u.PrintlnErr(err.Error())
+		return 1
+	}
+
+	if len(templates) == 0 {
+		u.PrintlnErr("No templates to delete!")
+		return 1
+	}
+
 	statusCode := 0
 
-	// Iterates through each template to delete
-	for i:= range(args) {
-		id := args[i]
-		resp, err := client.R().Delete(host+"/templates/"+id)
+	for _, template := range(templates) {
+		uuid := template.ID.String()
+		resp, err := client.R().Delete(host+"/templates/"+uuid)
 		if err != nil {
-			u.PrintlnErr("Error: "+err.Error())
+			u.PrintlnErr("Failed deleting template \""+template.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Template "+id+" deleted.")
+				u.Println("Deleted template \""+template.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed deleting template \""+template.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
+
 	}
+
 	return statusCode
 }
diff --git a/src/client_cli/cmdTemplate/templateList.go b/src/client_cli/cmdTemplate/templateList.go
index 49c3916..5e62104 100644
--- a/src/client_cli/cmdTemplate/templateList.go
+++ b/src/client_cli/cmdTemplate/templateList.go
@@ -13,7 +13,7 @@ func (cmd *List)GetDesc() string {
 }
 
 func (cmd *List)PrintUsage() {
-	printUsage(cmd)
+	printUsage(cmd, "listed")
 }
 
 func (cmd *List)Run(args []string) int {
diff --git a/src/client_cli/cmdVM/helper.go b/src/client_cli/cmdVM/helper.go
index bce68e1..c62a414 100644
--- a/src/client_cli/cmdVM/helper.go
+++ b/src/client_cli/cmdVM/helper.go
@@ -196,4 +196,13 @@ func getVMs(resp *resty.Response) ([]VM, error) {
 		return nil, err
 	}
 	return vms, nil
+}
+
+// Retrieve a single VM.
+func getVM(resp *resty.Response) (*VM, error) {
+	var vm *VM = &VM{}
+	if err := json.Unmarshal(resp.Body(), vm); err != nil {
+		return vm, err
+	}
+	return vm, nil
 }
\ No newline at end of file
diff --git a/src/client_cli/cmdVM/vmCreate.go b/src/client_cli/cmdVM/vmCreate.go
index e7fe682..3bcbc8e 100644
--- a/src/client_cli/cmdVM/vmCreate.go
+++ b/src/client_cli/cmdVM/vmCreate.go
@@ -97,7 +97,12 @@ func (cmd *Create)Run(args []string) int {
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Created VM \""+vmArgs.Name+"\"")
+				vm, err := getVM(resp)
+				if err != nil {
+					u.PrintlnErr("Failed retrieving server's response: "+err.Error())
+					statusCode = 1
+				}
+				u.Println("Created VM \""+vm.Name+"\" | "+vm.ID.String())
 			} else {
 				u.PrintlnErr("Failed creating VM \""+vmArgs.Name+"\": "+resp.Status()+": "+resp.String())
 				statusCode = 1
diff --git a/src/client_cli/cmdVM/vmDel.go b/src/client_cli/cmdVM/vmDel.go
index 5feb37c..f1b893f 100644
--- a/src/client_cli/cmdVM/vmDel.go
+++ b/src/client_cli/cmdVM/vmDel.go
@@ -48,13 +48,13 @@ func (cmd *Del)Run(args []string) int {
 		uuid := vm.ID.String()
 		resp, err := client.R().Delete(host+"/vms/"+uuid)
 		if err != nil {
-			u.PrintlnErr("Failed deleting VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			u.PrintlnErr("Failed deleting VM \""+vm.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Deleted VM \""+vm.Name+"\" ("+uuid+")")
+				u.Println("Deleted VM \""+vm.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Failed deleting VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed deleting VM \""+vm.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
diff --git a/src/client_cli/cmdVM/vmDelAccess.go b/src/client_cli/cmdVM/vmDelAccess.go
index 8e33cf0..0116315 100644
--- a/src/client_cli/cmdVM/vmDelAccess.go
+++ b/src/client_cli/cmdVM/vmDelAccess.go
@@ -69,13 +69,13 @@ func (cmd *DelAccess)Run(args []string) int {
 		uuid := vm.ID.String()
 		resp, err := client.R().Delete(host+"/vms/"+uuid+"/access/"+email)
 		if err != nil {
-			u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Removed VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+")")
+				u.Println("Removed VM access for "+email+" in VM \""+vm.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
diff --git a/src/client_cli/cmdVM/vmEdit.go b/src/client_cli/cmdVM/vmEdit.go
index 2da5383..007fd46 100644
--- a/src/client_cli/cmdVM/vmEdit.go
+++ b/src/client_cli/cmdVM/vmEdit.go
@@ -82,13 +82,13 @@ func (cmd *Edit)Run(args []string) int {
 		uuid := vm.ID.String()
 		resp, err := client.R().SetBody(vmParams).Put(host+"/vms/"+uuid)
 		if err != nil {
-			u.PrintlnErr("Failed editing VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			u.PrintlnErr("Failed editing VM \""+vm.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Edited VM \""+vm.Name+"\" ("+uuid+")")
+				u.Println("Edited VM \""+vm.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Failed editing VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed editing VM \""+vm.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
diff --git a/src/client_cli/cmdVM/vmSetAccess.go b/src/client_cli/cmdVM/vmSetAccess.go
index 58d0d57..08159be 100644
--- a/src/client_cli/cmdVM/vmSetAccess.go
+++ b/src/client_cli/cmdVM/vmSetAccess.go
@@ -75,13 +75,13 @@ func (cmd *SetAccess)Run(args []string) int {
 		uuid := vm.ID.String()
 		resp, err := client.R().SetBody(userUpdatedAccessArgs).Put(host+"/vms/"+uuid+"/access/"+email)
 		if err != nil {
-			u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Removed VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+")")
+				u.Println("Removed VM access for "+email+" in VM \""+vm.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed removing VM access for "+email+" in VM \""+vm.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
diff --git a/src/client_cli/cmdVM/vmStart.go b/src/client_cli/cmdVM/vmStart.go
index cea4e97..3ed02d2 100644
--- a/src/client_cli/cmdVM/vmStart.go
+++ b/src/client_cli/cmdVM/vmStart.go
@@ -48,13 +48,13 @@ func (cmd *Start)Run(args []string) int {
 		uuid := vm.ID.String()
 		resp, err := client.R().Put(host+"/vms/"+uuid+"/start")
 		if err != nil {
-			u.PrintlnErr("Failed starting VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			u.PrintlnErr("Failed starting VM \""+vm.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Started VM \""+vm.Name+"\" ("+uuid+")")
+				u.Println("Started VM \""+vm.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Failed starting VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed starting VM \""+vm.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
diff --git a/src/client_cli/cmdVM/vmStop.go b/src/client_cli/cmdVM/vmStop.go
index b1b8bdc..73180e1 100644
--- a/src/client_cli/cmdVM/vmStop.go
+++ b/src/client_cli/cmdVM/vmStop.go
@@ -48,13 +48,13 @@ func (cmd *Stop)Run(args []string) int {
 		uuid := vm.ID.String()
 		resp, err := client.R().Put(host+"/vms/"+uuid+"/stop")
 		if err != nil {
-			u.PrintlnErr("Failed stopping VM \""+vm.Name+"\" ("+uuid+"): "+err.Error())
+			u.PrintlnErr("Failed stopping VM \""+vm.Name+"\" | "+uuid+" : "+err.Error())
 			statusCode = 1
 		} else {
 			if resp.IsSuccess() {
-				u.Println("Stopped VM \""+vm.Name+"\" ("+uuid+")")
+				u.Println("Stopped VM \""+vm.Name+"\" | "+uuid)
 			} else {
-				u.PrintlnErr("Failed stopping VM \""+vm.Name+"\" ("+uuid+"): "+resp.Status()+": "+resp.String())
+				u.PrintlnErr("Failed stopping VM \""+vm.Name+"\" | "+uuid+" : "+resp.Status()+": "+resp.String())
 				statusCode = 1
 			}
 		}
-- 
GitLab