From 8d8762b5041aa2926270e415090f1890211cbddd Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Wed, 27 Apr 2022 18:57:48 +0200 Subject: [PATCH 01/17] Beginning of a new cli using cobra --- cli/cobra.go | 25 +++++++++++++++++++++++++ cli/command/commands/commands.go | 11 +++++++++++ cli/command/create/create.go | 15 +++++++++++++++ cli/command/delete/delete.go | 15 +++++++++++++++ cli/command/example/example.go | 15 +++++++++++++++ cli/command/get/get.go | 15 +++++++++++++++ cli/command/list/list.go | 15 +++++++++++++++ cli/command/set/set.go | 17 +++++++++++++++++ cli/command/update/update.go | 15 +++++++++++++++ cli/log.go | 11 +++++++++++ go.mod | 7 ++++++- go.sum | 9 +++++++++ main.go | 9 +++++++-- 13 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 cli/cobra.go create mode 100644 cli/command/commands/commands.go create mode 100644 cli/command/create/create.go create mode 100644 cli/command/delete/delete.go create mode 100644 cli/command/example/example.go create mode 100644 cli/command/get/get.go create mode 100644 cli/command/list/list.go create mode 100644 cli/command/set/set.go create mode 100644 cli/command/update/update.go create mode 100644 cli/log.go diff --git a/cli/cobra.go b/cli/cobra.go new file mode 100644 index 00000000..b7bef5bf --- /dev/null +++ b/cli/cobra.go @@ -0,0 +1,25 @@ +package cli + +import ( + "github.com/spf13/cobra" +) + +// Global else the nested folders dont work +var RootCmd = &cobra.Command{ + Use: "proxmox-api-go", + Short: "Application to configure Proxmox from the Api", +} + +func init() { + RootCmd.PersistentFlags().BoolP("insecure", "i", false, "TLS insecure mode") + RootCmd.PersistentFlags().BoolP("debug", "d", false, "debug mode") + RootCmd.PersistentFlags().IntP("timeout", "t", 300, "api task timeout in seconds") + RootCmd.PersistentFlags().StringP("file", "f", "", "file to get the config from") + RootCmd.PersistentFlags().StringP("proxyurl", "p", "", "proxy url to connect to") +} + +func Execute() { + if err := RootCmd.Execute(); err != nil { + LogFatalError(err) + } +} \ No newline at end of file diff --git a/cli/command/commands/commands.go b/cli/command/commands/commands.go new file mode 100644 index 00000000..1c563367 --- /dev/null +++ b/cli/command/commands/commands.go @@ -0,0 +1,11 @@ +package commands + +import ( + _ "github.com/Telmate/proxmox-api-go/cli/command/create" + _ "github.com/Telmate/proxmox-api-go/cli/command/delete" + _ "github.com/Telmate/proxmox-api-go/cli/command/example" + _ "github.com/Telmate/proxmox-api-go/cli/command/get" + _ "github.com/Telmate/proxmox-api-go/cli/command/list" + _ "github.com/Telmate/proxmox-api-go/cli/command/set" + _ "github.com/Telmate/proxmox-api-go/cli/command/update" +) \ No newline at end of file diff --git a/cli/command/create/create.go b/cli/command/create/create.go new file mode 100644 index 00000000..a98a6eb9 --- /dev/null +++ b/cli/command/create/create.go @@ -0,0 +1,15 @@ +package create + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "With this command you can create new items in proxmox", +} + +func init() { + cli.RootCmd.AddCommand(createCmd) +} diff --git a/cli/command/delete/delete.go b/cli/command/delete/delete.go new file mode 100644 index 00000000..230274c4 --- /dev/null +++ b/cli/command/delete/delete.go @@ -0,0 +1,15 @@ +package delete + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "With this command you can delete existing items from proxmox", +} + +func init() { + cli.RootCmd.AddCommand(deleteCmd) +} diff --git a/cli/command/example/example.go b/cli/command/example/example.go new file mode 100644 index 00000000..6fd9a7d4 --- /dev/null +++ b/cli/command/example/example.go @@ -0,0 +1,15 @@ +package example + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var exampleCmd = &cobra.Command{ + Use: "example", + Short: "This function show examples of fully populated config files", +} + +func init() { + cli.RootCmd.AddCommand(exampleCmd) +} diff --git a/cli/command/get/get.go b/cli/command/get/get.go new file mode 100644 index 00000000..817de443 --- /dev/null +++ b/cli/command/get/get.go @@ -0,0 +1,15 @@ +package get + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var getCmd = &cobra.Command{ + Use: "get", + Short: "With shows the current configuration an item in proxmox", +} + +func init() { + cli.RootCmd.AddCommand(getCmd) +} diff --git a/cli/command/list/list.go b/cli/command/list/list.go new file mode 100644 index 00000000..0a4c5913 --- /dev/null +++ b/cli/command/list/list.go @@ -0,0 +1,15 @@ +package list + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "Lists all items of the same kind from proxmox", +} + +func init() { + cli.RootCmd.AddCommand(listCmd) +} \ No newline at end of file diff --git a/cli/command/set/set.go b/cli/command/set/set.go new file mode 100644 index 00000000..cfacd43b --- /dev/null +++ b/cli/command/set/set.go @@ -0,0 +1,17 @@ +package set + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var setCmd = &cobra.Command{ + Use: "set", + Short: "This command sets the current configuration of an item", + Long: `This command sets the current configuration of an item. +Depending on if the item already exists the item will be created or updated.`, +} + +func init() { + cli.RootCmd.AddCommand(setCmd) +} \ No newline at end of file diff --git a/cli/command/update/update.go b/cli/command/update/update.go new file mode 100644 index 00000000..a5715375 --- /dev/null +++ b/cli/command/update/update.go @@ -0,0 +1,15 @@ +package update + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var updateCmd = &cobra.Command{ + Use: "update", + Short: "With this command you can update existing items within proxmox", +} + +func init() { + cli.RootCmd.AddCommand(updateCmd) +} \ No newline at end of file diff --git a/cli/log.go b/cli/log.go new file mode 100644 index 00000000..6072c57f --- /dev/null +++ b/cli/log.go @@ -0,0 +1,11 @@ +package cli + +import ( + "log" +) + +func LogFatalError(err error) { + if err != nil { + log.Fatal(err) + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 47e96922..b291a9ea 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,15 @@ module github.com/Telmate/proxmox-api-go go 1.17 -require github.com/stretchr/testify v1.6.1 +require ( + github.com/spf13/cobra v1.4.0 + github.com/stretchr/testify v1.6.1 +) require ( github.com/davecgh/go-spew v1.1.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index afe7890c..16c6dd47 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,20 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index a147026e..88878f85 100644 --- a/main.go +++ b/main.go @@ -12,11 +12,16 @@ import ( "io/ioutil" "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/Telmate/proxmox-api-go/cli" + _ "github.com/Telmate/proxmox-api-go/cli/command/commands" ) func main() { - var insecure *bool - insecure = flag.Bool("insecure", false, "TLS insecure mode") + if os.Getenv("NEW_CLI") == "true" { + cli.Execute() + os.Exit(0) + } + insecure := flag.Bool("insecure", false, "TLS insecure mode") proxmox.Debug = flag.Bool("debug", false, "debug mode") fConfigFile := flag.String("file", "", "file to get the config from") taskTimeout := flag.Int("timeout", 300, "api task timeout in seconds") From 98fe1c4acd88d045863b3a91f931481fd2a31e3c Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Wed, 27 Apr 2022 22:25:05 +0200 Subject: [PATCH 02/17] Copy NewClient logic from main to cobra --- cli/cobra.go | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/cli/cobra.go b/cli/cobra.go index b7bef5bf..9e5ba5f8 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -1,6 +1,12 @@ package cli import ( + "crypto/tls" + "log" + "os" + "regexp" + + "github.com/Telmate/proxmox-api-go/proxmox" "github.com/spf13/cobra" ) @@ -22,4 +28,36 @@ func Execute() { if err := RootCmd.Execute(); err != nil { LogFatalError(err) } -} \ No newline at end of file +} + +func NewClient()(c *proxmox.Client) { + insecure, _ := RootCmd.Flags().GetBool("insecure") + timeout, _ := RootCmd.Flags().GetInt("timeout") + proxyUrl, _ := RootCmd.Flags().GetString("proxyurl") + + tlsconf := &tls.Config{InsecureSkipVerify: true} + if !insecure { + tlsconf = nil + } + + c, err := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, tlsconf, proxyUrl, timeout) + LogFatalError(err) + if userRequiresAPIToken(os.Getenv("PM_USER")) { + c.SetAPIToken(os.Getenv("PM_USER"), os.Getenv("PM_PASS")) + // As test, get the version of the server + _, err := c.GetVersion() + if err != nil { + log.Fatalf("login error: %s", err) + } + } else { + err := c.Login(os.Getenv("PM_USER"), os.Getenv("PM_PASS"), os.Getenv("PM_OTP")) + LogFatalError(err) + } + return +} + +var rxUserRequiresToken = regexp.MustCompile("[a-z0-9]+@[a-z0-9]+![a-z0-9]+") + +func userRequiresAPIToken(userID string) bool { + return rxUserRequiresToken.MatchString(userID) +} From d1b6f22bbe376296410bb700010ae3b20a945288 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Wed, 27 Apr 2022 22:42:18 +0200 Subject: [PATCH 03/17] Added List commands --- cli/command/list/list-acmeaccounts.go | 18 ++++++++++++++++++ cli/command/list/list-acmeplugins.go | 18 ++++++++++++++++++ cli/command/list/list-guests.go | 17 +++++++++++++++++ cli/command/list/list-metricservers.go | 18 ++++++++++++++++++ cli/command/list/list-nodes.go | 17 +++++++++++++++++ cli/command/list/list-pools.go | 18 ++++++++++++++++++ cli/command/list/list-storages.go | 18 ++++++++++++++++++ cli/command/list/list-users.go | 17 +++++++++++++++++ cli/command/list/list.go | 26 ++++++++++++++++++++++++++ cli/log.go | 6 ++++++ cli/print.go | 12 ++++++++++++ 11 files changed, 185 insertions(+) create mode 100644 cli/command/list/list-acmeaccounts.go create mode 100644 cli/command/list/list-acmeplugins.go create mode 100644 cli/command/list/list-guests.go create mode 100644 cli/command/list/list-metricservers.go create mode 100644 cli/command/list/list-nodes.go create mode 100644 cli/command/list/list-pools.go create mode 100644 cli/command/list/list-storages.go create mode 100644 cli/command/list/list-users.go create mode 100644 cli/print.go diff --git a/cli/command/list/list-acmeaccounts.go b/cli/command/list/list-acmeaccounts.go new file mode 100644 index 00000000..e90cb893 --- /dev/null +++ b/cli/command/list/list-acmeaccounts.go @@ -0,0 +1,18 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_acmeaccountsCmd = &cobra.Command{ + Use: "acmeaccounts", + Short: "Prints a list of AcmeAccounts in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("AcmeAccounts") + }, +} + + +func init() { + listCmd.AddCommand(list_acmeaccountsCmd) +} diff --git a/cli/command/list/list-acmeplugins.go b/cli/command/list/list-acmeplugins.go new file mode 100644 index 00000000..36ed89fe --- /dev/null +++ b/cli/command/list/list-acmeplugins.go @@ -0,0 +1,18 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_acmepluginsCmd = &cobra.Command{ + Use: "acmeplugins", + Short: "Prints a list of AcmePlugins in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("AcmePlugins") + }, +} + + +func init() { + listCmd.AddCommand(list_acmepluginsCmd) +} diff --git a/cli/command/list/list-guests.go b/cli/command/list/list-guests.go new file mode 100644 index 00000000..9c1be139 --- /dev/null +++ b/cli/command/list/list-guests.go @@ -0,0 +1,17 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_qemuguestsCmd = &cobra.Command{ + Use: "guests", + Short: "Prints a list of Qemu/Lxc Guests in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("Guests") + }, +} + +func init() { + listCmd.AddCommand(list_qemuguestsCmd) +} diff --git a/cli/command/list/list-metricservers.go b/cli/command/list/list-metricservers.go new file mode 100644 index 00000000..5a1af818 --- /dev/null +++ b/cli/command/list/list-metricservers.go @@ -0,0 +1,18 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_metricserversCmd = &cobra.Command{ + Use: "metricservers", + Short: "Prints a list of MetricServers in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("MetricServers") + }, +} + + +func init() { + listCmd.AddCommand(list_metricserversCmd) +} diff --git a/cli/command/list/list-nodes.go b/cli/command/list/list-nodes.go new file mode 100644 index 00000000..03f8346d --- /dev/null +++ b/cli/command/list/list-nodes.go @@ -0,0 +1,17 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_nodesCmd = &cobra.Command{ + Use: "nodes", + Short: "Prints a list of Nodes in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("Nodes") + }, +} + +func init() { + listCmd.AddCommand(list_nodesCmd) +} diff --git a/cli/command/list/list-pools.go b/cli/command/list/list-pools.go new file mode 100644 index 00000000..c4f58568 --- /dev/null +++ b/cli/command/list/list-pools.go @@ -0,0 +1,18 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_poolsCmd = &cobra.Command{ + Use: "pools", + Short: "Prints a list of Pools in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("Pools") + }, +} + + +func init() { + listCmd.AddCommand(list_poolsCmd) +} diff --git a/cli/command/list/list-storages.go b/cli/command/list/list-storages.go new file mode 100644 index 00000000..116c575c --- /dev/null +++ b/cli/command/list/list-storages.go @@ -0,0 +1,18 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_storagesCmd = &cobra.Command{ + Use: "storages", + Short: "Prints a list of Storages in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("Storages") + }, +} + + +func init() { + listCmd.AddCommand(list_storagesCmd) +} diff --git a/cli/command/list/list-users.go b/cli/command/list/list-users.go new file mode 100644 index 00000000..b0d21e05 --- /dev/null +++ b/cli/command/list/list-users.go @@ -0,0 +1,17 @@ +package list + +import ( + "github.com/spf13/cobra" +) + +var list_usersCmd = &cobra.Command{ + Use: "users", + Short: "Prints a list of Users in raw json format", + Run: func(cmd *cobra.Command, args []string) { + ListRaw("Users") + }, +} + +func init() { + listCmd.AddCommand(list_usersCmd) +} diff --git a/cli/command/list/list.go b/cli/command/list/list.go index 0a4c5913..e50fecaf 100644 --- a/cli/command/list/list.go +++ b/cli/command/list/list.go @@ -12,4 +12,30 @@ var listCmd = &cobra.Command{ func init() { cli.RootCmd.AddCommand(listCmd) +} + +func ListRaw(IDtype string){ + c := cli.NewClient() + var list map[string]interface{} + var err error + switch IDtype { + case "AcmeAccounts": + list, err = c.GetAcmeAccountList() + case "AcmePlugins": + list, err = c.GetAcmePluginList() + case "Guests": + list, err = c.GetVmList() + case "MetricServers": + list, err = c.GetMetricsServerList() + case "Nodes": + list, err = c.GetNodeList() + case "Pools": + list, err = c.GetPoolList() + case "Storages": + list, err = c.GetStorageList() + case "Users": + list, err = c.GetUserList() + } + cli.LogFatalListing(IDtype, err) + cli.PrintRawJson(list) } \ No newline at end of file diff --git a/cli/log.go b/cli/log.go index 6072c57f..4b5e4ade 100644 --- a/cli/log.go +++ b/cli/log.go @@ -8,4 +8,10 @@ func LogFatalError(err error) { if err != nil { log.Fatal(err) } +} + +func LogFatalListing(text string, err error){ + if err != nil { + log.Fatalf("error listing %s %+v\n",text ,err) + } } \ No newline at end of file diff --git a/cli/print.go b/cli/print.go new file mode 100644 index 00000000..fc34298f --- /dev/null +++ b/cli/print.go @@ -0,0 +1,12 @@ +package cli + +import ( + "fmt" + "encoding/json" +) + +func PrintRawJson(input interface{}){ + list, err := json.Marshal(input) + LogFatalError(err) + fmt.Println(string(list)) +} From 8167c029c9ca880ae1d108e47affd6cf211ced65 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sat, 30 Apr 2022 17:05:20 +0200 Subject: [PATCH 04/17] New inplementation of the list snapshots feature --- cli/command/list/list-snapshots.go | 37 ++++++++++++++++++++++++++++++ cli/validate.go | 22 ++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 cli/command/list/list-snapshots.go create mode 100644 cli/validate.go diff --git a/cli/command/list/list-snapshots.go b/cli/command/list/list-snapshots.go new file mode 100644 index 00000000..2c2698ae --- /dev/null +++ b/cli/command/list/list-snapshots.go @@ -0,0 +1,37 @@ +package list + +import ( + "fmt" + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var list_snapshotsCmd = &cobra.Command{ + Use: "snapshots GuestID", + Short: "Prints a list of QemuSnapshots in raw json format", + Run: func(cmd *cobra.Command, args []string) { + id := cli.ValidateExistinGuestID(args, 0) + c := cli.NewClient() + vmr := proxmox.NewVmRef(id) + _, err := c.GetVmInfo(vmr) + cli.LogFatalError(err) + jbody, _, err := c.ListQemuSnapshot(vmr) + cli.LogFatalError(err) + temp := jbody["data"].([]interface{}) + if len(temp) == 1 { + fmt.Printf("Guest with ID (%d) has no snapshots",id) + } else { + for _, e := range temp { + snapshotName := e.(map[string]interface{})["name"].(string) + if snapshotName != "current" { + fmt.Println(snapshotName) + } + } + } + }, +} + +func init() { + listCmd.AddCommand(list_snapshotsCmd) +} diff --git a/cli/validate.go b/cli/validate.go new file mode 100644 index 00000000..7e4cc85c --- /dev/null +++ b/cli/validate.go @@ -0,0 +1,22 @@ +package cli + +import ( + "strconv" + "fmt" + "log" +) + +func ValidateIDset(args []string, indexPos int, text string) (string){ + if indexPos+1 > len(args) { + log.Fatal(fmt.Errorf("error: no %s has been provided", text)) + } + return args[indexPos] +} + +func ValidateExistinGuestID(args []string, indexPos int) (int){ + id, err := strconv.Atoi(ValidateIDset(args, indexPos, "GuestID")) + if err != nil || id < 100 { + log.Fatal(fmt.Errorf("error: GuestID must be a positive integer of 100 or greater")) + } + return id +} \ No newline at end of file From 5206832553c6471a4d2da5c87d4900ebdbd3d40c Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sun, 1 May 2022 17:16:19 +0200 Subject: [PATCH 05/17] Add Intergration test for List_Nodes, List_Users --- cli/command/list/list.go | 2 +- cli/print.go | 5 +++-- test/cli/Nodes/list_nodes_test.go | 23 +++++++++++++++++++++++ test/cli/Users/list_users_test.go | 24 ++++++++++++++++++++++++ test/cli/preperations.go | 12 ++++++++++++ test/cli/shared_tests.go | 25 +++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 test/cli/Nodes/list_nodes_test.go create mode 100644 test/cli/Users/list_users_test.go create mode 100644 test/cli/preperations.go create mode 100644 test/cli/shared_tests.go diff --git a/cli/command/list/list.go b/cli/command/list/list.go index e50fecaf..6264eef4 100644 --- a/cli/command/list/list.go +++ b/cli/command/list/list.go @@ -37,5 +37,5 @@ func ListRaw(IDtype string){ list, err = c.GetUserList() } cli.LogFatalListing(IDtype, err) - cli.PrintRawJson(list) + cli.PrintRawJson(listCmd.OutOrStdout(),list) } \ No newline at end of file diff --git a/cli/print.go b/cli/print.go index fc34298f..90f4e843 100644 --- a/cli/print.go +++ b/cli/print.go @@ -2,11 +2,12 @@ package cli import ( "fmt" + "io" "encoding/json" ) -func PrintRawJson(input interface{}){ +func PrintRawJson(out io.Writer, input interface{}){ list, err := json.Marshal(input) LogFatalError(err) - fmt.Println(string(list)) + fmt.Fprintln(out,string(list)) } diff --git a/test/cli/Nodes/list_nodes_test.go b/test/cli/Nodes/list_nodes_test.go new file mode 100644 index 00000000..e5d63467 --- /dev/null +++ b/test/cli/Nodes/list_nodes_test.go @@ -0,0 +1,23 @@ +package cli_node_test + +import ( + "testing" + cliTest "github.com/Telmate/proxmox-api-go/test/cli" +) + +func Test_List_Nodes(t *testing.T) { + cliTest.SetEnvironmentVariables() + tests := []struct { + name string + args []string + expected string + }{{ + name: "List_User_root@pam", + args: []string{"-i","list","nodes"}, + expected: `"id":"node/pve"`, + }} + + for _, test := range tests { + cliTest.ListTest(t,test.args,test.expected) + } +} \ No newline at end of file diff --git a/test/cli/Users/list_users_test.go b/test/cli/Users/list_users_test.go new file mode 100644 index 00000000..2c99725e --- /dev/null +++ b/test/cli/Users/list_users_test.go @@ -0,0 +1,24 @@ +package cli_user_test + +import ( + "testing" + _ "github.com/Telmate/proxmox-api-go/cli/command/commands" + cliTest "github.com/Telmate/proxmox-api-go/test/cli" +) + +func Test_List_Users(t *testing.T) { + cliTest.SetEnvironmentVariables() + tests := []struct { + name string + args []string + expected string + }{{ + name: "List_User_root@pam", + args: []string{"-i","list","users"}, + expected: `"userid":"root@pam"`, + }} + + for _, test := range tests { + cliTest.ListTest(t,test.args,test.expected) + } +} \ No newline at end of file diff --git a/test/cli/preperations.go b/test/cli/preperations.go new file mode 100644 index 00000000..ae7d5454 --- /dev/null +++ b/test/cli/preperations.go @@ -0,0 +1,12 @@ +package test + +import ( + "os" +) + + +func SetEnvironmentVariables() { + os.Setenv("PM_API_URL","https://192.168.67.4:8006/api2/json") + os.Setenv("PM_USER","root@pam") + os.Setenv("PM_PASS","Enter123!") +} diff --git a/test/cli/shared_tests.go b/test/cli/shared_tests.go new file mode 100644 index 00000000..889134af --- /dev/null +++ b/test/cli/shared_tests.go @@ -0,0 +1,25 @@ +package test + +import ( + "testing" + "bytes" + "io/ioutil" + "github.com/Telmate/proxmox-api-go/cli" + _ "github.com/Telmate/proxmox-api-go/cli/command/commands" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + + +func ListTest(t *testing.T, args []string, expected string) { + cli.RootCmd.SetArgs(append(args)) + + buffer := new(bytes.Buffer) + + cli.RootCmd.SetOut(buffer) + err := cli.RootCmd.Execute() + require.NoError(t, err) + + out, _ := ioutil.ReadAll(buffer) + assert.Contains(t, string(out), expected) +} From 3869e5b2d84c92b8155ac32ea2281a9930b94043 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 2 May 2022 01:52:42 +0200 Subject: [PATCH 06/17] Change Error handeling --- cli/cobra.go | 7 ++++--- main.go | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cli/cobra.go b/cli/cobra.go index 9e5ba5f8..603ee6d1 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -24,10 +24,11 @@ func init() { RootCmd.PersistentFlags().StringP("proxyurl", "p", "", "proxy url to connect to") } -func Execute() { - if err := RootCmd.Execute(); err != nil { - LogFatalError(err) +func Execute() (err error) { + if err = RootCmd.Execute(); err != nil { + return } + return } func NewClient()(c *proxmox.Client) { diff --git a/main.go b/main.go index 88878f85..4f56a06d 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,10 @@ import ( func main() { if os.Getenv("NEW_CLI") == "true" { - cli.Execute() + err := cli.Execute() + if err != nil { + failError(err) + } os.Exit(0) } insecure := flag.Bool("insecure", false, "TLS insecure mode") From 8077a0fd95e58a09f908e78a8f6e37ee4b3b98c6 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 2 May 2022 01:54:04 +0200 Subject: [PATCH 07/17] Add Pool Create,Delete,Get,update Functions --- cli/command/create/create-pool.go | 29 ++++++++++++++++++++++ cli/command/delete/delete-pool.go | 18 ++++++++++++++ cli/command/delete/delete.go | 14 +++++++++++ cli/command/get/get-pool.go | 31 ++++++++++++++++++++++++ cli/command/update/update-poolcomment.go | 29 ++++++++++++++++++++++ cli/print.go | 12 +++++++++ 6 files changed, 133 insertions(+) create mode 100644 cli/command/create/create-pool.go create mode 100644 cli/command/delete/delete-pool.go create mode 100644 cli/command/get/get-pool.go create mode 100644 cli/command/update/update-poolcomment.go diff --git a/cli/command/create/create-pool.go b/cli/command/create/create-pool.go new file mode 100644 index 00000000..97a20070 --- /dev/null +++ b/cli/command/create/create-pool.go @@ -0,0 +1,29 @@ +package create + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var create_poolCmd = &cobra.Command{ + Use: "pool POOLID [COMMENT]" , + Short: "Creates a new pool", + RunE: func(cmd *cobra.Command, args []string) (err error) { + id := cli.ValidateIDset(args, 0 ,"PoolID") + var comment string + if len(args) > 1 { + comment = args[1] + } + c := cli.NewClient() + err = c.CreatePool(id, comment) + if err != nil { + return + } + cli.PrintItemCreated(createCmd.OutOrStdout() ,id, "Pool") + return + }, +} + +func init() { + createCmd.AddCommand(create_poolCmd) +} diff --git a/cli/command/delete/delete-pool.go b/cli/command/delete/delete-pool.go new file mode 100644 index 00000000..69de767d --- /dev/null +++ b/cli/command/delete/delete-pool.go @@ -0,0 +1,18 @@ +package delete + +import ( + "github.com/spf13/cobra" +) + +var delete_poolCmd = &cobra.Command{ + Use: "pool POOLID", + Short: "Deletes the Speciefied pool", + RunE: func(cmd *cobra.Command, args []string) (err error) { + err = DeleteID(args, "Pool") + return + }, +} + +func init() { + deleteCmd.AddCommand(delete_poolCmd) +} diff --git a/cli/command/delete/delete.go b/cli/command/delete/delete.go index 230274c4..f21ff985 100644 --- a/cli/command/delete/delete.go +++ b/cli/command/delete/delete.go @@ -13,3 +13,17 @@ var deleteCmd = &cobra.Command{ func init() { cli.RootCmd.AddCommand(deleteCmd) } + +func DeleteID(args []string, IDtype string) (err error){ + id := cli.ValidateIDset(args, 0, IDtype+"ID") + c := cli.NewClient() + switch IDtype { + case "Pool" : + err = c.DeletePool(id) + } + if err != nil { + return + } + cli.PrintItemDeleted(deleteCmd.OutOrStdout(), id, IDtype) + return +} \ No newline at end of file diff --git a/cli/command/get/get-pool.go b/cli/command/get/get-pool.go new file mode 100644 index 00000000..2104399c --- /dev/null +++ b/cli/command/get/get-pool.go @@ -0,0 +1,31 @@ +package get + +import ( + "encoding/json" + "fmt" + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var get_poolCmd = &cobra.Command{ + Use: "pool POOLID", + Short: "Gets the configuration of the specied Pool", + RunE: func(cmd *cobra.Command, args []string) (err error) { + id := cli.ValidateIDset(args, 0, "PoolID") + c := cli.NewClient() + poolinfo, err := c.GetPoolInfo(id) + if err != nil { + return + } + poolList, err := json.Marshal(poolinfo) + if err != nil { + return + } + fmt.Fprintln(getCmd.OutOrStdout(),string(poolList)) + return + }, +} + +func init() { + getCmd.AddCommand(get_poolCmd) +} diff --git a/cli/command/update/update-poolcomment.go b/cli/command/update/update-poolcomment.go new file mode 100644 index 00000000..541d0747 --- /dev/null +++ b/cli/command/update/update-poolcomment.go @@ -0,0 +1,29 @@ +package update + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var update_poolCmd = &cobra.Command{ + Use: "poolcomment POOLID [COMMENT]", + Short: "Updates the comment on the speciefied pool", + RunE: func(cmd *cobra.Command, args []string) (err error){ + var comment string + id := cli.ValidateIDset(args, 0, "PoolID") + if len(args) > 1 { + comment = args[1] + } + c := cli.NewClient() + err = c.UpdatePoolComment(id, comment) + if err != nil { + return + } + cli.PrintItemUpdated(updateCmd.OutOrStdout() ,id, "PoolComment") + return + }, +} + +func init() { + updateCmd.AddCommand(update_poolCmd) +} diff --git a/cli/print.go b/cli/print.go index 90f4e843..ebf5b675 100644 --- a/cli/print.go +++ b/cli/print.go @@ -6,6 +6,18 @@ import ( "encoding/json" ) +func PrintItemCreated(out io.Writer, id, text string){ + fmt.Fprintf(out, "%s (%s) has been created\n", text, id) +} + +func PrintItemUpdated(out io.Writer, id, text string){ + fmt.Fprintf(out, "%s (%s) has been updated\n", text, id) +} + +func PrintItemDeleted(out io.Writer, id, text string){ + fmt.Fprintf(out, "%s (%s) has been deleted\n", text, id) +} + func PrintRawJson(out io.Writer, input interface{}){ list, err := json.Marshal(input) LogFatalError(err) From 804252bf9ebf8ea0b2cc818bdb8b5a3ec609d653 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 2 May 2022 01:54:52 +0200 Subject: [PATCH 08/17] Intergration Test: Pool --- test/cli/Pools/list_pools_test.go | 152 ++++++++++++++++++++++++++++++ test/cli/shared_tests.go | 39 ++++++++ 2 files changed, 191 insertions(+) create mode 100644 test/cli/Pools/list_pools_test.go diff --git a/test/cli/Pools/list_pools_test.go b/test/cli/Pools/list_pools_test.go new file mode 100644 index 00000000..becb9d36 --- /dev/null +++ b/test/cli/Pools/list_pools_test.go @@ -0,0 +1,152 @@ +package cli_pool_test + +import ( + "testing" + cliTest "github.com/Telmate/proxmox-api-go/test/cli" +) + +// Test0 +func Test_Pool_0_Cleanup(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: true, + Args: []string{"-i","delete","pool","test-pool0"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_Create_Without_Comment(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: false, + Args: []string{"-i","create","pool","test-pool0"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_List(t *testing.T){ + Test := cliTest.Test{ + Expected: `"test-pool0"`, + ReqErr: false, + Contains: true, + Args: []string{"-i","list","pools"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_Get_Without_Comment(t *testing.T){ + Test := cliTest.Test{ + NotExpected: `"comment"`, + ReqErr: false, + NotContains: true, + Args: []string{"-i","get","pool","test-pool0"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_Update_Comment(t *testing.T){ + Test := cliTest.Test{ + ReqErr: false, + Contains: true, + Expected: "(test-pool0)", + Args: []string{"-i","update","poolcomment","test-pool0","this is a comment"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_Get_With_Comment(t *testing.T){ + Test := cliTest.Test{ + Expected: `"this is a comment"`, + ReqErr: false, + Contains: true, + Args: []string{"-i","get","pool","test-pool0"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_Delete(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: false, + Args: []string{"-i","delete","pool","test-pool0"}, + } + Test.StandardTest(t) +} + +func Test_Pool_0_Removed(t *testing.T){ + Test := cliTest.Test{ + NotExpected: `"test-pool0"`, + ReqErr: false, + NotContains: true, + Args: []string{"-i","list","pools"}, + } + Test.StandardTest(t) +} + +// Test1 +func Test_Pool_1_Cleanup(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: true, + Args: []string{"-i","delete","pool","test-pool1"}, + } + Test.StandardTest(t) +} + +func Test_Pool_1_Create_With_Comment(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: false, + Args: []string{"-i","create","pool","test-pool1","This is a comment"}, + } + Test.StandardTest(t) +} + +func Test_Pool_1_Get_With_Comment(t *testing.T){ + Test := cliTest.Test{ + Expected: `"This is a comment"`, + ReqErr: false, + Contains: true, + Args: []string{"-i","get","pool","test-pool1"}, + } + Test.StandardTest(t) +} + +func Test_Pool_1_Update_Comment(t *testing.T){ + Test := cliTest.Test{ + ReqErr: false, + Contains: true, + Expected: "(test-pool1)", + Args: []string{"-i","update","poolcomment","test-pool1"}, + } + Test.StandardTest(t) +} + +func Test_Pool_1_Get_Without_Comment(t *testing.T){ + Test := cliTest.Test{ + NotExpected: `"comment"`, + ReqErr: false, + NotContains: true, + Args: []string{"-i","get","pool","test-pool1"}, + } + Test.StandardTest(t) +} + +func Test_Pool_1_Delete(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: false, + Args: []string{"-i","delete","pool","test-pool1"}, + } + Test.StandardTest(t) +} + +func Test_Pool_1_Removed(t *testing.T){ + Test := cliTest.Test{ + NotExpected: `"test-pool1"`, + ReqErr: false, + NotContains: true, + Args: []string{"-i","list","pools"}, + } + Test.StandardTest(t) +} \ No newline at end of file diff --git a/test/cli/shared_tests.go b/test/cli/shared_tests.go index 889134af..5095b805 100644 --- a/test/cli/shared_tests.go +++ b/test/cli/shared_tests.go @@ -10,6 +10,15 @@ import ( "github.com/stretchr/testify/require" ) +type Test struct { + Name string + Expected string + NotExpected string + ReqErr bool + Contains bool + NotContains bool + Args []string +} func ListTest(t *testing.T, args []string, expected string) { cli.RootCmd.SetArgs(append(args)) @@ -23,3 +32,33 @@ func ListTest(t *testing.T, args []string, expected string) { out, _ := ioutil.ReadAll(buffer) assert.Contains(t, string(out), expected) } + +func (test *Test) StandardTest(t *testing.T) { + SetEnvironmentVariables() + cli.RootCmd.SetArgs(test.Args) + buffer := new(bytes.Buffer) + cli.RootCmd.SetOut(buffer) + err := cli.RootCmd.Execute() + + if test.ReqErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + if test.Expected != "" { + out, _ := ioutil.ReadAll(buffer) + if test.Contains { + assert.Contains(t, string(out), test.Expected) + } else { + assert.Equal(t, string(out), test.Expected) + } + } + if test.NotExpected != "" { + out, _ := ioutil.ReadAll(buffer) + if test.NotContains { + assert.NotContains(t, string(out), test.NotExpected) + } else { + assert.NotEqual(t, string(out), test.NotExpected) + } + } +} From 8797572698d39cbfd9bfe0d42d601351de5a6a76 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 2 May 2022 16:45:41 +0200 Subject: [PATCH 09/17] Add Set user command --- cli/cobra.go | 15 ++++++++++++++- cli/command/set/set-user.go | 38 +++++++++++++++++++++++++++++++++++++ cli/print.go | 4 ++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 cli/command/set/set-user.go diff --git a/cli/cobra.go b/cli/cobra.go index 603ee6d1..bf1dd7c8 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -5,7 +5,7 @@ import ( "log" "os" "regexp" - + "io/ioutil" "github.com/Telmate/proxmox-api-go/proxmox" "github.com/spf13/cobra" ) @@ -62,3 +62,16 @@ var rxUserRequiresToken = regexp.MustCompile("[a-z0-9]+@[a-z0-9]+![a-z0-9]+") func userRequiresAPIToken(userID string) bool { return rxUserRequiresToken.MatchString(userID) } + +func NewConfig()(configSource []byte) { + var err error + file, _ := RootCmd.Flags().GetString("file") + if file != "" { + configSource, err = ioutil.ReadFile(file) + LogFatalError(err) + } else { + configSource, err = ioutil.ReadAll(RootCmd.InOrStdin()) + LogFatalError(err) + } + return +} \ No newline at end of file diff --git a/cli/command/set/set-user.go b/cli/command/set/set-user.go new file mode 100644 index 00000000..155003db --- /dev/null +++ b/cli/command/set/set-user.go @@ -0,0 +1,38 @@ +package set + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var set_userCmd = &cobra.Command{ + Use: "user USERID PASSWORD", + Short: "Sets the current state of a user", + Long: `Sets the current state of a user. +Depending on the current state of the user, the user will be created or updated. +The config can be set with the --file flag or piped from stdin. +For config examples see "example user"`, + RunE: func(cmd *cobra.Command, args []string) (err error) { + id := cli.ValidateIDset(args, 0, "UserID") + config, err := proxmox.NewConfigUserFromJson(cli.NewConfig()) + if err != nil { + return + } + var password string + if len(args) > 1 { + password = args[1] + } + c := cli.NewClient() + err = config.SetUser(id, password, c) + if err != nil { + return + } + cli.PrintItemSet(setCmd.OutOrStdout() ,id ,"User") + return + }, +} + +func init() { + setCmd.AddCommand(set_userCmd) +} diff --git a/cli/print.go b/cli/print.go index ebf5b675..6410cfda 100644 --- a/cli/print.go +++ b/cli/print.go @@ -18,6 +18,10 @@ func PrintItemDeleted(out io.Writer, id, text string){ fmt.Fprintf(out, "%s (%s) has been deleted\n", text, id) } +func PrintItemSet(out io.Writer, id, text string){ + fmt.Fprintf(out, "%s (%s) has been configured\n", text, id) +} + func PrintRawJson(out io.Writer, input interface{}){ list, err := json.Marshal(input) LogFatalError(err) From e095917b5f5aa53460394be32f6f2ee96d0b4cb5 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 2 May 2022 16:46:09 +0200 Subject: [PATCH 10/17] Add Delete User command --- cli/command/delete/delete-user.go | 18 ++++++++++++++++++ cli/command/delete/delete.go | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 cli/command/delete/delete-user.go diff --git a/cli/command/delete/delete-user.go b/cli/command/delete/delete-user.go new file mode 100644 index 00000000..57a70e5e --- /dev/null +++ b/cli/command/delete/delete-user.go @@ -0,0 +1,18 @@ +package delete + +import ( + "github.com/spf13/cobra" +) + +var delete_userCmd = &cobra.Command{ + Use: "user USERID", + Short: "Deletes the speciefied User", + RunE: func(cmd *cobra.Command, args []string) (err error) { + err = DeleteID(args, "User") + return + }, +} + +func init() { + deleteCmd.AddCommand(delete_userCmd) +} diff --git a/cli/command/delete/delete.go b/cli/command/delete/delete.go index f21ff985..16cd2e6f 100644 --- a/cli/command/delete/delete.go +++ b/cli/command/delete/delete.go @@ -20,6 +20,8 @@ func DeleteID(args []string, IDtype string) (err error){ switch IDtype { case "Pool" : err = c.DeletePool(id) + case "User" : + err = c.DeleteUser(id) } if err != nil { return From 4e92e705ec1239bdcabeb82c18cdcbf19ea4c288 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 2 May 2022 17:05:03 +0200 Subject: [PATCH 11/17] Add Input functionality to input Json --- test/cli/shared_tests.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/test/cli/shared_tests.go b/test/cli/shared_tests.go index 5095b805..d0fc8dda 100644 --- a/test/cli/shared_tests.go +++ b/test/cli/shared_tests.go @@ -4,6 +4,8 @@ import ( "testing" "bytes" "io/ioutil" + "strings" + "github.com/Telmate/proxmox-api-go/cli" _ "github.com/Telmate/proxmox-api-go/cli/command/commands" "github.com/stretchr/testify/assert" @@ -11,13 +13,18 @@ import ( ) type Test struct { - Name string - Expected string - NotExpected string - ReqErr bool - Contains bool - NotContains bool - Args []string + InputJson string //the inputted json + OutputJson string //the outputted json + + Expected string //the output that is expected + Contains bool //if the output contains (expected) or qeuals it + + NotExpected string //the output that is notexpected + NotContains bool //if the output contains (notexpected) or qeuals it + + ReqErr bool //if an error is expected as output + + Args []string //cli arguments } func ListTest(t *testing.T, args []string, expected string) { @@ -38,6 +45,7 @@ func (test *Test) StandardTest(t *testing.T) { cli.RootCmd.SetArgs(test.Args) buffer := new(bytes.Buffer) cli.RootCmd.SetOut(buffer) + cli.RootCmd.SetIn(strings.NewReader(test.InputJson)) err := cli.RootCmd.Execute() if test.ReqErr { From c3538085ad4d964c8780c8f707dc3309f92d4cee Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sat, 7 May 2022 02:26:37 +0200 Subject: [PATCH 12/17] Added the get user command --- cli/command/get/get-user.go | 17 +++++++++++++++++ cli/command/get/get.go | 16 ++++++++++++++++ cli/print.go | 6 ++++++ 3 files changed, 39 insertions(+) create mode 100644 cli/command/get/get-user.go diff --git a/cli/command/get/get-user.go b/cli/command/get/get-user.go new file mode 100644 index 00000000..77800232 --- /dev/null +++ b/cli/command/get/get-user.go @@ -0,0 +1,17 @@ +package get + +import ( + "github.com/spf13/cobra" +) + +var get_userCmd = &cobra.Command{ + Use: "user USERID", + Short: "Gets the configuration of the specified User", + RunE: func(cmd *cobra.Command, args []string) error { + return GetConfig(args, "User") + }, +} + +func init() { + getCmd.AddCommand(get_userCmd) +} diff --git a/cli/command/get/get.go b/cli/command/get/get.go index 817de443..984239cc 100644 --- a/cli/command/get/get.go +++ b/cli/command/get/get.go @@ -1,6 +1,7 @@ package get import ( + "github.com/Telmate/proxmox-api-go/proxmox" "github.com/Telmate/proxmox-api-go/cli" "github.com/spf13/cobra" ) @@ -13,3 +14,18 @@ var getCmd = &cobra.Command{ func init() { cli.RootCmd.AddCommand(getCmd) } + +func GetConfig(args []string, IDtype string) (err error) { + id := cli.ValidateIDset(args, 0, IDtype+"ID") + c := cli.NewClient() + var config interface{} + switch IDtype { + case "User" : + config, err = proxmox.NewConfigUserFromApi(id, c) + } + if err != nil { + return + } + cli.PrintFormattedJson(getCmd.OutOrStdout(),config) + return +} diff --git a/cli/print.go b/cli/print.go index 6410cfda..8f61d8af 100644 --- a/cli/print.go +++ b/cli/print.go @@ -27,3 +27,9 @@ func PrintRawJson(out io.Writer, input interface{}){ LogFatalError(err) fmt.Fprintln(out,string(list)) } + +func PrintFormattedJson(out io.Writer, input interface{}){ + list, err := json.MarshalIndent(input, "", " ") + LogFatalError(err) + fmt.Fprintln(out,string(list)) +} \ No newline at end of file From b4b6e4a9f237304b90e0fb37d4c0bed1a09e99c7 Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sat, 7 May 2022 02:27:48 +0200 Subject: [PATCH 13/17] Check for contents of err message and json output --- test/cli/shared_tests.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/cli/shared_tests.go b/test/cli/shared_tests.go index d0fc8dda..b923d81e 100644 --- a/test/cli/shared_tests.go +++ b/test/cli/shared_tests.go @@ -23,6 +23,7 @@ type Test struct { NotContains bool //if the output contains (notexpected) or qeuals it ReqErr bool //if an error is expected as output + ErrContains string //the string the error should contain Args []string //cli arguments } @@ -50,6 +51,9 @@ func (test *Test) StandardTest(t *testing.T) { if test.ReqErr { require.Error(t, err) + if test.ErrContains != "" { + assert.Contains(t, err.Error(), test.ErrContains) + } } else { require.NoError(t, err) } @@ -69,4 +73,8 @@ func (test *Test) StandardTest(t *testing.T) { assert.NotEqual(t, string(out), test.NotExpected) } } + if test.OutputJson != "" { + out, _ := ioutil.ReadAll(buffer) + require.JSONEq(t, test.OutputJson ,string(out)) + } } From f9219238d358fe551de6908e96af777d0a46804e Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sat, 7 May 2022 02:28:42 +0200 Subject: [PATCH 14/17] Intergration test for user commands --- test/cli/Users/User_0_test.go | 102 +++++++++++++++++++++++++++++ test/cli/Users/User_1_test.go | 97 +++++++++++++++++++++++++++ test/cli/Users/User_global_test.go | 17 +++++ test/cli/Users/list_users_test.go | 24 ------- 4 files changed, 216 insertions(+), 24 deletions(-) create mode 100644 test/cli/Users/User_0_test.go create mode 100644 test/cli/Users/User_1_test.go create mode 100644 test/cli/Users/User_global_test.go delete mode 100644 test/cli/Users/list_users_test.go diff --git a/test/cli/Users/User_0_test.go b/test/cli/Users/User_0_test.go new file mode 100644 index 00000000..b73d8622 --- /dev/null +++ b/test/cli/Users/User_0_test.go @@ -0,0 +1,102 @@ +package cli_user_test + +import ( + "testing" + _ "github.com/Telmate/proxmox-api-go/cli/command/commands" + cliTest "github.com/Telmate/proxmox-api-go/test/cli" +) + +func Test_User_0_Cleanup(t *testing.T){ + Test := cliTest.Test{ + ReqErr: true, + ErrContains: "test-user@pve", + Args: []string{"-i","delete","user","test-user@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_0_Set_Full_With_Password(t *testing.T){ + Test := cliTest.Test{ + InputJson: ` +{ + "comment": "this is a comment", + "email": "b.wayne@proxmox.com", + "enable": true, + "expire": 99999999, + "firstname": "Bruce", + "lastname": "Wayne", + "groups": [ + ], + "keys": "2fa key" +}`, + Expected: "(test-user@pve)", + Contains: true, + Args: []string{"-i","set","user","test-user@pve","Enter123!"}, + } + Test.StandardTest(t) +} + +// Test Login (no error) + +func Test_User_0_Get_Full(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.Test{ + OutputJson: ` +{ + "comment": "this is a comment", + "userid": "test-user@pve", + "email": "b.wayne@proxmox.com", + "enable": true, + "expire": 99999999, + "firstname": "Bruce", + "keys": "2fa key", + "lastname": "Wayne" +}`, + Args: []string{"-i","get","user","test-user@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_0_Set_Empty(t *testing.T){ + Test := cliTest.Test{ + InputJson: ` +{ + "comment": "", + "email": "", + "enable": false, + "expire": 0, + "firstname": "", + "lastname": "", + "groups": [ + ], + "keys": "" +}`, + Expected: "(test-user@pve)", + Contains: true, + Args: []string{"-i","set","user","test-user@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_0_Get_Empty(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.Test{ + OutputJson: ` +{ + "userid": "test-user@pve", + "enable": false, + "expire": 0 +}`, + Args: []string{"-i","get","user","test-user@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_0_Delete(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: false, + Args: []string{"-i","delete","user","test-user@pve"}, + } + Test.StandardTest(t) +} diff --git a/test/cli/Users/User_1_test.go b/test/cli/Users/User_1_test.go new file mode 100644 index 00000000..5463891e --- /dev/null +++ b/test/cli/Users/User_1_test.go @@ -0,0 +1,97 @@ +package cli_user_test + +import ( + "testing" + _ "github.com/Telmate/proxmox-api-go/cli/command/commands" + cliTest "github.com/Telmate/proxmox-api-go/test/cli" +) + +func Test_User_1_Cleanup(t *testing.T){ + Test := cliTest.Test{ + ReqErr: true, + ErrContains: "test-user1@pve", + Args: []string{"-i","delete","user","test-user1@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_1_Set_Empty_Without_Password(t *testing.T){ + Test := cliTest.Test{ + InputJson: ` +{ + "enable": false, + "expire": 0 +}`, + Expected: "(test-user1@pve)", + Contains: true, + Args: []string{"-i","set","user","test-user1@pve"}, + } + Test.StandardTest(t) +} + +// Test Login (error) + +func Test_User_1_Get_Empty(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.Test{ + OutputJson: ` +{ + "userid": "test-user1@pve", + "enable": false, + "expire": 0 +}`, + Args: []string{"-i","get","user","test-user1@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_1_Set_Full_With_Password(t *testing.T){ + Test := cliTest.Test{ + InputJson: ` +{ + "comment": "this is a comment", + "email": "b.wayne@proxmox.com", + "enable": true, + "expire": 99999999, + "firstname": "Bruce", + "lastname": "Wayne", + "groups": [ + ], + "keys": "2fa key" +}`, + Expected: "(test-user1@pve)", + Contains: true, + Args: []string{"-i","set","user","test-user1@pve","Enter123!"}, + } + Test.StandardTest(t) +} + +// Test Login (no error) + +func Test_User_1_Get_Full(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.Test{ + OutputJson: ` +{ + "comment": "this is a comment", + "userid": "test-user1@pve", + "email": "b.wayne@proxmox.com", + "enable": true, + "expire": 99999999, + "firstname": "Bruce", + "lastname": "Wayne", + "keys": "2fa key" +}`, + Args: []string{"-i","get","user","test-user1@pve"}, + } + Test.StandardTest(t) +} + +func Test_User_1_Delete(t *testing.T){ + Test := cliTest.Test{ + Expected: "", + ReqErr: false, + Args: []string{"-i","delete","user","test-user1@pve"}, + } + Test.StandardTest(t) +} \ No newline at end of file diff --git a/test/cli/Users/User_global_test.go b/test/cli/Users/User_global_test.go new file mode 100644 index 00000000..6502203f --- /dev/null +++ b/test/cli/Users/User_global_test.go @@ -0,0 +1,17 @@ +package cli_user_test + +import ( + "testing" + _ "github.com/Telmate/proxmox-api-go/cli/command/commands" + cliTest "github.com/Telmate/proxmox-api-go/test/cli" +) + +func Test_User_List(t *testing.T){ + Test := cliTest.Test{ + Expected: `"userid":"root@pam"`, + ReqErr: false, + Contains: true, + Args: []string{"-i","list","users"}, + } + Test.StandardTest(t) +} \ No newline at end of file diff --git a/test/cli/Users/list_users_test.go b/test/cli/Users/list_users_test.go deleted file mode 100644 index 2c99725e..00000000 --- a/test/cli/Users/list_users_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package cli_user_test - -import ( - "testing" - _ "github.com/Telmate/proxmox-api-go/cli/command/commands" - cliTest "github.com/Telmate/proxmox-api-go/test/cli" -) - -func Test_List_Users(t *testing.T) { - cliTest.SetEnvironmentVariables() - tests := []struct { - name string - args []string - expected string - }{{ - name: "List_User_root@pam", - args: []string{"-i","list","users"}, - expected: `"userid":"root@pam"`, - }} - - for _, test := range tests { - cliTest.ListTest(t,test.args,test.expected) - } -} \ No newline at end of file From 274e9ad76e2cf13ea0209156e8fe4e483432a9ad Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sat, 7 May 2022 02:31:01 +0200 Subject: [PATCH 15/17] Fix typos --- cli/command/get/get-pool.go | 2 +- cli/command/get/get.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/command/get/get-pool.go b/cli/command/get/get-pool.go index 2104399c..59ae68ce 100644 --- a/cli/command/get/get-pool.go +++ b/cli/command/get/get-pool.go @@ -9,7 +9,7 @@ import ( var get_poolCmd = &cobra.Command{ Use: "pool POOLID", - Short: "Gets the configuration of the specied Pool", + Short: "Gets the configuration of the specified Pool", RunE: func(cmd *cobra.Command, args []string) (err error) { id := cli.ValidateIDset(args, 0, "PoolID") c := cli.NewClient() diff --git a/cli/command/get/get.go b/cli/command/get/get.go index 984239cc..e4269f3b 100644 --- a/cli/command/get/get.go +++ b/cli/command/get/get.go @@ -8,7 +8,7 @@ import ( var getCmd = &cobra.Command{ Use: "get", - Short: "With shows the current configuration an item in proxmox", + Short: "get shows the current configuration an item in proxmox", } func init() { From 241b7361fe3e77e3d2cf768c465fe9983ad5766c Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Sat, 7 May 2022 03:04:05 +0200 Subject: [PATCH 16/17] Add Login Test for user --- cli/cobra.go | 37 +++++++++++++++++++++++++---------- test/cli/Users/User_0_test.go | 36 ++++++++++++++++++++++------------ test/cli/Users/User_1_test.go | 20 +++++++++++++++++-- test/cli/shared_tests.go | 17 ++++++++++++++++ 4 files changed, 85 insertions(+), 25 deletions(-) diff --git a/cli/cobra.go b/cli/cobra.go index bf1dd7c8..a45b7a08 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -2,10 +2,11 @@ package cli import ( "crypto/tls" - "log" + "fmt" + "io/ioutil" "os" "regexp" - "io/ioutil" + "github.com/Telmate/proxmox-api-go/proxmox" "github.com/spf13/cobra" ) @@ -32,6 +33,12 @@ func Execute() (err error) { } func NewClient()(c *proxmox.Client) { + c, err := Client("","","","") + LogFatalError(err) + return +} + +func Client(apiUlr, userID, password, otp string) (c *proxmox.Client, err error) { insecure, _ := RootCmd.Flags().GetBool("insecure") timeout, _ := RootCmd.Flags().GetInt("timeout") proxyUrl, _ := RootCmd.Flags().GetString("proxyurl") @@ -40,19 +47,29 @@ func NewClient()(c *proxmox.Client) { if !insecure { tlsconf = nil } - - c, err := proxmox.NewClient(os.Getenv("PM_API_URL"), nil, tlsconf, proxyUrl, timeout) + if apiUlr == "" { + apiUlr = os.Getenv("PM_API_URL") + } + if userID == "" { + userID = os.Getenv("PM_USER") + } + if password == "" { + password = os.Getenv("PM_PASS") + } + if otp == "" { + otp = os.Getenv("PM_OTP") + } + c, err = proxmox.NewClient(apiUlr, nil, tlsconf, proxyUrl, timeout) LogFatalError(err) - if userRequiresAPIToken(os.Getenv("PM_USER")) { - c.SetAPIToken(os.Getenv("PM_USER"), os.Getenv("PM_PASS")) + if userRequiresAPIToken(userID) { + c.SetAPIToken(userID, password) // As test, get the version of the server - _, err := c.GetVersion() + _, err = c.GetVersion() if err != nil { - log.Fatalf("login error: %s", err) + err = fmt.Errorf("login error: %s", err) } } else { - err := c.Login(os.Getenv("PM_USER"), os.Getenv("PM_PASS"), os.Getenv("PM_OTP")) - LogFatalError(err) + err = c.Login(userID, password, otp) } return } diff --git a/test/cli/Users/User_0_test.go b/test/cli/Users/User_0_test.go index b73d8622..9359db7f 100644 --- a/test/cli/Users/User_0_test.go +++ b/test/cli/Users/User_0_test.go @@ -9,13 +9,15 @@ import ( func Test_User_0_Cleanup(t *testing.T){ Test := cliTest.Test{ ReqErr: true, - ErrContains: "test-user@pve", - Args: []string{"-i","delete","user","test-user@pve"}, + ErrContains: "test-user0@pve", + Args: []string{"-i","delete","user","test-user0@pve"}, } Test.StandardTest(t) } -func Test_User_0_Set_Full_With_Password(t *testing.T){ +// Set groups + +func Test_User_0_Set_Full_With_Password_Set(t *testing.T){ Test := cliTest.Test{ InputJson: ` { @@ -29,14 +31,22 @@ func Test_User_0_Set_Full_With_Password(t *testing.T){ ], "keys": "2fa key" }`, - Expected: "(test-user@pve)", + Expected: "(test-user0@pve)", Contains: true, - Args: []string{"-i","set","user","test-user@pve","Enter123!"}, + Args: []string{"-i","set","user","test-user0@pve","Enter123!"}, } Test.StandardTest(t) } -// Test Login (no error) +func Test_User_0_Login_Password_Set(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.LoginTest{ + UserID: "test-user0@pve", + Password: "Enter123!", + ReqErr: false, + } + Test.Login(t) +} func Test_User_0_Get_Full(t *testing.T) { cliTest.SetEnvironmentVariables() @@ -44,7 +54,7 @@ func Test_User_0_Get_Full(t *testing.T) { OutputJson: ` { "comment": "this is a comment", - "userid": "test-user@pve", + "userid": "test-user0@pve", "email": "b.wayne@proxmox.com", "enable": true, "expire": 99999999, @@ -52,7 +62,7 @@ func Test_User_0_Get_Full(t *testing.T) { "keys": "2fa key", "lastname": "Wayne" }`, - Args: []string{"-i","get","user","test-user@pve"}, + Args: []string{"-i","get","user","test-user0@pve"}, } Test.StandardTest(t) } @@ -71,9 +81,9 @@ func Test_User_0_Set_Empty(t *testing.T){ ], "keys": "" }`, - Expected: "(test-user@pve)", + Expected: "(test-user0@pve)", Contains: true, - Args: []string{"-i","set","user","test-user@pve"}, + Args: []string{"-i","set","user","test-user0@pve"}, } Test.StandardTest(t) } @@ -83,11 +93,11 @@ func Test_User_0_Get_Empty(t *testing.T) { Test := cliTest.Test{ OutputJson: ` { - "userid": "test-user@pve", + "userid": "test-user0@pve", "enable": false, "expire": 0 }`, - Args: []string{"-i","get","user","test-user@pve"}, + Args: []string{"-i","get","user","test-user0@pve"}, } Test.StandardTest(t) } @@ -96,7 +106,7 @@ func Test_User_0_Delete(t *testing.T){ Test := cliTest.Test{ Expected: "", ReqErr: false, - Args: []string{"-i","delete","user","test-user@pve"}, + Args: []string{"-i","delete","user","test-user0@pve"}, } Test.StandardTest(t) } diff --git a/test/cli/Users/User_1_test.go b/test/cli/Users/User_1_test.go index 5463891e..f4dab7c0 100644 --- a/test/cli/Users/User_1_test.go +++ b/test/cli/Users/User_1_test.go @@ -29,7 +29,15 @@ func Test_User_1_Set_Empty_Without_Password(t *testing.T){ Test.StandardTest(t) } -// Test Login (error) +func Test_User_1_Login_Password_Not_Set(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.LoginTest{ + UserID: "test-user1@pve", + Password: "Enter123!", + ReqErr: true, + } + Test.Login(t) +} func Test_User_1_Get_Empty(t *testing.T) { cliTest.SetEnvironmentVariables() @@ -66,7 +74,15 @@ func Test_User_1_Set_Full_With_Password(t *testing.T){ Test.StandardTest(t) } -// Test Login (no error) +func Test_User_1_Login_Password_Set(t *testing.T) { + cliTest.SetEnvironmentVariables() + Test := cliTest.LoginTest{ + UserID: "test-user1@pve", + Password: "Enter123!", + ReqErr: false, + } + Test.Login(t) +} func Test_User_1_Get_Full(t *testing.T) { cliTest.SetEnvironmentVariables() diff --git a/test/cli/shared_tests.go b/test/cli/shared_tests.go index b923d81e..728f92ad 100644 --- a/test/cli/shared_tests.go +++ b/test/cli/shared_tests.go @@ -78,3 +78,20 @@ func (test *Test) StandardTest(t *testing.T) { require.JSONEq(t, test.OutputJson ,string(out)) } } + +type LoginTest struct { + APIurl string + UserID string + Password string + OTP string + ReqErr bool //if an error is expected as output +} + +func (test *LoginTest) Login(t *testing.T){ + _, err := cli.Client(test.APIurl,test.UserID,test.Password,test.OTP) + if test.ReqErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } +} \ No newline at end of file From b3c6385f4b89fe1ba6c577fd42a37887aee1ad7b Mon Sep 17 00:00:00 2001 From: Tinyblargon Date: Mon, 9 May 2022 10:33:23 +0200 Subject: [PATCH 17/17] Add: delete metricserver Command --- cli/command/delete/delete-metricserver.go | 17 +++++++++++++++++ cli/command/delete/delete.go | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 cli/command/delete/delete-metricserver.go diff --git a/cli/command/delete/delete-metricserver.go b/cli/command/delete/delete-metricserver.go new file mode 100644 index 00000000..1163b4a0 --- /dev/null +++ b/cli/command/delete/delete-metricserver.go @@ -0,0 +1,17 @@ +package delete + +import ( + "github.com/spf13/cobra" +) + +var delete_metricserverCmd = &cobra.Command{ + Use: "metricserver METRICSID", + Short: "Deletes the speciefied MetricServer", + RunE: func(cmd *cobra.Command, args []string) error { + return DeleteID(args, "MetricServer") + }, +} + +func init() { + deleteCmd.AddCommand(delete_metricserverCmd) +} diff --git a/cli/command/delete/delete.go b/cli/command/delete/delete.go index 16cd2e6f..87bb3674 100644 --- a/cli/command/delete/delete.go +++ b/cli/command/delete/delete.go @@ -18,6 +18,8 @@ func DeleteID(args []string, IDtype string) (err error){ id := cli.ValidateIDset(args, 0, IDtype+"ID") c := cli.NewClient() switch IDtype { + case "MetricServer" : + err = c.DeleteMetricServer(id) case "Pool" : err = c.DeletePool(id) case "User" :