diff --git a/src/cmd/Command.go b/src/cmd/Command.go index c1b3898229736d274e54398e5f7e4d3a278b7fc5..040c9abec60c57ea0f37041a253045641b578aba 100644 --- a/src/cmd/Command.go +++ b/src/cmd/Command.go @@ -1,39 +1,39 @@ package cmd import ( - "strings" - u "nexus-client/utils" + "strings" + u "nexus-client/utils" ) type Command interface { - GetName() string - GetDesc() []string - PrintUsage() - Run(args []string) int // Returns 0 if the Command was successful. + GetName() string + GetDesc() []string + PrintUsage() + Run(args []string) int // Returns 0 if the Command was successful. } func Help(commands []Command, indent string) { - padding := 20 - for _, cmd := range commands { - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - cmdStr := indent+cmd.GetName() - u.PrintErr(cmdStr) - pad := strings.Repeat(" ", padding-len(cmd.GetName())) - for i, desc := range cmd.GetDesc() { - if i > 0 { - u.PrintErr(strings.Repeat(" ", len(cmdStr))) - } - u.PrintlnErr(pad+desc) - } - } + padding := 20 + for _, cmd := range commands { + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + cmdStr := indent+cmd.GetName() + u.PrintErr(cmdStr) + pad := strings.Repeat(" ", padding-len(cmd.GetName())) + for i, desc := range cmd.GetDesc() { + if i > 0 { + u.PrintErr(strings.Repeat(" ", len(cmdStr))) + } + u.PrintlnErr(pad+desc) + } + } } // Returns true if the specified command exists. func Match(name string, cmdList []Command) (bool, Command) { - for _, cmd := range cmdList { - if name == cmd.GetName() { - return true, cmd - } - } - return false, nil + for _, cmd := range cmdList { + if name == cmd.GetName() { + return true, cmd + } + } + return false, nil } diff --git a/src/cmdLogin/login.go b/src/cmdLogin/login.go index 9c3137eb3e9f9db77e9de0d09880d5a557db99bd..6d1d8a6aac899fdbbfe746f4e832339e56e62a92 100644 --- a/src/cmdLogin/login.go +++ b/src/cmdLogin/login.go @@ -1,53 +1,53 @@ package cmdLogin import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) import ( - "errors" - "encoding/json" + "errors" + "encoding/json" ) type Login struct { - Name string + Name string } func (cmd *Login)GetName() string { - return cmd.Name + return cmd.Name } func (cmd *Login)GetDesc() []string { - return []string{ - "Log in and obtain an access token."} + return []string{ + "Log in and obtain an access token."} } func (cmd *Login)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.Name+" email password") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.Name+" email password") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func (cmd *Login)Run(args []string) int { - if len(args) != 2 { - cmd.PrintUsage() - return 1 - } + if len(args) != 2 { + cmd.PrintUsage() + return 1 + } - email := args[0] - pwd := args[1] - token, err := GetToken(email, pwd) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } + email := args[0] + pwd := args[1] + token, err := GetToken(email, pwd) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } - u.Println(token) - return 0 + u.Println(token) + return 0 } // Returns the token if the authentication was successful or an error if it wasn't. @@ -56,39 +56,39 @@ func (cmd *Login)Run(args []string) int { // Returns a jwt token if authentication was successful: // {"token":"<jwt token>"} func GetToken(user, pwd string) (string, error) { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - type LoginArgs struct { - Email string - Pwd string - } + type LoginArgs struct { + Email string + Pwd string + } - loginArgs := &LoginArgs {user, pwd} - resp, err := client.R().SetBody(loginArgs).Post(host+"/login") + loginArgs := &LoginArgs {user, pwd} + resp, err := client.R().SetBody(loginArgs).Post(host+"/login") if err != nil { - return "", err - } + return "", err + } - if resp.IsSuccess() { - type Response struct { - Token string - } - var response Response - err = json.Unmarshal(resp.Body(), &response) - if err != nil { - return "", err - } - return response.Token, nil - } else { - type Response struct { - Message string - } - var response Response - err = json.Unmarshal(resp.Body(), &response) - if err != nil { - return "", err - } - return "", errors.New(response.Message) - } + if resp.IsSuccess() { + type Response struct { + Token string + } + var response Response + err = json.Unmarshal(resp.Body(), &response) + if err != nil { + return "", err + } + return response.Token, nil + } else { + type Response struct { + Message string + } + var response Response + err = json.Unmarshal(resp.Body(), &response) + if err != nil { + return "", err + } + return "", errors.New(response.Message) + } } diff --git a/src/cmdTemplate/helper.go b/src/cmdTemplate/helper.go index 7507cd417aa561a725c7fd672e05f19344a6389c..3dc7365cbf319a22207dc6743e32eee0731cc3cd 100644 --- a/src/cmdTemplate/helper.go +++ b/src/cmdTemplate/helper.go @@ -1,113 +1,113 @@ package cmdTemplate import ( - "fmt" - "errors" - "regexp" - "strings" - "encoding/json" - "nexus-client/cmd" - u "nexus-client/utils" - g "nexus-client/globals" - "github.com/google/uuid" - "github.com/go-resty/resty/v2" + "fmt" + "errors" + "regexp" + "strings" + "encoding/json" + "nexus-client/cmd" + u "nexus-client/utils" + g "nexus-client/globals" + "github.com/google/uuid" + "github.com/go-resty/resty/v2" ) // Converts a Template structure into a pretty string. func (tpl *Template)String() string { - output, err := json.MarshalIndent(tpl, "", " ") + output, err := json.MarshalIndent(tpl, "", " ") if err != nil { return err.Error() } - return string(output) + return string(output) } func printUsage(c cmd.Command, action string) { - for _, desc := range c.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",c.GetName()," [ID ...] [regex ...]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - 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. + for _, desc := range c.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",c.GetName()," [ID ...] [regex ...]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + 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 "bla" -> matches any templates containing "bla"` - u.PrintlnErr(usage) + u.PrintlnErr(usage) } func printRegexUsageDetails() { - u.PrintlnErr("The action only applies to templates matching the specified IDs or regexes.") - const usage string = `Any number of IDs or regexes can be specified. + u.PrintlnErr("The action only applies to templates matching the specified IDs or regexes.") + const usage string = `Any number of IDs or regexes can be specified. The regex only matches the template's name and is case-insensitive. Regex examples: "." -> matches any templates "bla" -> matches any templates containing "bla" in their name` - u.PrintlnErr(usage) + 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) < 1 { - c.PrintUsage() - return 1 - } - - // Helper function to remove an element from a string array at a given index - removeArgAtIndex := func (slice []string, index int) []string { - return append(slice[:index], slice[index+1:]...) - } - - // Check if a "-l" argument is specified - foundLongOutputFlag := -1 - for idx, arg := range args { - if arg == "-l" { - foundLongOutputFlag = idx - } - } - - if foundLongOutputFlag >= 0 { - removeArgAtIndex(args, foundLongOutputFlag) - } - - templates, err := getFilteredTemplates(route, args) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - - if foundLongOutputFlag >= 0 { - for _, template := range templates { - u.Println(template.String()) - } - } else { - // Compute the length of the longest name and access (used for padding columns) - nameLen := 0 - idLen := 0 - accessLen := 0 - for _, template := range templates { - l := len(template.Name) - if l > nameLen { - nameLen = l - } - idLen = len(template.ID.String()) // ID is a UUID of constant length - l = len(template.Access) - if l > accessLen { - accessLen = l - } - } - - paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",nameLen, idLen, accessLen) - - for _, template := range templates { - fmt.Printf(paddedFmt, template.Name, template.ID, template.Access) - } - } - - return 0 + if len(args) < 1 { + c.PrintUsage() + return 1 + } + + // Helper function to remove an element from a string array at a given index + removeArgAtIndex := func (slice []string, index int) []string { + return append(slice[:index], slice[index+1:]...) + } + + // Check if a "-l" argument is specified + foundLongOutputFlag := -1 + for idx, arg := range args { + if arg == "-l" { + foundLongOutputFlag = idx + } + } + + if foundLongOutputFlag >= 0 { + removeArgAtIndex(args, foundLongOutputFlag) + } + + templates, err := getFilteredTemplates(route, args) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + if foundLongOutputFlag >= 0 { + for _, template := range templates { + u.Println(template.String()) + } + } else { + // Compute the length of the longest name and access (used for padding columns) + nameLen := 0 + idLen := 0 + accessLen := 0 + for _, template := range templates { + l := len(template.Name) + if l > nameLen { + nameLen = l + } + idLen = len(template.ID.String()) // ID is a UUID of constant length + l = len(template.Access) + if l > accessLen { + accessLen = l + } + } + + paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",nameLen, idLen, accessLen) + + for _, template := range templates { + fmt.Printf(paddedFmt, template.Name, template.ID, template.Access) + } + } + + return 0 } @@ -123,73 +123,73 @@ func printFilteredTemplates(c cmd.Command, args []string, route string) int { // "." -> matches everything // "bla" -> matches any template name containing "bla" func getFilteredTemplates(route string, patterns []string) ([]Template, error) { - if len(patterns) < 1 { - return nil, errors.New("At least one ID or regex must be specified") - } - - client := g.GetInstance().Client - host := g.GetInstance().Host - - var ids []string - var regexes []string - - for _, pattern := range patterns { - _, err := uuid.Parse(pattern) - if err != nil { - regexes = append(regexes, pattern) - } else { - ids = append(ids, pattern) - } - } - - resp, err := client.R().Get(host+route) - if err != nil { - return nil, err - } - - templatesList := []Template{} - - if resp.IsSuccess() { - templates, err := getTemplates(resp) - if err != nil { - return nil, err - } - - for _, template := range templates { - found := false - for _, id := range ids { - if id == template.ID.String() { - templatesList = append(templatesList, template) - found = true - break - } - } - if found { - continue - } - for _, regex := range regexes { - match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(template.Name)) - if err != nil { - return nil, errors.New("Error matching \""+regex+"\": "+err.Error()) - } else { - if match { - templatesList = append(templatesList, template) - break - } - } - } - } - return templatesList, nil - } else { - return nil, errors.New("Error: "+resp.Status()+": "+resp.String()) - } + if len(patterns) < 1 { + return nil, errors.New("At least one ID or regex must be specified") + } + + client := g.GetInstance().Client + host := g.GetInstance().Host + + var ids []string + var regexes []string + + for _, pattern := range patterns { + _, err := uuid.Parse(pattern) + if err != nil { + regexes = append(regexes, pattern) + } else { + ids = append(ids, pattern) + } + } + + resp, err := client.R().Get(host+route) + if err != nil { + return nil, err + } + + templatesList := []Template{} + + if resp.IsSuccess() { + templates, err := getTemplates(resp) + if err != nil { + return nil, err + } + + for _, template := range templates { + found := false + for _, id := range ids { + if id == template.ID.String() { + templatesList = append(templatesList, template) + found = true + break + } + } + if found { + continue + } + for _, regex := range regexes { + match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(template.Name)) + if err != nil { + return nil, errors.New("Error matching \""+regex+"\": "+err.Error()) + } else { + if match { + templatesList = append(templatesList, template) + break + } + } + } + } + return templatesList, nil + } else { + 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 { - return nil, err - } - return templates, nil + templates := []Template{} + if err := json.Unmarshal(resp.Body(), &templates); err != nil { + return nil, err + } + return templates, nil } \ No newline at end of file diff --git a/src/cmdTemplate/template.go b/src/cmdTemplate/template.go index ff2d7b62e0b3404fbc7a7aed1d20c73f44a4f699..ae5611811a981cfe509251ffe7b9e4588ae0baac 100644 --- a/src/cmdTemplate/template.go +++ b/src/cmdTemplate/template.go @@ -1,17 +1,17 @@ package cmdTemplate import ( - "time" - "github.com/google/uuid" + "time" + "github.com/google/uuid" ) // Make sure these types MATCH their counterparts in nexus-server codebase! type ( - Template struct { - ID uuid.UUID `json:"id" validate:"required"` - Name string `json:"name" validate:"required,min=2,max=256"` - Owner string `json:"owner" validate:"required,email"` - Access string `json:"access" validate:"required,min=4,max=16"` // private or public - CreationTime time.Time `json:"creationTime" validate:"required"` - } + Template struct { + ID uuid.UUID `json:"id" validate:"required"` + Name string `json:"name" validate:"required,min=2,max=256"` + Owner string `json:"owner" validate:"required,email"` + Access string `json:"access" validate:"required,min=4,max=16"` // private or public + CreationTime time.Time `json:"creationTime" validate:"required"` + } ) diff --git a/src/cmdTemplate/templateCreate.go b/src/cmdTemplate/templateCreate.go index cb187614e850e50fd5d2be86945b42fef62a0200..7eea3025870504dfbdf93cd7da0ec1b0b7019ae4 100644 --- a/src/cmdTemplate/templateCreate.go +++ b/src/cmdTemplate/templateCreate.go @@ -1,9 +1,9 @@ package cmdTemplate import ( - u "nexus-client/utils" - g "nexus-client/globals" - "github.com/go-resty/resty/v2" + u "nexus-client/utils" + g "nexus-client/globals" + "github.com/go-resty/resty/v2" ) type Create struct { @@ -11,68 +11,68 @@ type Create struct { } func (cmd *Create)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Create)GetDesc() []string { - return []string{ - "Creates a template from an existing VM.", - "Requires TPL_CREATE user capability."} + return []string{ + "Creates a template from an existing VM.", + "Requires TPL_CREATE user capability."} } func (cmd *Create)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" ID name access") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("ID ID of the VM used to create the template.") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" ID name access") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("ID ID of the VM used to create the template.") u.PrintlnErr("name Name of the template to create.") - u.PrintlnErr("access Access type, either \"public\" or \"private\"") + u.PrintlnErr("access Access type, either \"public\" or \"private\"") } func (cmd *Create)Run(args []string) int { - argc := len(args) - if argc != 3 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc != 3 { + cmd.PrintUsage() + return 1 + } - id := args[0] - name := args[1] - access := args[2] + id := args[0] + name := args[1] + access := args[2] - resp, err := cmd.makeRequestForID(id, name, access) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - if resp.IsSuccess() { - u.Println(resp) - return 0 - } else { - u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) - return 1 - } + resp, err := cmd.makeRequestForID(id, name, access) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + if resp.IsSuccess() { + u.Println(resp) + return 0 + } else { + u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) + return 1 + } } func (cmd *Create)makeRequestForID(vmID, name, access string) (*resty.Response, error) { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host + + type TemplateArgs struct { + VMID string + Name string + Access string + } + + templateArgs := &TemplateArgs { vmID, name, access } - type TemplateArgs struct { - VMID string - Name string - Access string - } - - templateArgs := &TemplateArgs { vmID, name, access } - - resp, err := client.R().SetBody(templateArgs).Post(host+"/templates/vm") - if err != nil { - return nil, err - } + resp, err := client.R().SetBody(templateArgs).Post(host+"/templates/vm") + if err != nil { + return nil, err + } - return resp, nil + return resp, nil } diff --git a/src/cmdTemplate/templateDel.go b/src/cmdTemplate/templateDel.go index e1e23fd3f4d8ef3fae3e3829acb399e89a09e3a3..b92bf39c22e592cac3c7cac29c4ff59d113d2e7c 100644 --- a/src/cmdTemplate/templateDel.go +++ b/src/cmdTemplate/templateDel.go @@ -1,8 +1,8 @@ package cmdTemplate import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Del struct { @@ -10,50 +10,50 @@ type Del struct { } func (cmd *Del)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Del)GetDesc() []string { - return []string{ - "Deletes a template.", - "Requires TPL_DESTROY or TPL_DESTROY_ANY user capability."} + return []string{ + "Deletes a template.", + "Requires TPL_DESTROY or TPL_DESTROY_ANY user capability."} } func (cmd *Del)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName()," ID") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("ID ID of the template to delete.") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName()," ID") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("ID ID of the template to delete.") } func (cmd *Del)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if argc != 1 { - cmd.PrintUsage() - return 1 - } - - tplID := args[0] - statusCode := 0 - - resp, err := client.R().Delete(host+"/templates/"+tplID) - if err != nil { - u.PrintlnErr("Failed deleting template \""+tplID+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Deleted template \""+tplID+"\"") - } else { - u.PrintlnErr("Failed deleting template \""+tplID+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } - - return statusCode + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc != 1 { + cmd.PrintUsage() + return 1 + } + + tplID := args[0] + statusCode := 0 + + resp, err := client.R().Delete(host+"/templates/"+tplID) + if err != nil { + u.PrintlnErr("Failed deleting template \""+tplID+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Deleted template \""+tplID+"\"") + } else { + u.PrintlnErr("Failed deleting template \""+tplID+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } + + return statusCode } diff --git a/src/cmdTemplate/templateEdit.go b/src/cmdTemplate/templateEdit.go index 9f02ee52d7b5b9cd7f417af501180833bab39f0d..ddb58b7ef74142c9cbd6b63f144a6e6864c25827 100644 --- a/src/cmdTemplate/templateEdit.go +++ b/src/cmdTemplate/templateEdit.go @@ -1,10 +1,10 @@ package cmdTemplate import ( - "errors" - "strings" - u "nexus-client/utils" - g "nexus-client/globals" + "errors" + "strings" + u "nexus-client/utils" + g "nexus-client/globals" ) type Edit struct { @@ -12,104 +12,104 @@ type Edit struct { } type templateEditParameters struct { - Name string - Access string + Name string + Access string } func (cmd *Edit)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Edit)GetDesc() []string { - return []string{ - "Edits one or more template's properties: name, access.", - "Requires TPL_EDIT or TPL_EDIT_ANY user capability."} + return []string{ + "Edits one or more template's properties: name, access.", + "Requires TPL_EDIT or TPL_EDIT_ANY user capability."} } func (cmd *Edit)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" ID [name=\"new name\"] [access=private/public]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("ID ID of the template to edit.") - const usage string = `Parameters that can be changed (at least one must be specified): + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" ID [name=\"new name\"] [access=private/public]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("ID ID of the template to edit.") + const usage string = `Parameters that can be changed (at least one must be specified): name Name of the template. access Access type, either "private" or "public".` } func (cmd *Edit)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if argc < 2 { - cmd.PrintUsage() - return 1 - } - - templateParams, err := cmd.parseArgs(args[1:]) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - if templateParams == nil { - cmd.PrintUsage() - return 1 - } - - tplID := args[0] - statusCode := 0 - - resp, err := client.R().SetBody(templateParams).Put(host+"/templates/"+tplID) - if err != nil { - u.PrintlnErr("Failed editing template \""+tplID+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Successfully edited template \""+tplID+"\"") - } else { - u.PrintlnErr("Failed editing template \""+tplID+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } - - return statusCode + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc < 2 { + cmd.PrintUsage() + return 1 + } + + templateParams, err := cmd.parseArgs(args[1:]) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + if templateParams == nil { + cmd.PrintUsage() + return 1 + } + + tplID := args[0] + statusCode := 0 + + resp, err := client.R().SetBody(templateParams).Put(host+"/templates/"+tplID) + if err != nil { + u.PrintlnErr("Failed editing template \""+tplID+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Successfully edited template \""+tplID+"\"") + } else { + u.PrintlnErr("Failed editing template \""+tplID+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } + + return statusCode } func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { - templateParams := &templateEditParameters {} - atLeastOneArg := false - - getStringVal := func(s string, prefix string) string { - if strings.HasPrefix(s, prefix) { - parts := strings.Split(s, prefix) - return parts[1] - } - return "" - } - - for _, arg := range args { - s := getStringVal(arg, "name=") - if s != "" { - templateParams.Name = s - atLeastOneArg = true - continue - } - s = getStringVal(arg, "access=") - if s != "" { - templateParams.Access = s - atLeastOneArg = true - continue - } - return nil, errors.New("Invalid argument: "+arg) - } - - if atLeastOneArg { - return templateParams, nil - } else { - return nil, nil - } + templateParams := &templateEditParameters {} + atLeastOneArg := false + + getStringVal := func(s string, prefix string) string { + if strings.HasPrefix(s, prefix) { + parts := strings.Split(s, prefix) + return parts[1] + } + return "" + } + + for _, arg := range args { + s := getStringVal(arg, "name=") + if s != "" { + templateParams.Name = s + atLeastOneArg = true + continue + } + s = getStringVal(arg, "access=") + if s != "" { + templateParams.Access = s + atLeastOneArg = true + continue + } + return nil, errors.New("Invalid argument: "+arg) + } + + if atLeastOneArg { + return templateParams, nil + } else { + return nil, nil + } } diff --git a/src/cmdTemplate/templateExportDisk.go b/src/cmdTemplate/templateExportDisk.go index 885aae92d55fb9aba7b859107e0fa3f19f0d4044..42e79e81066c10e88745a9293f9a7c5e53404399 100644 --- a/src/cmdTemplate/templateExportDisk.go +++ b/src/cmdTemplate/templateExportDisk.go @@ -1,8 +1,8 @@ package cmdTemplate import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type ExportDisk struct { @@ -12,54 +12,54 @@ type ExportDisk struct { const outputFile = "disk.qcow" func (cmd *ExportDisk)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *ExportDisk)GetDesc() []string { - return []string{ - "Exports a template's disk.", - "Requires TPL_READFS or TPL_READFS_ANY user capability."} + return []string{ + "Exports a template's disk.", + "Requires TPL_READFS or TPL_READFS_ANY user capability."} } func (cmd *ExportDisk)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName()," ID file") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("ID ID of the template for which the disk must be exported.") - u.PrintlnErr("file Name of the file to save the disk into.") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName()," ID file") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("ID ID of the template for which the disk must be exported.") + u.PrintlnErr("file Name of the file to save the disk into.") } func (cmd *ExportDisk)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc != 2 { + cmd.PrintUsage() + return 1 + } - argc := len(args) - if argc != 2 { - cmd.PrintUsage() - return 1 - } - - tplID := args[0] - outputFile := args[1] - statusCode := 0 + tplID := args[0] + outputFile := args[1] + statusCode := 0 - u.Println("Exporting disk from template \""+tplID+"\" into "+outputFile+" ...") + u.Println("Exporting disk from template \""+tplID+"\" into "+outputFile+" ...") - resp, err := client.R().SetOutput(outputFile).Get(host+"/templates/"+tplID+"/disk") - if err != nil { - u.PrintlnErr("Failed: "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Successfully exported disk into "+outputFile) - } else { - u.PrintlnErr("Failed: "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } + resp, err := client.R().SetOutput(outputFile).Get(host+"/templates/"+tplID+"/disk") + if err != nil { + u.PrintlnErr("Failed: "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Successfully exported disk into "+outputFile) + } else { + u.PrintlnErr("Failed: "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } - return statusCode + return statusCode } diff --git a/src/cmdTemplate/templateList.go b/src/cmdTemplate/templateList.go index 3bf469860ff9cb5f856bffdfb748dce5658e9c5b..d39c8bcf3bc46606622d763d804b20e1ab9c4285 100644 --- a/src/cmdTemplate/templateList.go +++ b/src/cmdTemplate/templateList.go @@ -1,7 +1,7 @@ package cmdTemplate import ( - u "nexus-client/utils" + u "nexus-client/utils" ) type List struct { @@ -9,26 +9,26 @@ type List struct { } func (cmd *List)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *List)GetDesc() []string { - return []string{ - "Lists templates.", - "Requires TPL_LIST or TPL_LIST_ANY user capability."} + return []string{ + "Lists templates.", + "Requires TPL_LIST or TPL_LIST_ANY user capability."} } func (cmd *List)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName(), " [-l] [ID ...] [regex ...]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("Use \"-l\" to specify detailed templates output.") - printRegexUsageDetails() + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName(), " [-l] [ID ...] [regex ...]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("Use \"-l\" to specify detailed templates output.") + printRegexUsageDetails() } func (cmd *List)Run(args []string) int { - return printFilteredTemplates(cmd, args, "/templates") + return printFilteredTemplates(cmd, args, "/templates") } diff --git a/src/cmdToken/tokenRefresh.go b/src/cmdToken/tokenRefresh.go index 9ce1b30057192b7b66d46be5a20efb2b4ee22508..9b5078c303d50156066772a55b1b3a1b33d98761 100644 --- a/src/cmdToken/tokenRefresh.go +++ b/src/cmdToken/tokenRefresh.go @@ -1,70 +1,70 @@ package cmdToken import ( - "errors" - "encoding/json" - u "nexus-client/utils" - g "nexus-client/globals" + "errors" + "encoding/json" + u "nexus-client/utils" + g "nexus-client/globals" ) type Refresh struct { - Name string + Name string } func (cmd *Refresh)GetName() string { - return cmd.Name + return cmd.Name } func (cmd *Refresh)GetDesc() []string { - return []string{ - "Obtains a new access token."} + return []string{ + "Obtains a new access token."} } func (cmd *Refresh)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.Name) - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.Name) + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func (cmd *Refresh)Run(args []string) int { - if len(args) > 0 { - cmd.PrintUsage() - return 1 - } + if len(args) > 0 { + cmd.PrintUsage() + return 1 + } - token, err := GetToken() - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } + token, err := GetToken() + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } - u.Println(token) - return 0 + u.Println(token) + return 0 } func GetToken() (string, error) { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - resp, err := client.R().Get(host+"/token/refresh") - if err != nil { - return "", err - } else { - if resp.IsSuccess() { - type Response struct { - Token string - } - var response Response - err = json.Unmarshal(resp.Body(), &response) - if err != nil { - return "", err - } - return response.Token, nil - } else { - return "", errors.New(resp.Status()+": "+resp.String()) - } - } + resp, err := client.R().Get(host+"/token/refresh") + if err != nil { + return "", err + } else { + if resp.IsSuccess() { + type Response struct { + Token string + } + var response Response + err = json.Unmarshal(resp.Body(), &response) + if err != nil { + return "", err + } + return response.Token, nil + } else { + return "", errors.New(resp.Status()+": "+resp.String()) + } + } } diff --git a/src/cmdUser/helper.go b/src/cmdUser/helper.go index b917c2c4a995c29c1290b973a0735cd696849346..c0f3f40f80ec1af34be3dea3046f8d7988fc6c7e 100644 --- a/src/cmdUser/helper.go +++ b/src/cmdUser/helper.go @@ -1,43 +1,43 @@ package cmdUser import ( - "fmt" - "regexp" - "strings" - "encoding/json" - "nexus-client/cmd" - u "nexus-client/utils" - g "nexus-client/globals" - "github.com/go-resty/resty/v2" + "fmt" + "regexp" + "strings" + "encoding/json" + "nexus-client/cmd" + u "nexus-client/utils" + g "nexus-client/globals" + "github.com/go-resty/resty/v2" ) // Converts a User structure into a pretty string. func (user *UserWithoutPwd) String() string { - output, err := json.MarshalIndent(user, "", " ") - if err != nil { - return err.Error() - } + output, err := json.MarshalIndent(user, "", " ") + if err != nil { + return err.Error() + } - return string(output) + return string(output) } func printRegexUsage(c cmd.Command) { - for _, desc := range c.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ", c.GetName(), " [regex ...]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range c.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ", c.GetName(), " [regex ...]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func printRegexUsageDetails() { - const usage string = `Only users matching the specified regexes will be listed. + const usage string = `Only users matching the specified regexes will be listed. Any number of regexes can be specified. The regex matches the user email, first name and last name and is case-insensitive. Regex examples: "." -> matches any users "bla" -> matches any users containing "bla"` - u.PrintlnErr(usage) + u.PrintlnErr(usage) } // Prints a list of filtered users for a given route. @@ -48,103 +48,103 @@ Regex examples: // "." -> matches everything // "bla" -> matches any user containing "bla" func printFilteredUsers(c cmd.Command, args []string, route string) int { - if len(args) < 1 { - c.PrintUsage() - return 1 - } - - // Helper function to remove an element from a string array at a given index - removeArgAtIndex := func(slice []string, index int) []string { - return append(slice[:index], slice[index+1:]...) - } - - // Check if a "-l" argument is specified - foundLongOutputFlag := -1 - for idx, arg := range args { - if arg == "-l" { - foundLongOutputFlag = idx - } - } - - if foundLongOutputFlag >= 0 { - removeArgAtIndex(args, foundLongOutputFlag) - } - - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().Get(host + route) - if err != nil { - u.PrintlnErr("Error: " + err.Error()) - return 1 - } - - if resp.IsSuccess() { - users, err := getUsers(resp) - if err != nil { - u.PrintlnErr("Error: " + err.Error()) - return 1 - } - - userList := []UserWithoutPwd{} - - for _, user := range users { - for _, regex := range args { - fieldsToMatch := user.Email + " " + user.FirstName + " " + user.LastName - match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(fieldsToMatch)) - if err != nil { - u.PrintlnErr("Error matching \"" + regex + "\": " + err.Error()) - } else { - if match { - userList = append(userList, user) - break - } - } - } - } - - if foundLongOutputFlag >= 0 { - for _, user := range userList { - u.Println(user.String()) - } - } else { - // Compute the length of the longest LastName, FirstName and Email (used for padding columns) - lastNameLen := 0 - firstNameLen := 0 - emailLen := 0 - for _, user := range userList { - l := len(user.LastName) - if l > lastNameLen { - lastNameLen = l - } - l = len(user.FirstName) - if l > firstNameLen { - firstNameLen = l - } - l = len(user.Email) - if l > emailLen { - emailLen = l - } - } - - paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",lastNameLen, firstNameLen, emailLen) - - for _, user := range userList { - fmt.Printf(paddedFmt, user.LastName, user.FirstName, user.Email) - } - } - - return 0 - } else { - u.PrintlnErr("Error: " + resp.Status() + ": " + resp.String()) - return 1 - } + if len(args) < 1 { + c.PrintUsage() + return 1 + } + + // Helper function to remove an element from a string array at a given index + removeArgAtIndex := func(slice []string, index int) []string { + return append(slice[:index], slice[index+1:]...) + } + + // Check if a "-l" argument is specified + foundLongOutputFlag := -1 + for idx, arg := range args { + if arg == "-l" { + foundLongOutputFlag = idx + } + } + + if foundLongOutputFlag >= 0 { + removeArgAtIndex(args, foundLongOutputFlag) + } + + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().Get(host + route) + if err != nil { + u.PrintlnErr("Error: " + err.Error()) + return 1 + } + + if resp.IsSuccess() { + users, err := getUsers(resp) + if err != nil { + u.PrintlnErr("Error: " + err.Error()) + return 1 + } + + userList := []UserWithoutPwd{} + + for _, user := range users { + for _, regex := range args { + fieldsToMatch := user.Email + " " + user.FirstName + " " + user.LastName + match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(fieldsToMatch)) + if err != nil { + u.PrintlnErr("Error matching \"" + regex + "\": " + err.Error()) + } else { + if match { + userList = append(userList, user) + break + } + } + } + } + + if foundLongOutputFlag >= 0 { + for _, user := range userList { + u.Println(user.String()) + } + } else { + // Compute the length of the longest LastName, FirstName and Email (used for padding columns) + lastNameLen := 0 + firstNameLen := 0 + emailLen := 0 + for _, user := range userList { + l := len(user.LastName) + if l > lastNameLen { + lastNameLen = l + } + l = len(user.FirstName) + if l > firstNameLen { + firstNameLen = l + } + l = len(user.Email) + if l > emailLen { + emailLen = l + } + } + + paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",lastNameLen, firstNameLen, emailLen) + + for _, user := range userList { + fmt.Printf(paddedFmt, user.LastName, user.FirstName, user.Email) + } + } + + return 0 + } else { + u.PrintlnErr("Error: " + resp.Status() + ": " + resp.String()) + return 1 + } } func getUsers(resp *resty.Response) ([]UserWithoutPwd, error) { - users := []UserWithoutPwd{} - if err := json.Unmarshal(resp.Body(), &users); err != nil { - return nil, err - } - return users, nil + users := []UserWithoutPwd{} + if err := json.Unmarshal(resp.Body(), &users); err != nil { + return nil, err + } + return users, nil } diff --git a/src/cmdUser/user.go b/src/cmdUser/user.go index 757f945fc0f63c34f34ca8325e50a2c29b589eb5..aa6fd28a970affcd314a036fc752f993dd8a55dc 100644 --- a/src/cmdUser/user.go +++ b/src/cmdUser/user.go @@ -2,12 +2,12 @@ package cmdUser // Make sure these types MATCH their counterparts in nexus-server codebase! type ( - Capabilities map[string]int + Capabilities map[string]int - UserWithoutPwd struct { - Email string `json:"email" validate:"required,email"` - FirstName string `json:"firstname" validate:"required,min=2,max=32"` - LastName string `json:"lastname" validate:"required,min=2,max=32"` - Caps Capabilities `json:"caps" validate:"required"` - } + UserWithoutPwd struct { + Email string `json:"email" validate:"required,email"` + FirstName string `json:"firstname" validate:"required,min=2,max=32"` + LastName string `json:"lastname" validate:"required,min=2,max=32"` + Caps Capabilities `json:"caps" validate:"required"` + } ) \ No newline at end of file diff --git a/src/cmdUser/userAdd.go b/src/cmdUser/userAdd.go index 73391edf8f31aeb6e8bf3abb6e29e8ca3147096a..ecadbeaf9d66413f62646349fca75339ad09f48e 100644 --- a/src/cmdUser/userAdd.go +++ b/src/cmdUser/userAdd.go @@ -1,13 +1,13 @@ package cmdUser import ( - "io" - "os" - "errors" - "strings" - "encoding/csv" - u "nexus-client/utils" - g "nexus-client/globals" + "io" + "os" + "errors" + "strings" + "encoding/csv" + u "nexus-client/utils" + g "nexus-client/globals" ) type Add struct { @@ -15,143 +15,143 @@ type Add struct { } func (cmd *Add)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Add)GetDesc() []string { - return []string{ - "Creates a user.", - "Requires USER_CREATE user capability."} + return []string{ + "Creates a user.", + "Requires USER_CREATE user capability."} } func (cmd *Add)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" email firstname lastname password [capability ...]") - u.PrintlnErr(" "+cmd.GetName()+" file.csv") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `file.csv 5-column CSV file defining each new user: + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" email firstname lastname password [capability ...]") + u.PrintlnErr(" "+cmd.GetName()+" file.csv") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `file.csv 5-column CSV file defining each new user: column1 email column2 firstname column3 lastname column4 password column5 capabilities (space separated)` - u.PrintlnErr(usage) + u.PrintlnErr(usage) } type cmdArgs struct { - Email string - FirstName string - LastName string - Pwd string - Caps map[string]int + Email string + FirstName string + LastName string + Pwd string + Caps map[string]int } func (cmd *Add)Run(args []string) int { - statusCode := 0 - - argc := len(args) - if argc >= 4 { - // Detected syntax: email firstname lastname pwd [caps] - - cargs := &cmdArgs { - Email: args[0], - FirstName: args[1], - LastName: args[2], - Pwd: args[3], - Caps: make(map[string]int), - } - if argc > 4 { - for _, cap := range args[4:] { - cargs.Caps[cap] = 1 - } - } - - if err := cmd.runRequest(cargs); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } - } else if argc == 1 { - // Detected syntax: file.csv - - csvFile := args[0] - file, err := os.Open(csvFile) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - defer file.Close() - reader := csv.NewReader(file) - line := 0 - for { - line += 1 - columns, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - u.PrintlnErr("FAILED reading "+err.Error()) - continue - } - - columnCount := len(columns) - if columnCount < 5 { - u.PrintlnErr("FAILED reading record on line ",line,": expecting 5 columns") - statusCode = 1 - continue - } - - email := columns[0] - if !u.IsEmail(email) { - u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") - statusCode = 1 - continue - } - - cargs := &cmdArgs { - Email: email, - FirstName: columns[1], - LastName: columns[2], - Pwd: columns[3], - Caps: make(map[string]int), - } - capsStr := strings.TrimSpace(columns[4]) - if len(capsStr) > 0 { - caps := strings.Split(capsStr, " ") - for _, cap := range caps { - cargs.Caps[cap] = 1 - } - } - - if err := cmd.runRequest(cargs); err != nil { - u.PrintlnErr("FAILED creating user "+cargs.Email+": "+err.Error()) - statusCode = 1 - } else { - u.Println("Successfully created user "+cargs.Email) - } - } - } else { - cmd.PrintUsage() - return 1 - } - - return statusCode + statusCode := 0 + + argc := len(args) + if argc >= 4 { + // Detected syntax: email firstname lastname pwd [caps] + + cargs := &cmdArgs { + Email: args[0], + FirstName: args[1], + LastName: args[2], + Pwd: args[3], + Caps: make(map[string]int), + } + if argc > 4 { + for _, cap := range args[4:] { + cargs.Caps[cap] = 1 + } + } + + if err := cmd.runRequest(cargs); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } + } else if argc == 1 { + // Detected syntax: file.csv + + csvFile := args[0] + file, err := os.Open(csvFile) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + defer file.Close() + reader := csv.NewReader(file) + line := 0 + for { + line += 1 + columns, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + u.PrintlnErr("FAILED reading "+err.Error()) + continue + } + + columnCount := len(columns) + if columnCount < 5 { + u.PrintlnErr("FAILED reading record on line ",line,": expecting 5 columns") + statusCode = 1 + continue + } + + email := columns[0] + if !u.IsEmail(email) { + u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") + statusCode = 1 + continue + } + + cargs := &cmdArgs { + Email: email, + FirstName: columns[1], + LastName: columns[2], + Pwd: columns[3], + Caps: make(map[string]int), + } + capsStr := strings.TrimSpace(columns[4]) + if len(capsStr) > 0 { + caps := strings.Split(capsStr, " ") + for _, cap := range caps { + cargs.Caps[cap] = 1 + } + } + + if err := cmd.runRequest(cargs); err != nil { + u.PrintlnErr("FAILED creating user "+cargs.Email+": "+err.Error()) + statusCode = 1 + } else { + u.Println("Successfully created user "+cargs.Email) + } + } + } else { + cmd.PrintUsage() + return 1 + } + + return statusCode } func (cmd *Add)runRequest(args *cmdArgs) error { - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().SetBody(args).Post(host+"/users") - if err != nil { - return errors.New("Error: "+err.Error()) - } - - if resp.IsSuccess() { - return nil - } else { - return errors.New("Error: "+resp.Status()+": "+resp.String()) - } + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().SetBody(args).Post(host+"/users") + if err != nil { + return errors.New("Error: "+err.Error()) + } + + if resp.IsSuccess() { + return nil + } else { + return errors.New("Error: "+resp.Status()+": "+resp.String()) + } } diff --git a/src/cmdUser/userDel.go b/src/cmdUser/userDel.go index 3e12145d93c873943020916ebf46c988e8f3bc46..8667cda243777220815eb0da8ed0482aaec3d89e 100644 --- a/src/cmdUser/userDel.go +++ b/src/cmdUser/userDel.go @@ -1,12 +1,12 @@ package cmdUser import ( - "io" - "os" - "errors" - "encoding/csv" - u "nexus-client/utils" - g "nexus-client/globals" + "io" + "os" + "errors" + "encoding/csv" + u "nexus-client/utils" + g "nexus-client/globals" ) type Del struct { @@ -14,116 +14,116 @@ type Del struct { } func (cmd *Del)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Del)GetDesc() []string { - return []string{ - "Deletes one or more users.", - "Requires USER_DESTROY user capability."} + return []string{ + "Deletes one or more users.", + "Requires USER_DESTROY user capability."} } func (cmd *Del)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" email [email ...]") - u.PrintlnErr(" "+cmd.GetName()+" file.csv") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("file.csv 1-column CSV file containing emails of users to delete.") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" email [email ...]") + u.PrintlnErr(" "+cmd.GetName()+" file.csv") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("file.csv 1-column CSV file containing emails of users to delete.") } func (cmd *Del)Run(args []string) int { - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } - - statusCode := 0 - - if argc == 1 && !u.IsEmail(args[0]) { - // Single argument and it's a CSV file - - csvFile := args[0] - - if u.IsEmail(csvFile) { - cmd.PrintUsage() - return 1 - } - - file, err := os.Open(csvFile) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - defer file.Close() - reader := csv.NewReader(file) - line := 0 - for { - line += 1 - columns, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - u.PrintlnErr("FAILED reading "+err.Error()) - statusCode = 1 - continue - } - - columnCount := len(columns) - if columnCount != 1 { - u.PrintlnErr("FAILED reading record on line ",line,": expecting 1 column") - statusCode = 1 - continue - } - - email := columns[0] - if !u.IsEmail(email) { - u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") - statusCode = 1 - continue - } - - if err := cmd.runRequest(email); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully deleted user "+email) - } - } - } else { - // Argument(s) is/are email addresses - - // Iterates through each user (email) to delete - for i:= range(args) { - email := args[i] - if err := cmd.runRequest(email); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully deleted user "+email) - } - } - } - - return statusCode + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } + + statusCode := 0 + + if argc == 1 && !u.IsEmail(args[0]) { + // Single argument and it's a CSV file + + csvFile := args[0] + + if u.IsEmail(csvFile) { + cmd.PrintUsage() + return 1 + } + + file, err := os.Open(csvFile) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + defer file.Close() + reader := csv.NewReader(file) + line := 0 + for { + line += 1 + columns, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + u.PrintlnErr("FAILED reading "+err.Error()) + statusCode = 1 + continue + } + + columnCount := len(columns) + if columnCount != 1 { + u.PrintlnErr("FAILED reading record on line ",line,": expecting 1 column") + statusCode = 1 + continue + } + + email := columns[0] + if !u.IsEmail(email) { + u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") + statusCode = 1 + continue + } + + if err := cmd.runRequest(email); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully deleted user "+email) + } + } + } else { + // Argument(s) is/are email addresses + + // Iterates through each user (email) to delete + for i:= range(args) { + email := args[i] + if err := cmd.runRequest(email); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully deleted user "+email) + } + } + } + + return statusCode } func (cmd *Del)runRequest(email string) error { - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().Delete(host+"/users/"+email) - if err != nil { - return errors.New("Error: "+err.Error()) - } - - if resp.IsSuccess() { - return nil - } else { - return errors.New("Error: "+resp.Status()+": "+resp.String()) - } + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().Delete(host+"/users/"+email) + if err != nil { + return errors.New("Error: "+err.Error()) + } + + if resp.IsSuccess() { + return nil + } else { + return errors.New("Error: "+resp.Status()+": "+resp.String()) + } } diff --git a/src/cmdUser/userList.go b/src/cmdUser/userList.go index 610fbfa6622e29048c5e43cdc37b154a8d526634..926e4b7f14bdfeec0789ee632e53170042c84d46 100644 --- a/src/cmdUser/userList.go +++ b/src/cmdUser/userList.go @@ -1,7 +1,7 @@ package cmdUser import ( - u "nexus-client/utils" + u "nexus-client/utils" ) type List struct { @@ -9,26 +9,26 @@ type List struct { } func (cmd *List)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *List)GetDesc() []string { - return []string{ - "Lists users.", - "Requires USER_LIST user capability."} + return []string{ + "Lists users.", + "Requires USER_LIST user capability."} } func (cmd *List)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName(), " [-l] [regex ...]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("Use \"-l\" to specify detailed user output.") - printRegexUsageDetails() + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName(), " [-l] [regex ...]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("Use \"-l\" to specify detailed user output.") + printRegexUsageDetails() } func (cmd *List)Run(args []string) int { - return printFilteredUsers(cmd, args, "/users") + return printFilteredUsers(cmd, args, "/users") } diff --git a/src/cmdUser/userSetCaps.go b/src/cmdUser/userSetCaps.go index 92b654f893a356f0e1a16144b596f10d32ce655d..b27042097f52cc477134ddaab9a741906b965acf 100644 --- a/src/cmdUser/userSetCaps.go +++ b/src/cmdUser/userSetCaps.go @@ -1,13 +1,13 @@ package cmdUser import ( - "io" - "os" - "errors" - "strings" - "encoding/csv" - u "nexus-client/utils" - g "nexus-client/globals" + "io" + "os" + "errors" + "strings" + "encoding/csv" + u "nexus-client/utils" + g "nexus-client/globals" ) type SetCaps struct { @@ -15,136 +15,136 @@ type SetCaps struct { } func (cmd *SetCaps)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *SetCaps)GetDesc() []string { - return []string{ - "Sets a user's capabilities.", - "Requires USER_SET_CAPS user capability."} + return []string{ + "Sets a user's capabilities.", + "Requires USER_SET_CAPS user capability."} } func (cmd *SetCaps)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" email [capability ...]") - u.PrintlnErr(" "+cmd.GetName()+" file.csv") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `file.csv 2-column CSV file specifying each user's capabilities: + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" email [capability ...]") + u.PrintlnErr(" "+cmd.GetName()+" file.csv") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `file.csv 2-column CSV file specifying each user's capabilities: column1 email column2 capabilities (space separated)` - u.PrintlnErr(usage) + u.PrintlnErr(usage) } type capsArgs struct { - Caps map[string]int `json:"caps" validate:"required"` + Caps map[string]int `json:"caps" validate:"required"` } func (cmd *SetCaps)Run(args []string) int { - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } - - statusCode := 0 - - if argc == 1 && !u.IsEmail(args[0]) { - // Single argument and it's a CSV file - - csvFile := args[0] - - if u.IsEmail(csvFile) { - cmd.PrintUsage() - return 1 - } - - file, err := os.Open(csvFile) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - defer file.Close() - reader := csv.NewReader(file) - line := 0 - for { - line += 1 - columns, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - u.PrintlnErr("FAILED reading "+err.Error()) - statusCode = 1 - continue - } - - columnCount := len(columns) - if columnCount != 2 { - u.PrintlnErr("FAILED reading record on line ",line,": expecting 2 columns") - statusCode = 1 - continue - } - - userCaps := &capsArgs { make(map[string]int) } - - capsStr := strings.TrimSpace(columns[1]) - if len(capsStr) > 0 { - caps := strings.Split(capsStr, " ") - for _, cap := range caps { - userCaps.Caps[cap] = 1 - } - } - - email := columns[0] - if !u.IsEmail(email) { - u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") - statusCode = 1 - continue - } - - if err := cmd.runRequest(email, userCaps); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully set capabilities for "+email) - } - } - } else { - // Argument is an email address - - email := args[0] - - userCaps := &capsArgs { make(map[string]int) } - for _, cap := range args[1:] { - userCaps.Caps[cap] = 1 - } - - if err := cmd.runRequest(email, userCaps); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully set capabilities for "+email) - } - } - - return statusCode + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } + + statusCode := 0 + + if argc == 1 && !u.IsEmail(args[0]) { + // Single argument and it's a CSV file + + csvFile := args[0] + + if u.IsEmail(csvFile) { + cmd.PrintUsage() + return 1 + } + + file, err := os.Open(csvFile) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + defer file.Close() + reader := csv.NewReader(file) + line := 0 + for { + line += 1 + columns, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + u.PrintlnErr("FAILED reading "+err.Error()) + statusCode = 1 + continue + } + + columnCount := len(columns) + if columnCount != 2 { + u.PrintlnErr("FAILED reading record on line ",line,": expecting 2 columns") + statusCode = 1 + continue + } + + userCaps := &capsArgs { make(map[string]int) } + + capsStr := strings.TrimSpace(columns[1]) + if len(capsStr) > 0 { + caps := strings.Split(capsStr, " ") + for _, cap := range caps { + userCaps.Caps[cap] = 1 + } + } + + email := columns[0] + if !u.IsEmail(email) { + u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") + statusCode = 1 + continue + } + + if err := cmd.runRequest(email, userCaps); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully set capabilities for "+email) + } + } + } else { + // Argument is an email address + + email := args[0] + + userCaps := &capsArgs { make(map[string]int) } + for _, cap := range args[1:] { + userCaps.Caps[cap] = 1 + } + + if err := cmd.runRequest(email, userCaps); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully set capabilities for "+email) + } + } + + return statusCode } func (cmd *SetCaps)runRequest(email string, userCaps *capsArgs) error { - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().SetBody(userCaps).Put(host+"/users/"+email+"/caps") - if err != nil { - return errors.New("Error: "+err.Error()) - } - - if resp.IsSuccess() { - return nil - } else { - return errors.New("Error: "+resp.Status()+": "+resp.String()) - } + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().SetBody(userCaps).Put(host+"/users/"+email+"/caps") + if err != nil { + return errors.New("Error: "+err.Error()) + } + + if resp.IsSuccess() { + return nil + } else { + return errors.New("Error: "+resp.Status()+": "+resp.String()) + } } diff --git a/src/cmdUser/userUpdatePwd.go b/src/cmdUser/userUpdatePwd.go index 3c75d1882a763e9491c95a07b459b453589b138f..dc0695c50039ebea5623044bc1e3bbdd16bfcf76 100644 --- a/src/cmdUser/userUpdatePwd.go +++ b/src/cmdUser/userUpdatePwd.go @@ -1,13 +1,13 @@ package cmdUser import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) import ( - "bytes" - "golang.org/x/crypto/ssh/terminal" + "bytes" + "golang.org/x/crypto/ssh/terminal" ) type UpdatePwd struct { @@ -15,72 +15,72 @@ type UpdatePwd struct { } func (cmd *UpdatePwd)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *UpdatePwd)GetDesc() []string { - return []string{ - "Updates the current user's password."} + return []string{ + "Updates the current user's password."} } func (cmd *UpdatePwd)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()) - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()) + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func (cmd *UpdatePwd)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc != 0 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc != 0 { + cmd.PrintUsage() + return 1 + } - // Read the new password from stdin - u.Print("New password: ") - newPwd, err := terminal.ReadPassword(0) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - u.Print("\nRetype new password: ") - newPwd2, err := terminal.ReadPassword(0) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - u.Println("") + // Read the new password from stdin + u.Print("New password: ") + newPwd, err := terminal.ReadPassword(0) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + u.Print("\nRetype new password: ") + newPwd2, err := terminal.ReadPassword(0) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + u.Println("") - if !bytes.Equal(newPwd, newPwd2) { - u.PrintlnErr("Sorry, passwords do not match!") - u.PrintlnErr("Password unchanged.") - return 1 - } + if !bytes.Equal(newPwd, newPwd2) { + u.PrintlnErr("Sorry, passwords do not match!") + u.PrintlnErr("Password unchanged.") + return 1 + } - pwdStr := string(newPwd) + pwdStr := string(newPwd) - type PwdArgs struct { - Pwd string - } + type PwdArgs struct { + Pwd string + } - pwdArgs := &PwdArgs{pwdStr} - resp, err := client.R().SetBody(pwdArgs).Put(host+"/users/pwd") - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } else { - if resp.IsSuccess() { - u.Println(resp) - return 0 - } else { - u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) - return 1 - } - } + pwdArgs := &PwdArgs{pwdStr} + resp, err := client.R().SetBody(pwdArgs).Put(host+"/users/pwd") + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } else { + if resp.IsSuccess() { + u.Println(resp) + return 0 + } else { + u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) + return 1 + } + } } diff --git a/src/cmdUser/userWhoami.go b/src/cmdUser/userWhoami.go index 9be106226e083ac5d21270aece4d12c335028d15..91c0677a08e6dac070485166f8609e68de1051ca 100644 --- a/src/cmdUser/userWhoami.go +++ b/src/cmdUser/userWhoami.go @@ -1,8 +1,8 @@ package cmdUser import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Whoami struct { @@ -10,43 +10,43 @@ type Whoami struct { } func (cmd *Whoami)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Whoami)GetDesc() []string { - return []string{ - "Displays the current user's details."} + return []string{ + "Displays the current user's details."} } func (cmd *Whoami)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()) - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()) + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func (cmd *Whoami)Run(args []string) int { - if len(args) > 0 { - cmd.PrintUsage() - return 1 - } - - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().Get(host+"/users/whoami") - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } else { - if resp.IsSuccess() { - u.Println(resp) - return 0 - } else { - u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) - return 1 - } - } + if len(args) > 0 { + cmd.PrintUsage() + return 1 + } + + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().Get(host+"/users/whoami") + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } else { + if resp.IsSuccess() { + u.Println(resp) + return 0 + } else { + u.PrintlnErr("Error: "+resp.Status()+": "+resp.String()) + return 1 + } + } } diff --git a/src/cmdVM/helper.go b/src/cmdVM/helper.go index 14eb9fd9dfc5997ca86d819697987e743c1c8fbb..c6e734e316b0f79464bfbfe59ad12ce07db9f087 100644 --- a/src/cmdVM/helper.go +++ b/src/cmdVM/helper.go @@ -1,106 +1,106 @@ package cmdVM import ( - "fmt" - "regexp" - "errors" - "strings" - "encoding/json" - "nexus-client/cmd" - u "nexus-client/utils" - g "nexus-client/globals" - "github.com/google/uuid" - "github.com/go-resty/resty/v2" + "fmt" + "regexp" + "errors" + "strings" + "encoding/json" + "nexus-client/cmd" + u "nexus-client/utils" + g "nexus-client/globals" + "github.com/google/uuid" + "github.com/go-resty/resty/v2" ) // Converts a VM structure into a pretty string. func (vm *VMSerialized)String() string { - output, err := json.MarshalIndent(vm, "", " ") + output, err := json.MarshalIndent(vm, "", " ") if err != nil { return err.Error() } - return string(output) + return string(output) } func printRegexUsage(c cmd.Command) { - for _, desc := range c.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",c.GetName()," [ID ...] [regex ...]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range c.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",c.GetName()," [ID ...] [regex ...]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func printRegexUsageDetails() { - u.PrintlnErr("The action only applies to VMs matching the specified IDs or regexes.") - const usage string = `Any number of IDs or regexes can be specified. + u.PrintlnErr("The action only applies to VMs matching the specified IDs or regexes.") + const usage string = `Any number of IDs or regexes can be specified. The regex only matches the VM's name and is case-insensitive. Regex examples: "." -> matches any VMs "bla" -> matches any VMs containing "bla" in their name` - u.PrintlnErr(usage) + u.PrintlnErr(usage) } // Prints a list of filtered VMs for a given route. // Return 0 if everything went well or 1 in case of failure. func printFilteredVMs(c cmd.Command, args []string, route string) int { - if len(args) < 1 { - c.PrintUsage() - return 1 - } - - // Helper function to remove an element from a string array at a given index - removeArgAtIndex := func (slice []string, index int) []string { - return append(slice[:index], slice[index+1:]...) - } - - // Check if a "-l" argument is specified - foundLongOutputFlag := -1 - for idx, arg := range args { - if arg == "-l" { - foundLongOutputFlag = idx - } - } - - if foundLongOutputFlag >= 0 { - removeArgAtIndex(args, foundLongOutputFlag) - } - - vms, err := getFilteredVMs(route, args) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - - if foundLongOutputFlag >= 0 { - for _, vm := range vms { - u.Println(vm.String()) - } - } else { - // Compute the length of the longest name and state (used for padding columns) - nameLen := 0 - idLen := 0 - stateLen := 0 - for _, vm := range vms { - l := len(vm.Name) - if l > nameLen { - nameLen = l - } - idLen = len(vm.ID.String()) // ID is a UUID of constant length - l = len(vm.State) - if l > stateLen { - stateLen = l - } - } - - paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",nameLen, idLen, stateLen) - - for _, vm := range vms { - fmt.Printf(paddedFmt, vm.Name, vm.ID, vm.State) - } - } - - return 0 + if len(args) < 1 { + c.PrintUsage() + return 1 + } + + // Helper function to remove an element from a string array at a given index + removeArgAtIndex := func (slice []string, index int) []string { + return append(slice[:index], slice[index+1:]...) + } + + // Check if a "-l" argument is specified + foundLongOutputFlag := -1 + for idx, arg := range args { + if arg == "-l" { + foundLongOutputFlag = idx + } + } + + if foundLongOutputFlag >= 0 { + removeArgAtIndex(args, foundLongOutputFlag) + } + + vms, err := getFilteredVMs(route, args) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + if foundLongOutputFlag >= 0 { + for _, vm := range vms { + u.Println(vm.String()) + } + } else { + // Compute the length of the longest name and state (used for padding columns) + nameLen := 0 + idLen := 0 + stateLen := 0 + for _, vm := range vms { + l := len(vm.Name) + if l > nameLen { + nameLen = l + } + idLen = len(vm.ID.String()) // ID is a UUID of constant length + l = len(vm.State) + if l > stateLen { + stateLen = l + } + } + + paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",nameLen, idLen, stateLen) + + for _, vm := range vms { + fmt.Printf(paddedFmt, vm.Name, vm.ID, vm.State) + } + } + + return 0 } // Returns a list of filtered VMs for a given route. @@ -115,82 +115,82 @@ func printFilteredVMs(c cmd.Command, args []string, route string) int { // "." -> matches everything // "bla" -> matches any VM name containing "bla" func getFilteredVMs(route string, patterns []string) ([]VMSerialized, error) { - if len(patterns) < 1 { - return nil, errors.New("At least one ID or regex must be specified") - } - - client := g.GetInstance().Client - host := g.GetInstance().Host - - var ids []string - var regexes []string - - for _, pattern := range patterns { - _, err := uuid.Parse(pattern) - if err != nil { - regexes = append(regexes, pattern) - } else { - ids = append(ids, pattern) - } - } - - resp, err := client.R().Get(host+route) - if err != nil { - return nil, err - } - - vmsList := []VMSerialized{} - - if resp.IsSuccess() { - vms, err := getVMs(resp) - if err != nil { - return nil, err - } - - for _, vm := range vms { - found := false - for _, id := range ids { - if id == vm.ID.String() { - vmsList = append(vmsList, vm) - found = true - break - } - } - if found { - continue - } - for _, regex := range regexes { - match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(vm.Name)) - if err != nil { - return nil, errors.New("Error matching \""+regex+"\": "+err.Error()) - } else { - if match { - vmsList = append(vmsList, vm) - break - } - } - } - } - return vmsList, nil - } else { - return nil, errors.New("Error: "+resp.Status()+": "+resp.String()) - } + if len(patterns) < 1 { + return nil, errors.New("At least one ID or regex must be specified") + } + + client := g.GetInstance().Client + host := g.GetInstance().Host + + var ids []string + var regexes []string + + for _, pattern := range patterns { + _, err := uuid.Parse(pattern) + if err != nil { + regexes = append(regexes, pattern) + } else { + ids = append(ids, pattern) + } + } + + resp, err := client.R().Get(host+route) + if err != nil { + return nil, err + } + + vmsList := []VMSerialized{} + + if resp.IsSuccess() { + vms, err := getVMs(resp) + if err != nil { + return nil, err + } + + for _, vm := range vms { + found := false + for _, id := range ids { + if id == vm.ID.String() { + vmsList = append(vmsList, vm) + found = true + break + } + } + if found { + continue + } + for _, regex := range regexes { + match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(vm.Name)) + if err != nil { + return nil, errors.New("Error matching \""+regex+"\": "+err.Error()) + } else { + if match { + vmsList = append(vmsList, vm) + break + } + } + } + } + return vmsList, nil + } else { + return nil, errors.New("Error: "+resp.Status()+": "+resp.String()) + } } // Retrieves all VMs (no filtering). func getVMs(resp *resty.Response) ([]VMSerialized, error) { - vms := []VMSerialized{} - if err := json.Unmarshal(resp.Body(), &vms); err != nil { - return nil, err - } - return vms, nil + vms := []VMSerialized{} + if err := json.Unmarshal(resp.Body(), &vms); err != nil { + return nil, err + } + return vms, nil } // Retrieve a single VM. func getVM(resp *resty.Response) (*VMSerialized, error) { - var vm *VMSerialized = &VMSerialized{} - if err := json.Unmarshal(resp.Body(), vm); err != nil { - return vm, err - } - return vm, nil + var vm *VMSerialized = &VMSerialized{} + 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/cmdVM/vm.go b/src/cmdVM/vm.go index 7a898ab08721f67f7cf2f794c011218f2085453d..8557cbfa3881e2c76e38a18f7f20d2b28f0156cf 100644 --- a/src/cmdVM/vm.go +++ b/src/cmdVM/vm.go @@ -1,33 +1,33 @@ package cmdVM import ( - "github.com/google/uuid" + "github.com/google/uuid" ) // Make sure these types MATCH their counterparts in nexus-server codebase! type ( - VMSerialized struct { - ID uuid.UUID - Owner string - Name string - Cpus int - Ram int - Nic NicType - TemplateID uuid.UUID - Access map[string]Capabilities - State VMState - Port int - Pwd string - DiskBusy bool // If true the VM's disk is busy (cannot be modified or deleted) - } + VMSerialized struct { + ID uuid.UUID + Owner string + Name string + Cpus int + Ram int + Nic NicType + TemplateID uuid.UUID + Access map[string]Capabilities + State VMState + Port int + Pwd string + DiskBusy bool // If true the VM's disk is busy (cannot be modified or deleted) + } - Capabilities map[string]int + Capabilities map[string]int - VMState string - NicType string + VMState string + NicType string ) const ( - STOPPED VMState = "STOPPED" - RUNNING = "RUNNING" + STOPPED VMState = "STOPPED" + RUNNING = "RUNNING" ) \ No newline at end of file diff --git a/src/cmdVM/vmAddAccess.go b/src/cmdVM/vmAddAccess.go index ea788cdbc53cec987448fff1b04dab317f2f12e5..7dfefe79aa16d7666a22fcfbebf276f78d7a86a3 100644 --- a/src/cmdVM/vmAddAccess.go +++ b/src/cmdVM/vmAddAccess.go @@ -1,14 +1,14 @@ package cmdVM import ( - "io" - "os" - "errors" - "strings" - "net/mail" - "encoding/csv" - u "nexus-client/utils" - g "nexus-client/globals" + "io" + "os" + "errors" + "strings" + "net/mail" + "encoding/csv" + u "nexus-client/utils" + g "nexus-client/globals" ) type AddAccess struct { @@ -16,184 +16,184 @@ type AddAccess struct { } func (cmd *AddAccess)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *AddAccess)GetDesc() []string { - return []string{ - "Adds a user's VM access in one or more VMs.", - "Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."} + return []string{ + "Adds a user's VM access in one or more VMs.", + "Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."} } func (cmd *AddAccess)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" [ID ...] [regex ...] email [capability ...]") - u.PrintlnErr(" "+cmd.GetName()+" file.csv") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `file.csv 3-column CSV file specifying which VMs must have which VM access added: + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" [ID ...] [regex ...] email [capability ...]") + u.PrintlnErr(" "+cmd.GetName()+" file.csv") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `file.csv 3-column CSV file specifying which VMs must have which VM access added: column1 VM ID column2 email for which the VM Access must be removed column3 capabilities (space separated)` - u.PrintlnErr(usage) - printRegexUsageDetails() + u.PrintlnErr(usage) + printRegexUsageDetails() } type vmAccessForUserCaps struct { - Access map[string]int `json:"access" validate:"required"` + Access map[string]int `json:"access" validate:"required"` } func (cmd *AddAccess)Run(args []string) int { - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } - - statusCode := 0 - - if argc == 1 { - // Single argument and it's a CSV file - csvFile := args[0] - - file, err := os.Open(csvFile) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - defer file.Close() - reader := csv.NewReader(file) - line := 0 - for { - line += 1 - columns, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - u.PrintlnErr("FAILED reading "+err.Error()) - statusCode = 1 - continue - } - - columnCount := len(columns) - if columnCount != 3 { - u.PrintlnErr("FAILED reading record on line ",line,": expecting 3 columns") - statusCode = 1 - continue - } - - vmID := columns[0] - - email := columns[1] - if !u.IsEmail(email) { - u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") - statusCode = 1 - continue - } - - vmAccessCaps := &vmAccessForUserCaps { make(map[string]int) } - - capsStr := strings.TrimSpace(columns[2]) - if len(capsStr) > 0 { - caps := strings.Split(capsStr, " ") - for _, cap := range caps { - vmAccessCaps.Access[cap] = 1 - } - } - - if err := cmd.runRequest(vmID, vmID, email, vmAccessCaps); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully set VM access for "+email+" in VM \""+vmID+"\"") - } - } - } else { - // Multiple arguments: not a CSV file - - email, caps, patterns, err := cmd.parseArgs(args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - vms, err := getFilteredVMs("/vms/editaccess", patterns) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } - - vmAccessCaps := &vmAccessForUserCaps { make(map[string]int) } - for _, cap := range caps { - vmAccessCaps.Access[cap] = 1 - } - - for _, vm := range(vms) { - vmID := vm.ID.String() - vmName := vm.Name - if err := cmd.runRequest(vmID, vmName, email, vmAccessCaps); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully set VM access for "+email+" in VM \""+vm.Name+"\"") - } - } - } - - return statusCode + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } + + statusCode := 0 + + if argc == 1 { + // Single argument and it's a CSV file + csvFile := args[0] + + file, err := os.Open(csvFile) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + defer file.Close() + reader := csv.NewReader(file) + line := 0 + for { + line += 1 + columns, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + u.PrintlnErr("FAILED reading "+err.Error()) + statusCode = 1 + continue + } + + columnCount := len(columns) + if columnCount != 3 { + u.PrintlnErr("FAILED reading record on line ",line,": expecting 3 columns") + statusCode = 1 + continue + } + + vmID := columns[0] + + email := columns[1] + if !u.IsEmail(email) { + u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") + statusCode = 1 + continue + } + + vmAccessCaps := &vmAccessForUserCaps { make(map[string]int) } + + capsStr := strings.TrimSpace(columns[2]) + if len(capsStr) > 0 { + caps := strings.Split(capsStr, " ") + for _, cap := range caps { + vmAccessCaps.Access[cap] = 1 + } + } + + if err := cmd.runRequest(vmID, vmID, email, vmAccessCaps); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully set VM access for "+email+" in VM \""+vmID+"\"") + } + } + } else { + // Multiple arguments: not a CSV file + + email, caps, patterns, err := cmd.parseArgs(args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + vms, err := getFilteredVMs("/vms/editaccess", patterns) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } + + vmAccessCaps := &vmAccessForUserCaps { make(map[string]int) } + for _, cap := range caps { + vmAccessCaps.Access[cap] = 1 + } + + for _, vm := range(vms) { + vmID := vm.ID.String() + vmName := vm.Name + if err := cmd.runRequest(vmID, vmName, email, vmAccessCaps); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully set VM access for "+email+" in VM \""+vm.Name+"\"") + } + } + } + + return statusCode } func (cmd *AddAccess)runRequest(vmID, vmName, email string, vmAccessCaps *vmAccessForUserCaps) error { - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().SetBody(vmAccessCaps).Put(host+"/vms/"+vmID+"/access/"+email) - if err != nil { - return errors.New("Failed setting VM access for "+email+" in VM \""+vmName+"\": "+err.Error()) - } - - if resp.IsSuccess() { - return nil - } else { - return errors.New("Failed setting VM access for "+email+" in VM \""+vmName+"\": "+resp.Status()+": "+resp.String()) - } + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().SetBody(vmAccessCaps).Put(host+"/vms/"+vmID+"/access/"+email) + if err != nil { + return errors.New("Failed setting VM access for "+email+" in VM \""+vmName+"\": "+err.Error()) + } + + if resp.IsSuccess() { + return nil + } else { + return errors.New("Failed setting VM access for "+email+" in VM \""+vmName+"\": "+resp.Status()+": "+resp.String()) + } } func (cmd *AddAccess)parseArgs(args []string) (string, []string, []string, error) { - var patterns []string - var capabilities []string - emailFound := false - var email string - - for _, arg := range args { - // Before or after the email addres? - if !emailFound { - // Before the email address: it's either a "pattern" (ID or regex) or an email - parsed, err := mail.ParseAddress(arg) - if err != nil { - // Not an email address, then must be a "pattern" (ID or regex) - patterns = append(patterns, arg) - } else { - email = parsed.Address - emailFound = true - } - } else { - // After the email address: it's a capability - capabilities = append(capabilities, arg) - } - } - - if !emailFound { - return "", nil, nil, errors.New("An email address must be specified") - } - - return email, capabilities, patterns, nil + var patterns []string + var capabilities []string + emailFound := false + var email string + + for _, arg := range args { + // Before or after the email addres? + if !emailFound { + // Before the email address: it's either a "pattern" (ID or regex) or an email + parsed, err := mail.ParseAddress(arg) + if err != nil { + // Not an email address, then must be a "pattern" (ID or regex) + patterns = append(patterns, arg) + } else { + email = parsed.Address + emailFound = true + } + } else { + // After the email address: it's a capability + capabilities = append(capabilities, arg) + } + } + + if !emailFound { + return "", nil, nil, errors.New("An email address must be specified") + } + + return email, capabilities, patterns, nil } diff --git a/src/cmdVM/vmAttach.go b/src/cmdVM/vmAttach.go index 51014c9a730cdf17f96dae602558f42fa70fb3a8..d34fb9cab5326b0059ac5d98e3d5a386c2c506b7 100644 --- a/src/cmdVM/vmAttach.go +++ b/src/cmdVM/vmAttach.go @@ -1,10 +1,10 @@ package cmdVM import ( - // "sync" - "nexus-client/exec" - u "nexus-client/utils" - g "nexus-client/globals" + // "sync" + "nexus-client/exec" + u "nexus-client/utils" + g "nexus-client/globals" ) type Attach struct { @@ -12,53 +12,53 @@ type Attach struct { } func (cmd *Attach)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Attach)GetDesc() []string { - return []string{ - "Attaches to one or more VMs in order to use their desktop environment.", - "Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} + return []string{ + "Attaches to one or more VMs in order to use their desktop environment.", + "Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} } func (cmd *Attach)PrintUsage() { - printRegexUsage(cmd) - printRegexUsageDetails() + printRegexUsage(cmd) + printRegexUsageDetails() } func (cmd *Attach)Run(args []string) int { - hostname := g.GetInstance().Hostname - cert := g.GetInstance().PubCert + hostname := g.GetInstance().Hostname + cert := g.GetInstance().PubCert - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } - vms, err := getFilteredVMs("/vms/attach", args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/attach", args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - // Use wait groups to wait until all viewers threads have completed. - // var wg sync.WaitGroup - // wg.Add(len(vms)) + // Use wait groups to wait until all viewers threads have completed. + // var wg sync.WaitGroup + // wg.Add(len(vms)) - for _, vm := range(vms) { - go func(vm VMSerialized) { - exec.RunRemoteViewer(hostname, cert, vm.Name, vm.Port, vm.Pwd, false) - // wg.Done() - } (vm) - } + for _, vm := range(vms) { + go func(vm VMSerialized) { + exec.RunRemoteViewer(hostname, cert, vm.Name, vm.Port, vm.Pwd, false) + // wg.Done() + } (vm) + } - // wg.Wait() + // wg.Wait() - return 0 + return 0 } diff --git a/src/cmdVM/vmCreate.go b/src/cmdVM/vmCreate.go index 892e109eb2356c54f75b5fb06717ee99e9dbf9b2..fc0bf6109056cce489da78cd88a99a1611e9d527 100644 --- a/src/cmdVM/vmCreate.go +++ b/src/cmdVM/vmCreate.go @@ -1,10 +1,10 @@ package cmdVM import ( - "fmt" - "strconv" - u "nexus-client/utils" - g "nexus-client/globals" + "fmt" + "strconv" + u "nexus-client/utils" + g "nexus-client/globals" ) type Create struct { @@ -12,23 +12,23 @@ type Create struct { } func (cmd *Create)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Create)GetDesc() []string { - return []string{ - "Creates one or more VMs.", - "Requires VM_CREATE user capability."} + return []string{ + "Creates one or more VMs.", + "Requires VM_CREATE user capability."} } func (cmd *Create)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" name cpus ram nic template [count|file.csv]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `name Name of the VM to create. + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" name cpus ram nic template [count|file.csv]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `name Name of the VM to create. cpus Number of CPUs, between 1 and 16. ram Amount of RAM in MB, between 512 and 32768. nic Network interface, either "none" (no network) or "user" (network access). @@ -38,106 +38,106 @@ count Number of VMs to create (if not specified, one is created). where n ranges from 1..count. file.csv 1-column CSV file defining the students names. Each VM's name is postfixed with each entry in the CSV file.` - u.PrintlnErr(usage) + u.PrintlnErr(usage) } func (cmd *Create)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if argc < 5 || argc > 6 { - cmd.PrintUsage() - return 1 - } - - name := args[0] - ncpus, err := strconv.Atoi(args[1]) - if err != nil { - u.PrintlnErr("Invalid number of CPU(s)") - return 1 - } - ram, err := strconv.Atoi(args[2]) - if err != nil { - u.PrintlnErr("Invalid amount of RAM") - return 1 - } - nic := args[3] - template := args[4] - count := 1 - var csvEntries []string = nil - - // If there is a 6th argument, checks whether it's a number or a CSV file. - if argc == 6 { - count, err = strconv.Atoi(args[5]) - if err != nil { - // It's not a number, we assume it's a CSV file and parse it. - csvFile := args[5] - csvEntries, err = u.ReadCSVColumn(csvFile, 0) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - count = len(csvEntries) - } else { - if count < 1 { - u.PrintlnErr("count must be > 0") - return 1 - } - } - } - - // At this point: - // if csvFile == "" -> we must create count VMs - // otherwise -> we parse the CSV to know how many VMs to create - - type VMArgs struct { - Name string - Cpus int - Ram int - Nic string - TemplateID string - } - - vmArgs := &VMArgs { - Name: name, - Cpus: ncpus, - Ram: ram, - Nic: nic, - TemplateID: template, - } - - statusCode := 0 - digits := len(strconv.Itoa(count)) - - for i := 1; i <= count; i++ { - if csvEntries == nil { - if count > 1 { - numberPadded := fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", i) - vmArgs.Name = name+" ["+numberPadded+"]" - } - } else { - vmArgs.Name = name+" ["+csvEntries[i-1]+"]" - } - - resp, err := client.R().SetBody(vmArgs).Post(host+"/vms") - if err != nil { - u.PrintlnErr("Failed creating VM \""+vmArgs.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - 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 - } - } - } - - return statusCode + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc < 5 || argc > 6 { + cmd.PrintUsage() + return 1 + } + + name := args[0] + ncpus, err := strconv.Atoi(args[1]) + if err != nil { + u.PrintlnErr("Invalid number of CPU(s)") + return 1 + } + ram, err := strconv.Atoi(args[2]) + if err != nil { + u.PrintlnErr("Invalid amount of RAM") + return 1 + } + nic := args[3] + template := args[4] + count := 1 + var csvEntries []string = nil + + // If there is a 6th argument, checks whether it's a number or a CSV file. + if argc == 6 { + count, err = strconv.Atoi(args[5]) + if err != nil { + // It's not a number, we assume it's a CSV file and parse it. + csvFile := args[5] + csvEntries, err = u.ReadCSVColumn(csvFile, 0) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + count = len(csvEntries) + } else { + if count < 1 { + u.PrintlnErr("count must be > 0") + return 1 + } + } + } + + // At this point: + // if csvFile == "" -> we must create count VMs + // otherwise -> we parse the CSV to know how many VMs to create + + type VMArgs struct { + Name string + Cpus int + Ram int + Nic string + TemplateID string + } + + vmArgs := &VMArgs { + Name: name, + Cpus: ncpus, + Ram: ram, + Nic: nic, + TemplateID: template, + } + + statusCode := 0 + digits := len(strconv.Itoa(count)) + + for i := 1; i <= count; i++ { + if csvEntries == nil { + if count > 1 { + numberPadded := fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", i) + vmArgs.Name = name+" ["+numberPadded+"]" + } + } else { + vmArgs.Name = name+" ["+csvEntries[i-1]+"]" + } + + resp, err := client.R().SetBody(vmArgs).Post(host+"/vms") + if err != nil { + u.PrintlnErr("Failed creating VM \""+vmArgs.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + 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 + } + } + } + + return statusCode } diff --git a/src/cmdVM/vmCred2pdf.go b/src/cmdVM/vmCred2pdf.go index 9d1d644881e0ab98bcc3a8bc6aca698af9fae06a..52f60b7b8dc37c7914179e662a6489c2ff59f26d 100644 --- a/src/cmdVM/vmCred2pdf.go +++ b/src/cmdVM/vmCred2pdf.go @@ -1,9 +1,9 @@ package cmdVM import ( - "strconv" - u "nexus-client/utils" - "github.com/go-pdf/fpdf" + "strconv" + u "nexus-client/utils" + "github.com/go-pdf/fpdf" ) type Cred2pdf struct { @@ -11,78 +11,78 @@ type Cred2pdf struct { } func (cmd *Cred2pdf)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Cred2pdf)GetDesc() []string { - return []string{ - "Creates a PDF with the credentials required to attach to running VMs.", - "Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} + return []string{ + "Creates a PDF with the credentials required to attach to running VMs.", + "Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} } func (cmd *Cred2pdf)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName()," [ID ...] [regex ...] pdfile") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - printRegexUsageDetails() + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName()," [ID ...] [regex ...] pdfile") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + printRegexUsageDetails() } func (cmd *Cred2pdf)Run(args []string) int { - argc := len(args) - if argc < 2 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 2 { + cmd.PrintUsage() + return 1 + } + + pdfFile := args[argc-1] - pdfFile := args[argc-1] + vms, err := getFilteredVMs("/vms/attach", args[:argc-1]) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } - vms, err := getFilteredVMs("/vms/attach", args[:argc-1]) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + const leftMargin = 8. + const topMargin = 5. + const rightMargin = 0. + columnWidth := [...]float64 {96.,14.,23.} + const rowHeight = 15. + const fontSize = 10. + const cellBorder = "1" // full cell border; use "" for no border + const nextPos = 0 // 0 means to move right + const align = "L" + const bkgd = false // transparent background; use true for filled + const link = 0 // no link + const url = "" // no url - const leftMargin = 8. - const topMargin = 5. - const rightMargin = 0. - columnWidth := [...]float64 {96.,14.,23.} - const rowHeight = 15. - const fontSize = 10. - const cellBorder = "1" // full cell border; use "" for no border - const nextPos = 0 // 0 means to move right - const align = "L" - const bkgd = false // transparent background; use true for filled - const link = 0 // no link - const url = "" // no url - - pdf := fpdf.New("p", "mm", "A4", "") // "p" means portrait orienation - pdf.SetMargins(leftMargin, topMargin, rightMargin) - pdf.AddPage() + pdf := fpdf.New("p", "mm", "A4", "") // "p" means portrait orienation + pdf.SetMargins(leftMargin, topMargin, rightMargin) + pdf.AddPage() - for _, vm := range vms { - pdf.SetX(leftMargin) - pdf.SetFont("Arial", "b", fontSize) // "b" means bold; use "" for normal - pdf.CellFormat(columnWidth[0], rowHeight, vm.Name, cellBorder, nextPos, align, bkgd, link, url) - //pdf.SetFont("Courier", "b", fontSize) // "b" means bold; use "" for normal - pdf.CellFormat(columnWidth[1], rowHeight, strconv.Itoa(vm.Port), cellBorder, nextPos, "C", bkgd, link, url) - //pdf.SetFont("Courier", "b", fontSize+1) // "b" means bold; use "" for normal - pdf.CellFormat(columnWidth[2], rowHeight, vm.Pwd, cellBorder, nextPos, "C", bkgd, link, url) - pdf.Ln(-1) // line break; -1 means move by the height of the last printed cell - } + for _, vm := range vms { + pdf.SetX(leftMargin) + pdf.SetFont("Arial", "b", fontSize) // "b" means bold; use "" for normal + pdf.CellFormat(columnWidth[0], rowHeight, vm.Name, cellBorder, nextPos, align, bkgd, link, url) + //pdf.SetFont("Courier", "b", fontSize) // "b" means bold; use "" for normal + pdf.CellFormat(columnWidth[1], rowHeight, strconv.Itoa(vm.Port), cellBorder, nextPos, "C", bkgd, link, url) + //pdf.SetFont("Courier", "b", fontSize+1) // "b" means bold; use "" for normal + pdf.CellFormat(columnWidth[2], rowHeight, vm.Pwd, cellBorder, nextPos, "C", bkgd, link, url) + pdf.Ln(-1) // line break; -1 means move by the height of the last printed cell + } - if err = pdf.OutputFileAndClose(pdfFile); err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } + if err = pdf.OutputFileAndClose(pdfFile); err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } - u.Println(pdfFile+" written successfully.") - return 0 + u.Println(pdfFile+" written successfully.") + return 0 } diff --git a/src/cmdVM/vmDel.go b/src/cmdVM/vmDel.go index 650d8584b73f6d28011f804072238b4dcbac14ca..0480b1b41968bd217788877b1d836f1cdee4a6fd 100644 --- a/src/cmdVM/vmDel.go +++ b/src/cmdVM/vmDel.go @@ -1,8 +1,8 @@ package cmdVM import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Del struct { @@ -10,59 +10,59 @@ type Del struct { } func (cmd *Del)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Del)GetDesc() []string { - return []string{ - "Deletes one or more VMs.", - "Requires VM_DESTROY VM access capability or VM_DESTROY_ANY user capability."} + return []string{ + "Deletes one or more VMs.", + "Requires VM_DESTROY VM access capability or VM_DESTROY_ANY user capability."} } func (cmd *Del)PrintUsage() { - printRegexUsage(cmd) - printRegexUsageDetails() + printRegexUsage(cmd) + printRegexUsageDetails() } func (cmd *Del)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } - vms, err := getFilteredVMs("/vms/del", args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/del", args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - statusCode := 0 + statusCode := 0 - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().Delete(host+"/vms/"+uuid) - if err != nil { - u.PrintlnErr("Failed deleting VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Deleted VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Failed deleting VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().Delete(host+"/vms/"+uuid) + if err != nil { + u.PrintlnErr("Failed deleting VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Deleted VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Failed deleting VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } - } + } - return statusCode + return statusCode } diff --git a/src/cmdVM/vmDelAccess.go b/src/cmdVM/vmDelAccess.go index 4a287c45e54ff0e535ce29b6cf3087731c0a19b7..51919ff2fb9066d65f58597c7f4276eed0394fa1 100644 --- a/src/cmdVM/vmDelAccess.go +++ b/src/cmdVM/vmDelAccess.go @@ -1,13 +1,13 @@ package cmdVM import ( - "io" - "os" - "errors" - "net/mail" - "encoding/csv" - u "nexus-client/utils" - g "nexus-client/globals" + "io" + "os" + "errors" + "net/mail" + "encoding/csv" + u "nexus-client/utils" + g "nexus-client/globals" ) type DelAccess struct { @@ -15,160 +15,160 @@ type DelAccess struct { } func (cmd *DelAccess)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *DelAccess)GetDesc() []string { - return []string{ - "Removes a user's VM access in one or more VMs.", - "Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."} + return []string{ + "Removes a user's VM access in one or more VMs.", + "Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."} } func (cmd *DelAccess)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" [ID ...] [regex ...] email") - u.PrintlnErr(" "+cmd.GetName()+" file.csv") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `file.csv 2-column CSV file specifying which VMs must have which VM access removed: + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" [ID ...] [regex ...] email") + u.PrintlnErr(" "+cmd.GetName()+" file.csv") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `file.csv 2-column CSV file specifying which VMs must have which VM access removed: column1 VM ID column2 email for which the VM Access must be removed` - u.PrintlnErr(usage) - printRegexUsageDetails() + u.PrintlnErr(usage) + printRegexUsageDetails() } func (cmd *DelAccess)Run(args []string) int { - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } - - statusCode := 0 - - if argc == 1 { - // Single argument and it's a CSV file - csvFile := args[0] - - file, err := os.Open(csvFile) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - defer file.Close() - reader := csv.NewReader(file) - line := 0 - for { - line += 1 - columns, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - u.PrintlnErr("FAILED reading "+err.Error()) - statusCode = 1 - continue - } - - columnCount := len(columns) - if columnCount != 2 { - u.PrintlnErr("FAILED reading record on line ",line,": expecting 2 columns") - statusCode = 1 - continue - } - - vmID := columns[0] - - email := columns[1] - if !u.IsEmail(email) { - u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") - statusCode = 1 - continue - } - - if err := cmd.runRequest(vmID, vmID, email); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully removed VM access for "+email+" in VM \""+vmID+"\"") - } - } - } else { - // Multiple arguments: not a CSV file - - email, patterns, err := cmd.parseArgs(args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - vms, err := getFilteredVMs("/vms/editaccess", patterns) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } - - for _, vm := range(vms) { - vmID := vm.ID.String() - vmName := vm.Name - if err := cmd.runRequest(vmID, vmName, email); err != nil { - u.PrintlnErr(err.Error()) - statusCode = 1 - } else { - u.Println("Successfully removed VM access for "+email+" in VM \""+vm.Name+"\"") - } - } - } - - return statusCode + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } + + statusCode := 0 + + if argc == 1 { + // Single argument and it's a CSV file + csvFile := args[0] + + file, err := os.Open(csvFile) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + defer file.Close() + reader := csv.NewReader(file) + line := 0 + for { + line += 1 + columns, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + u.PrintlnErr("FAILED reading "+err.Error()) + statusCode = 1 + continue + } + + columnCount := len(columns) + if columnCount != 2 { + u.PrintlnErr("FAILED reading record on line ",line,": expecting 2 columns") + statusCode = 1 + continue + } + + vmID := columns[0] + + email := columns[1] + if !u.IsEmail(email) { + u.PrintlnErr("FAILED reading record on line ",line,": ",email," is not a valid email") + statusCode = 1 + continue + } + + if err := cmd.runRequest(vmID, vmID, email); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully removed VM access for "+email+" in VM \""+vmID+"\"") + } + } + } else { + // Multiple arguments: not a CSV file + + email, patterns, err := cmd.parseArgs(args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + vms, err := getFilteredVMs("/vms/editaccess", patterns) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } + + for _, vm := range(vms) { + vmID := vm.ID.String() + vmName := vm.Name + if err := cmd.runRequest(vmID, vmName, email); err != nil { + u.PrintlnErr(err.Error()) + statusCode = 1 + } else { + u.Println("Successfully removed VM access for "+email+" in VM \""+vm.Name+"\"") + } + } + } + + return statusCode } func (cmd *DelAccess)runRequest(vmID, vmName, email string) error { - client := g.GetInstance().Client - host := g.GetInstance().Host - - resp, err := client.R().Delete(host+"/vms/"+vmID+"/access/"+email) - if err != nil { - return errors.New("Failed removing VM access for "+email+" in VM \""+vmName+"\": "+err.Error()) - } - - if resp.IsSuccess() { - return nil - } else { - return errors.New("Failed removing VM access for "+email+" in VM \""+vmName+"\": "+resp.Status()+": "+resp.String()) - } + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().Delete(host+"/vms/"+vmID+"/access/"+email) + if err != nil { + return errors.New("Failed removing VM access for "+email+" in VM \""+vmName+"\": "+err.Error()) + } + + if resp.IsSuccess() { + return nil + } else { + return errors.New("Failed removing VM access for "+email+" in VM \""+vmName+"\": "+resp.Status()+": "+resp.String()) + } } func (cmd *DelAccess)parseArgs(args []string) (string, []string, error) { - var patterns []string - emailFound := false - var email string - - for _, arg := range args { - parsed, err := mail.ParseAddress(arg) - if err != nil { - // Not an email address, then must be a "pattern" (ID or regex) - patterns = append(patterns, arg) - } else { - // An email address was found - if emailFound { - return "", nil, errors.New("Only one email address must be specified") - } - email = parsed.Address - emailFound = true - } - } - - if !emailFound { - return "", nil, errors.New("An email address must be specified") - } - - return email, patterns, nil + var patterns []string + emailFound := false + var email string + + for _, arg := range args { + parsed, err := mail.ParseAddress(arg) + if err != nil { + // Not an email address, then must be a "pattern" (ID or regex) + patterns = append(patterns, arg) + } else { + // An email address was found + if emailFound { + return "", nil, errors.New("Only one email address must be specified") + } + email = parsed.Address + emailFound = true + } + } + + if !emailFound { + return "", nil, errors.New("An email address must be specified") + } + + return email, patterns, nil } diff --git a/src/cmdVM/vmEdit.go b/src/cmdVM/vmEdit.go index b40a54707fbcd04b92f191fcbde608a59db9fac7..c2685361ddd3ffdf6c3008bdaff4bd91438913d7 100644 --- a/src/cmdVM/vmEdit.go +++ b/src/cmdVM/vmEdit.go @@ -1,11 +1,11 @@ package cmdVM import ( - "strconv" - "strings" - "errors" - u "nexus-client/utils" - g "nexus-client/globals" + "strconv" + "strings" + "errors" + u "nexus-client/utils" + g "nexus-client/globals" ) type Edit struct { @@ -13,145 +13,145 @@ type Edit struct { } type vmEditParameters struct { - Name string - Cpus int - Ram int - Nic string + Name string + Cpus int + Ram int + Nic string } func (cmd *Edit)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Edit)GetDesc() []string { - return []string{ - "Edits one or more VMs' properties: name, cpus, ram or nic.", - "Requires VM_EDIT VM access capability or VM_EDIT_ANY user capability."} + return []string{ + "Edits one or more VMs' properties: name, cpus, ram or nic.", + "Requires VM_EDIT VM access capability or VM_EDIT_ANY user capability."} } func (cmd *Edit)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" [ID ...] [regex ...] [name=\"new name\"] [cpus=n] [ram=n] [nic=none/user]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `Parameters that can be changed (at least one must be specified): + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()+" [ID ...] [regex ...] [name=\"new name\"] [cpus=n] [ram=n] [nic=none/user]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `Parameters that can be changed (at least one must be specified): name Name of the VM. cpus Number of CPUs, between 1 and 16. ram Amount of RAM in MB, between 512 and 32768. nic Network interface, either "none" (no network) or "user" (network access).` - u.PrintlnErr(usage) - u.PrintlnErr("") - printRegexUsageDetails() + u.PrintlnErr(usage) + u.PrintlnErr("") + printRegexUsageDetails() } func (cmd *Edit)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } - - vmParams, patterns, err := cmd.parseArgs(args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - if vmParams == nil { - cmd.PrintUsage() - return 1 - } - - vms, err := getFilteredVMs("/vms/edit", patterns) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } - - statusCode := 0 - - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().SetBody(vmParams).Put(host+"/vms/"+uuid) - if err != nil { - u.PrintlnErr("Failed editing VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Successfully edited VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Failed editing VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } - } - - return statusCode + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } + + vmParams, patterns, err := cmd.parseArgs(args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + if vmParams == nil { + cmd.PrintUsage() + return 1 + } + + vms, err := getFilteredVMs("/vms/edit", patterns) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } + + statusCode := 0 + + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().SetBody(vmParams).Put(host+"/vms/"+uuid) + if err != nil { + u.PrintlnErr("Failed editing VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Successfully edited VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Failed editing VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } + } + + return statusCode } func (cmd *Edit)parseArgs(args []string) (*vmEditParameters, []string, error) { - vmParams := &vmEditParameters {} - var patterns []string - atLeastOneArg := false - - getStringVal := func(s string, prefix string) string { - if strings.HasPrefix(s, prefix) { - parts := strings.Split(s, prefix) - return parts[1] - } - return "" - } - - for _, arg := range args { - s := getStringVal(arg, "name=") - if s != "" { - vmParams.Name = s - atLeastOneArg = true - continue - } - s = getStringVal(arg, "cpus=") - if s != "" { - cpus, err := strconv.Atoi(s) - if err != nil { - return nil, nil, errors.New("Invalid number of CPU(s)") - } - vmParams.Cpus = cpus - atLeastOneArg = true - continue - } - s = getStringVal(arg, "ram=") - if s != "" { - ram, err := strconv.Atoi(s) - if err != nil { - return nil, nil, errors.New("Invalid amount of RAM") - } - vmParams.Ram = ram - atLeastOneArg = true - continue - } - s = getStringVal(arg, "nic=") - if s != "" { - vmParams.Nic = s - atLeastOneArg = true - continue - } - - patterns = append(patterns, arg) - } - - if atLeastOneArg { - return vmParams, patterns, nil - } else { - return nil, nil, nil - } + vmParams := &vmEditParameters {} + var patterns []string + atLeastOneArg := false + + getStringVal := func(s string, prefix string) string { + if strings.HasPrefix(s, prefix) { + parts := strings.Split(s, prefix) + return parts[1] + } + return "" + } + + for _, arg := range args { + s := getStringVal(arg, "name=") + if s != "" { + vmParams.Name = s + atLeastOneArg = true + continue + } + s = getStringVal(arg, "cpus=") + if s != "" { + cpus, err := strconv.Atoi(s) + if err != nil { + return nil, nil, errors.New("Invalid number of CPU(s)") + } + vmParams.Cpus = cpus + atLeastOneArg = true + continue + } + s = getStringVal(arg, "ram=") + if s != "" { + ram, err := strconv.Atoi(s) + if err != nil { + return nil, nil, errors.New("Invalid amount of RAM") + } + vmParams.Ram = ram + atLeastOneArg = true + continue + } + s = getStringVal(arg, "nic=") + if s != "" { + vmParams.Nic = s + atLeastOneArg = true + continue + } + + patterns = append(patterns, arg) + } + + if atLeastOneArg { + return vmParams, patterns, nil + } else { + return nil, nil, nil + } } diff --git a/src/cmdVM/vmExportDir.go b/src/cmdVM/vmExportDir.go index 65339f0d9123a4a24363939589f5d106b650a89f..a21da1ce912ec6268fc95d7aeb54e6326781885e 100644 --- a/src/cmdVM/vmExportDir.go +++ b/src/cmdVM/vmExportDir.go @@ -1,9 +1,9 @@ package cmdVM import ( - "time" - u "nexus-client/utils" - g "nexus-client/globals" + "time" + u "nexus-client/utils" + g "nexus-client/globals" ) type ExportDir struct { @@ -11,79 +11,79 @@ type ExportDir struct { } type vmExportDirParams struct { - Dir string + Dir string } func (cmd *ExportDir)GetName() string { - return cmd.Name + return cmd.Name } func (cmd *ExportDir)GetDesc() []string { - return []string{ - "Exports one or more VMs' directory into one or more compressed archives.", - "Requires VM_READFS VM access capability or VM_READFS_ANY user capability."} + return []string{ + "Exports one or more VMs' directory into one or more compressed archives.", + "Requires VM_READFS VM access capability or VM_READFS_ANY user capability."} } func (cmd *ExportDir)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName()," [ID ...] [regex ...] vmDir") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `vmDir Directory (in the VM) to export into a compressed archive. + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName()," [ID ...] [regex ...] vmDir") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `vmDir Directory (in the VM) to export into a compressed archive. Remark: one archive, named after the VM's name, is created per VM.` - u.PrintlnErr(usage) - u.PrintlnErr("") - printRegexUsageDetails() + u.PrintlnErr(usage) + u.PrintlnErr("") + printRegexUsageDetails() } func (cmd *ExportDir)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host - - argc := len(args) - if argc < 2 { - cmd.PrintUsage() - return 1 - } - - dir := args[argc-1] - - vms, err := getFilteredVMs("/vms/exportdir", args[:argc-1]) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } - - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } - - params := &vmExportDirParams { Dir: dir } - - statusCode := 0 - - client.SetAllowGetMethodPayload(true) - - for _, vm := range(vms) { - uuid := vm.ID.String() - outputFile := vm.Name+".tar.gz" - resp, err := client.R().SetOutput(outputFile).SetBody(params).Get(host+"/vms/"+uuid+"/exportdir") - time.Sleep(2 * time.Second) - if err != nil { - u.PrintlnErr("Failed exporting "+dir+" from VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Exported "+dir+" from VM \""+vm.Name+"\" into "+outputFile) - } else { - u.PrintlnErr("Failed exporting "+dir+" from VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } - } - - return statusCode + client := g.GetInstance().Client + host := g.GetInstance().Host + + argc := len(args) + if argc < 2 { + cmd.PrintUsage() + return 1 + } + + dir := args[argc-1] + + vms, err := getFilteredVMs("/vms/exportdir", args[:argc-1]) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } + + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } + + params := &vmExportDirParams { Dir: dir } + + statusCode := 0 + + client.SetAllowGetMethodPayload(true) + + for _, vm := range(vms) { + uuid := vm.ID.String() + outputFile := vm.Name+".tar.gz" + resp, err := client.R().SetOutput(outputFile).SetBody(params).Get(host+"/vms/"+uuid+"/exportdir") + time.Sleep(2 * time.Second) + if err != nil { + u.PrintlnErr("Failed exporting "+dir+" from VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Exported "+dir+" from VM \""+vm.Name+"\" into "+outputFile) + } else { + u.PrintlnErr("Failed exporting "+dir+" from VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } + } + + return statusCode } diff --git a/src/cmdVM/vmImportDir.go b/src/cmdVM/vmImportDir.go index 53c049ce904e1d3b6f8e78f1d1a8794e3ef0c6ab..c2f9a33e76b87ad64efae330bb863f19288dce64 100644 --- a/src/cmdVM/vmImportDir.go +++ b/src/cmdVM/vmImportDir.go @@ -1,9 +1,9 @@ package cmdVM import ( - "os" - u "nexus-client/utils" - g "nexus-client/globals" + "os" + u "nexus-client/utils" + g "nexus-client/globals" ) type ImportDir struct { @@ -11,86 +11,86 @@ type ImportDir struct { } func (cmd *ImportDir)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *ImportDir)GetDesc() []string { - return []string{ - "Copies a local directory (or file) and all its content into one or more VMs.", - "Requires VM_WRITEFS VM access capability or VM_WRITEFS_ANY user capability."} + return []string{ + "Copies a local directory (or file) and all its content into one or more VMs.", + "Requires VM_WRITEFS VM access capability or VM_WRITEFS_ANY user capability."} } func (cmd *ImportDir)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName()," [ID ...] [regex ...] localDir vmDir") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - const usage string = `localDir Directory (or file) on the local filesystem to be copied into the VM. + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName()," [ID ...] [regex ...] localDir vmDir") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + const usage string = `localDir Directory (or file) on the local filesystem to be copied into the VM. vmdir Directory in the VM where localDir will be copied. IMPORTANT: to avoid disk corruption, files can only be imported into non-running VMs.` - u.PrintlnErr(usage) - u.PrintlnErr("") - printRegexUsageDetails() + u.PrintlnErr(usage) + u.PrintlnErr("") + printRegexUsageDetails() } func (cmd *ImportDir)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc < 3 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 3 { + cmd.PrintUsage() + return 1 + } - vmDir := args[argc-1] - localDir := args[argc-2] + vmDir := args[argc-1] + localDir := args[argc-2] - vms, err := getFilteredVMs("/vms/importfiles", args[:argc-2]) - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/importfiles", args[:argc-2]) + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - statusCode := 0 + statusCode := 0 - tmpTarGzFile, err := u.GetRandomTempFilename() - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } - tmpTarGzFile += ".tar" - defer os.Remove(tmpTarGzFile) - if err := u.TarGzDir(localDir, tmpTarGzFile); err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + tmpTarGzFile, err := u.GetRandomTempFilename() + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } + tmpTarGzFile += ".tar" + defer os.Remove(tmpTarGzFile) + if err := u.TarGzDir(localDir, tmpTarGzFile); err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().SetFile("file", tmpTarGzFile). - SetFormData(map[string]string { - "vmDir": vmDir, - }).Post(host+"/vms/"+uuid+"/importfiles") - if err != nil { - u.PrintlnErr("Failed copying \""+localDir+"\" into \""+vmDir+"\" in VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Copied \""+localDir+"\" into \""+vmDir+"\" in VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Failed copying \""+localDir+"\" into \""+vmDir+"\" in VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } - } + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().SetFile("file", tmpTarGzFile). + SetFormData(map[string]string { + "vmDir": vmDir, + }).Post(host+"/vms/"+uuid+"/importfiles") + if err != nil { + u.PrintlnErr("Failed copying \""+localDir+"\" into \""+vmDir+"\" in VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Copied \""+localDir+"\" into \""+vmDir+"\" in VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Failed copying \""+localDir+"\" into \""+vmDir+"\" in VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } + } - return statusCode + return statusCode } diff --git a/src/cmdVM/vmList.go b/src/cmdVM/vmList.go index b710688a964c740d195382f72392bebbf4e5e60a..6a5af7d93fd3995ecb4d383c684d507c857cb7b4 100644 --- a/src/cmdVM/vmList.go +++ b/src/cmdVM/vmList.go @@ -1,7 +1,7 @@ package cmdVM import ( - u "nexus-client/utils" + u "nexus-client/utils" ) type List struct { @@ -9,26 +9,26 @@ type List struct { } func (cmd *List)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *List)GetDesc() []string { - return []string{ - "Lists VMs.", - "Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} + return []string{ + "Lists VMs.", + "Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} } func (cmd *List)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: ",cmd.GetName(), " [-l] [ID ...] [regex ...]") - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("Use \"-l\" to specify detailed VMs output.") - printRegexUsageDetails() + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: ",cmd.GetName(), " [-l] [ID ...] [regex ...]") + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("Use \"-l\" to specify detailed VMs output.") + printRegexUsageDetails() } func (cmd *List)Run(args []string) int { - return printFilteredVMs(cmd, args, "/vms") + return printFilteredVMs(cmd, args, "/vms") } diff --git a/src/cmdVM/vmReboot.go b/src/cmdVM/vmReboot.go index ccba0a8c3389d95339cc1ed22b57894d8b06fc54..70ef2045ba99efec37929a397f51b58156df570f 100644 --- a/src/cmdVM/vmReboot.go +++ b/src/cmdVM/vmReboot.go @@ -1,8 +1,8 @@ package cmdVM import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Reboot struct { @@ -10,59 +10,59 @@ type Reboot struct { } func (cmd *Reboot)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Reboot)GetDesc() []string { - return []string{ - "Gracefully reboots one or more VMs.", - "Requires VM_REBOOT VM access capability or VM_REBOOT_ANY user capability."} + return []string{ + "Gracefully reboots one or more VMs.", + "Requires VM_REBOOT VM access capability or VM_REBOOT_ANY user capability."} } func (cmd *Reboot)PrintUsage() { - printRegexUsage(cmd) - printRegexUsageDetails() + printRegexUsage(cmd) + printRegexUsageDetails() } func (cmd *Reboot)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } - vms, err := getFilteredVMs("/vms/reboot", args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/reboot", args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - statusCode := 0 + statusCode := 0 - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().Put(host+"/vms/"+uuid+"/reboot") - if err != nil { - u.PrintlnErr("Reboot failed for VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Sent reboot message to VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Reboot failed for VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().Put(host+"/vms/"+uuid+"/reboot") + if err != nil { + u.PrintlnErr("Reboot failed for VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Sent reboot message to VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Reboot failed for VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } - } + } - return statusCode + return statusCode } diff --git a/src/cmdVM/vmShutdown.go b/src/cmdVM/vmShutdown.go index 21a97d26e6b733b10dfc19c4d0de879c5fcfaf27..2f71b446d11c56377fb6f4d240db2bef6debfe50 100644 --- a/src/cmdVM/vmShutdown.go +++ b/src/cmdVM/vmShutdown.go @@ -1,8 +1,8 @@ package cmdVM import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Shutdown struct { @@ -10,59 +10,59 @@ type Shutdown struct { } func (cmd *Shutdown)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Shutdown)GetDesc() []string { - return []string{ - "Gracefully shutdowns one or more VMs.", - "Requires VM_STOP VM access capability or VM_STOP_ANY user capability."} + return []string{ + "Gracefully shutdowns one or more VMs.", + "Requires VM_STOP VM access capability or VM_STOP_ANY user capability."} } func (cmd *Shutdown)PrintUsage() { - printRegexUsage(cmd) - printRegexUsageDetails() + printRegexUsage(cmd) + printRegexUsageDetails() } func (cmd *Shutdown)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } - vms, err := getFilteredVMs("/vms/stop", args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/stop", args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - statusCode := 0 + statusCode := 0 - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().Put(host+"/vms/"+uuid+"/shutdown") - if err != nil { - u.PrintlnErr("Shutdown failed for VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Sent shutdown message to VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Shutdown failed for VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().Put(host+"/vms/"+uuid+"/shutdown") + if err != nil { + u.PrintlnErr("Shutdown failed for VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Sent shutdown message to VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Shutdown failed for VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } - } + } - return statusCode + return statusCode } diff --git a/src/cmdVM/vmStart.go b/src/cmdVM/vmStart.go index fc847ca51a1a00a051e5b6c789b41fb9f73f600c..519ac4829e7e61fe88352316c4b9734a5deb9f68 100644 --- a/src/cmdVM/vmStart.go +++ b/src/cmdVM/vmStart.go @@ -1,8 +1,8 @@ package cmdVM import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Start struct { @@ -10,59 +10,59 @@ type Start struct { } func (cmd *Start)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Start)GetDesc() []string { - return []string{ - "Starts one or more VMs.", - "Requires VM_START VM access capability or VM_START_ANY user capability."} + return []string{ + "Starts one or more VMs.", + "Requires VM_START VM access capability or VM_START_ANY user capability."} } func (cmd *Start)PrintUsage() { - printRegexUsage(cmd) - printRegexUsageDetails() + printRegexUsage(cmd) + printRegexUsageDetails() } func (cmd *Start)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } - vms, err := getFilteredVMs("/vms/start", args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/start", args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - statusCode := 0 + statusCode := 0 - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().Put(host+"/vms/"+uuid+"/start") - if err != nil { - u.PrintlnErr("Failed starting VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Started VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Failed starting VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().Put(host+"/vms/"+uuid+"/start") + if err != nil { + u.PrintlnErr("Failed starting VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Started VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Failed starting VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } - } + } - return statusCode + return statusCode } diff --git a/src/cmdVM/vmStop.go b/src/cmdVM/vmStop.go index c2a66a1cc0313af53abd532dd00981cb2f23d51c..3655cfa4d6d122523ed9b30590dcf2e4bc5f2988 100644 --- a/src/cmdVM/vmStop.go +++ b/src/cmdVM/vmStop.go @@ -1,8 +1,8 @@ package cmdVM import ( - u "nexus-client/utils" - g "nexus-client/globals" + u "nexus-client/utils" + g "nexus-client/globals" ) type Stop struct { @@ -10,59 +10,59 @@ type Stop struct { } func (cmd *Stop)GetName() string { - return cmd.Name + return cmd.Name } - + func (cmd *Stop)GetDesc() []string { - return []string{ - "Kills one or more VMs.", - "Requires VM_STOP VM access capability or VM_STOP_ANY user capability."} + return []string{ + "Kills one or more VMs.", + "Requires VM_STOP VM access capability or VM_STOP_ANY user capability."} } func (cmd *Stop)PrintUsage() { - printRegexUsage(cmd) - printRegexUsageDetails() + printRegexUsage(cmd) + printRegexUsageDetails() } func (cmd *Stop)Run(args []string) int { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - argc := len(args) - if argc < 1 { - cmd.PrintUsage() - return 1 - } + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } - vms, err := getFilteredVMs("/vms/stop", args) - if err != nil { - u.PrintlnErr(err.Error()) - return 1 - } + vms, err := getFilteredVMs("/vms/stop", args) + if err != nil { + u.PrintlnErr(err.Error()) + return 1 + } - if len(vms) == 0 { - u.PrintlnErr("No match.") - return 1 - } + if len(vms) == 0 { + u.PrintlnErr("No match.") + return 1 + } - statusCode := 0 + statusCode := 0 - for _, vm := range(vms) { - uuid := vm.ID.String() - resp, err := client.R().Put(host+"/vms/"+uuid+"/stop") - if err != nil { - u.PrintlnErr("Failed stopping VM \""+vm.Name+"\": "+err.Error()) - statusCode = 1 - } else { - if resp.IsSuccess() { - u.Println("Stopped VM \""+vm.Name+"\"") - } else { - u.PrintlnErr("Failed stopping VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) - statusCode = 1 - } - } + for _, vm := range(vms) { + uuid := vm.ID.String() + resp, err := client.R().Put(host+"/vms/"+uuid+"/stop") + if err != nil { + u.PrintlnErr("Failed stopping VM \""+vm.Name+"\": "+err.Error()) + statusCode = 1 + } else { + if resp.IsSuccess() { + u.Println("Stopped VM \""+vm.Name+"\"") + } else { + u.PrintlnErr("Failed stopping VM \""+vm.Name+"\": "+resp.Status()+": "+resp.String()) + statusCode = 1 + } + } - } + } - return statusCode + return statusCode } diff --git a/src/cmdVersion/version.go b/src/cmdVersion/version.go index 4dba2e66aae87bc3d6dafc80e547efa188b23e21..d749e110f9924ffee710f424c237b9dd61fdbbc6 100644 --- a/src/cmdVersion/version.go +++ b/src/cmdVersion/version.go @@ -1,94 +1,94 @@ package cmdVersion import ( - "errors" - "encoding/json" - "nexus-client/version" - u "nexus-client/utils" - g "nexus-client/globals" + "errors" + "encoding/json" + "nexus-client/version" + u "nexus-client/utils" + g "nexus-client/globals" ) type Version struct { - Name string + Name string } func (cmd *Version)GetName() string { - return cmd.Name + return cmd.Name } func (cmd *Version)GetDesc() []string { - return []string{ - "Get nexus server's version."} + return []string{ + "Get nexus server's version."} } func (cmd *Version)PrintUsage() { - for _, desc := range cmd.GetDesc() { - u.PrintlnErr(desc) - } - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()) - u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + for _, desc := range cmd.GetDesc() { + u.PrintlnErr(desc) + } + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") + u.PrintlnErr("USAGE: "+cmd.GetName()) + u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") } func (cmd *Version)Run(args []string) int { - if len(args) > 0 { - cmd.PrintUsage() - return 1 - } + if len(args) > 0 { + cmd.PrintUsage() + return 1 + } - version, err := getVersion() - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return 1 - } + version, err := getVersion() + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return 1 + } - u.Println("server version: "+version) - return 0 + u.Println("server version: "+version) + return 0 } func getVersion() (string, error) { - client := g.GetInstance().Client - host := g.GetInstance().Host + client := g.GetInstance().Client + host := g.GetInstance().Host - resp, err := client.R().Get(host+"/version") - if err != nil { - return "", err - } else { - if resp.IsSuccess() { - type Response struct { - Version string - } - var response Response - err = json.Unmarshal(resp.Body(), &response) - if err != nil { - return "", err - } - return response.Version, nil - } else { - return "", errors.New(resp.Status()+": "+resp.String()) - } - } + resp, err := client.R().Get(host+"/version") + if err != nil { + return "", err + } else { + if resp.IsSuccess() { + type Response struct { + Version string + } + var response Response + err = json.Unmarshal(resp.Body(), &response) + if err != nil { + return "", err + } + return response.Version, nil + } else { + return "", errors.New(resp.Status()+": "+resp.String()) + } + } } // Checks the client version is compatible with the server's API. func CheckServerCompatibility(appname string) bool { - // Checks the client version is compatible with the server's API. - v, err := getVersion() - if err != nil { - u.PrintlnErr("Error: "+err.Error()) - return false - } - clientVersion := version.Get() - serverVersion, _ := version.FromString(v) - if !clientVersion.IsCompatible(serverVersion) { - u.PrintlnErr(appname+" version "+clientVersion.String()+" is not compatible with server version "+serverVersion.String()+".") - if clientVersion.IsOlder(serverVersion) { - u.PrintlnErr("Please upgrade "+appname+" to version "+serverVersion.StringWithoutBugfix()+".") - } else { - u.PrintlnErr("Please downgrade "+appname+" to version "+serverVersion.StringWithoutBugfix()+".") - } - return false - } + // Checks the client version is compatible with the server's API. + v, err := getVersion() + if err != nil { + u.PrintlnErr("Error: "+err.Error()) + return false + } + clientVersion := version.Get() + serverVersion, _ := version.FromString(v) + if !clientVersion.IsCompatible(serverVersion) { + u.PrintlnErr(appname+" version "+clientVersion.String()+" is not compatible with server version "+serverVersion.String()+".") + if clientVersion.IsOlder(serverVersion) { + u.PrintlnErr("Please upgrade "+appname+" to version "+serverVersion.StringWithoutBugfix()+".") + } else { + u.PrintlnErr("Please downgrade "+appname+" to version "+serverVersion.StringWithoutBugfix()+".") + } + return false + } - return true + return true } \ No newline at end of file diff --git a/src/defaults/Defaults.go b/src/defaults/Defaults.go index e4312c0734f0d00eab4339873af21fa229c99a1f..8f1d245ba3c26c4a146152db027c323714f1a28f 100644 --- a/src/defaults/Defaults.go +++ b/src/defaults/Defaults.go @@ -1,7 +1,7 @@ package defaults import ( - "os" + "os" ) import _ "embed" @@ -9,20 +9,20 @@ import _ "embed" var embeddedNexusCert string const ( - defaultServer = "10.136.26.127" // isc-nexus-prod - defaultPort = "1077" + defaultServer = "10.136.26.127" // isc-nexus-prod + defaultPort = "1077" - NexusServer string = defaultServer+":"+defaultPort + NexusServer string = defaultServer+":"+defaultPort ) func CreateCert() (string, error) { - f, err := os.CreateTemp("", "nexus-client-") - if err != nil { - return "", err - } - if _, err := f.Write([]byte(embeddedNexusCert)); err != nil { - return "", err - } - defer f.Close() - return f.Name(), nil + f, err := os.CreateTemp("", "nexus-client-") + if err != nil { + return "", err + } + if _, err := f.Write([]byte(embeddedNexusCert)); err != nil { + return "", err + } + defer f.Close() + return f.Name(), nil } \ No newline at end of file diff --git a/src/exec/RemoteViewer.go b/src/exec/RemoteViewer.go index 3aae24810ec4f2f06c9b8c3b54ec9835f11a1434..066ca36361c795e67e2f734c30ff9b40b1c1cda0 100644 --- a/src/exec/RemoteViewer.go +++ b/src/exec/RemoteViewer.go @@ -1,47 +1,47 @@ package exec import ( - "fmt" - "errors" - "os/exec" - "strings" - "strconv" - "runtime" - u "nexus-client/utils" + "fmt" + "errors" + "os/exec" + "strings" + "strconv" + "runtime" + u "nexus-client/utils" ) const ( - remoteViewerBinary = "remote-viewer" + remoteViewerBinary = "remote-viewer" ) func CheckRemoteViewer() error { - os := runtime.GOOS - output, err := exec.Command(remoteViewerBinary, "--version").Output() - if os == "linux" { - if err != nil { - return errors.New(remoteViewerBinary+" is required but not found. On Ubuntu/Debian, "+remoteViewerBinary+" is part of the virt-viewer package that can be installed with \"sudo apt-get install virt-viewer\".") - } - } else if os == "darwin" { - if err != nil { - return errors.New(remoteViewerBinary+" is required but not found. On OSX, "+remoteViewerBinary+" is part of the virt-viewer package from MacPorts that can be installed from here: https://ports.macports.org/port/virt-viewer/") - } - } else { // must be "windows" - if err != nil { - return errors.New(remoteViewerBinary+" is required but not found. Make sure to add it to the system PATH. On Windows, "+remoteViewerBinary+" is part of the virt-viewer MSI package that can be installed from here: https://virt-manager.org/download/") - } - } - out := string(output) - lines := strings.Split(out, "\n") - fields := strings.Split(lines[0], " ") - if len(fields) < 3 { - return errors.New("Failed extracting "+remoteViewerBinary+" version number!") - } - // This is what we should get if remote-viewer is working properly. - if fields[0] == remoteViewerBinary && fields[1] == "version" { - return nil - } - return errors.New("Failed extracting "+remoteViewerBinary+" version number!") + os := runtime.GOOS + output, err := exec.Command(remoteViewerBinary, "--version").Output() + if os == "linux" { + if err != nil { + return errors.New(remoteViewerBinary+" is required but not found. On Ubuntu/Debian, "+remoteViewerBinary+" is part of the virt-viewer package that can be installed with \"sudo apt-get install virt-viewer\".") + } + } else if os == "darwin" { + if err != nil { + return errors.New(remoteViewerBinary+" is required but not found. On OSX, "+remoteViewerBinary+" is part of the virt-viewer package from MacPorts that can be installed from here: https://ports.macports.org/port/virt-viewer/") + } + } else { // must be "windows" + if err != nil { + return errors.New(remoteViewerBinary+" is required but not found. Make sure to add it to the system PATH. On Windows, "+remoteViewerBinary+" is part of the virt-viewer MSI package that can be installed from here: https://virt-manager.org/download/") + } + } + out := string(output) + lines := strings.Split(out, "\n") + fields := strings.Split(lines[0], " ") + if len(fields) < 3 { + return errors.New("Failed extracting "+remoteViewerBinary+" version number!") + } + // This is what we should get if remote-viewer is working properly. + if fields[0] == remoteViewerBinary && fields[1] == "version" { + return nil + } + return errors.New("Failed extracting "+remoteViewerBinary+" version number!") } // Run remote-viewer. @@ -49,24 +49,24 @@ func CheckRemoteViewer() error { // port is the port the VM spice server is listening to. // pwd is the spice password to connect to the running VM. func RunRemoteViewer(hostname string, cert string, name string, port int, pwd string, fullscreen bool) { - portStr := strconv.Itoa(port) - spice := "spice://"+hostname+"?tls-port="+portStr+"&password="+pwd - spiceCert := "--spice-ca-file="+cert - spiceSecure := "--spice-secure-channels=all" - // To exit fullscreen mode, either: - // - move the mouse to the middle of the top of the screen. - // - use ctrl+F12 as specified below - // To activate kiosk mode, add these options: "-k --kiosk-quit=on-disconnect" - var c *exec.Cmd - if fullscreen { - c = exec.Command("remote-viewer", spice, spiceCert, spiceSecure, "-t", name, "--hotkeys=toggle-fullscreen=ctrl+f12", "-f") - } else { - c = exec.Command("remote-viewer", spice, spiceCert, spiceSecure, "-t", name, "--hotkeys=toggle-fullscreen=ctrl+f12") - } - stdoutStderr, err := c.CombinedOutput() - if err != nil { - u.PrintlnErr("Failed attaching to VM!") - output := fmt.Sprintf("[%s]", stdoutStderr) - u.PrintlnErr(output) - } + portStr := strconv.Itoa(port) + spice := "spice://"+hostname+"?tls-port="+portStr+"&password="+pwd + spiceCert := "--spice-ca-file="+cert + spiceSecure := "--spice-secure-channels=all" + // To exit fullscreen mode, either: + // - move the mouse to the middle of the top of the screen. + // - use ctrl+F12 as specified below + // To activate kiosk mode, add these options: "-k --kiosk-quit=on-disconnect" + var c *exec.Cmd + if fullscreen { + c = exec.Command("remote-viewer", spice, spiceCert, spiceSecure, "-t", name, "--hotkeys=toggle-fullscreen=ctrl+f12", "-f") + } else { + c = exec.Command("remote-viewer", spice, spiceCert, spiceSecure, "-t", name, "--hotkeys=toggle-fullscreen=ctrl+f12") + } + stdoutStderr, err := c.CombinedOutput() + if err != nil { + u.PrintlnErr("Failed attaching to VM!") + output := fmt.Sprintf("[%s]", stdoutStderr) + u.PrintlnErr(output) + } } diff --git a/src/globals/Globals.go b/src/globals/Globals.go index 0e7a4641d5017c236217e44dae63e5f6f022da2d..e5cd57adb5f495ccd6f19596164c8f9762769db8 100644 --- a/src/globals/Globals.go +++ b/src/globals/Globals.go @@ -1,29 +1,29 @@ package globals import ( - "github.com/go-resty/resty/v2" + "github.com/go-resty/resty/v2" ) type Globals struct { - Hostname string - Host string - PubCert string - Client *resty.Client + Hostname string + Host string + PubCert string + Client *resty.Client } const ( - ENV_NEXUS_SERVER = "NEXUS_SERVER" - ENV_NEXUS_TOKEN = "NEXUS_TOKEN" - ENV_NEXUS_CERT = "NEXUS_CERT" + ENV_NEXUS_SERVER = "NEXUS_SERVER" + ENV_NEXUS_TOKEN = "NEXUS_TOKEN" + ENV_NEXUS_CERT = "NEXUS_CERT" ) var globals *Globals func GetInstance() *Globals { - return globals + return globals } func Init(hostname, host, pubCert string, client *resty.Client) { - globals = &Globals{hostname, host, pubCert, client} + globals = &Globals{hostname, host, pubCert, client} } diff --git a/src/nexus-cli/nexus-cli.go b/src/nexus-cli/nexus-cli.go index f8570e3b7d741bf98eac75006c1eec20c893df11..dda4afe2a4abf8dfc02b0aac8fbded652c800597 100644 --- a/src/nexus-cli/nexus-cli.go +++ b/src/nexus-cli/nexus-cli.go @@ -1,156 +1,156 @@ package main import ( - "os" - "path" - "strings" - "syscall" - "os/signal" - u "nexus-client/utils" - g "nexus-client/globals" - "nexus-client/defaults" - "nexus-client/version" - "nexus-client/exec" - "nexus-client/cmd" - "nexus-client/cmdLogin" - "nexus-client/cmdToken" - "nexus-client/cmdUser" - "nexus-client/cmdVM" - "nexus-client/cmdVersion" - "nexus-client/cmdTemplate" - "github.com/go-resty/resty/v2" + "os" + "path" + "strings" + "syscall" + "os/signal" + u "nexus-client/utils" + g "nexus-client/globals" + "nexus-client/defaults" + "nexus-client/version" + "nexus-client/exec" + "nexus-client/cmd" + "nexus-client/cmdLogin" + "nexus-client/cmdToken" + "nexus-client/cmdUser" + "nexus-client/cmdVM" + "nexus-client/cmdVersion" + "nexus-client/cmdTemplate" + "github.com/go-resty/resty/v2" ) var cmdList = []cmd.Command { - &cmdLogin.Login{"login"}, - &cmdToken.Refresh{"refresh"}, - &cmdVersion.Version{"version"}, - - &cmdUser.Whoami{"whoami"}, - &cmdUser.UpdatePwd{"passwd"}, - &cmdUser.List{"userlist"}, - &cmdUser.Add{"usercreate"}, - &cmdUser.Del{"userdel"}, - &cmdUser.SetCaps{"usersetcaps"}, - - &cmdVM.List{"vmlist"}, - &cmdVM.Cred2pdf{"vmcred2pdf"}, - &cmdVM.Start{"vmstart"}, - &cmdVM.Stop{"vmkill"}, - &cmdVM.Shutdown{"vmshutdown"}, - &cmdVM.Reboot{"vmreboot"}, - &cmdVM.Attach{"vmattach"}, - &cmdVM.Create{"vmcreate"}, - &cmdVM.Edit{"vmedit"}, - &cmdVM.Del{"vmdel"}, - &cmdVM.AddAccess{"vmaddaccess"}, - &cmdVM.DelAccess{"vmdelaccess"}, - &cmdVM.ExportDir{"vmexportdir"}, - &cmdVM.ImportDir{"vmimportdir"}, - - &cmdTemplate.List{"tpllist"}, - &cmdTemplate.Create{"tplcreate"}, - &cmdTemplate.Edit{"tpledit"}, - &cmdTemplate.Del{"tpldel"}, - &cmdTemplate.ExportDisk{"tplexportdisk"}, + &cmdLogin.Login{"login"}, + &cmdToken.Refresh{"refresh"}, + &cmdVersion.Version{"version"}, + + &cmdUser.Whoami{"whoami"}, + &cmdUser.UpdatePwd{"passwd"}, + &cmdUser.List{"userlist"}, + &cmdUser.Add{"usercreate"}, + &cmdUser.Del{"userdel"}, + &cmdUser.SetCaps{"usersetcaps"}, + + &cmdVM.List{"vmlist"}, + &cmdVM.Cred2pdf{"vmcred2pdf"}, + &cmdVM.Start{"vmstart"}, + &cmdVM.Stop{"vmkill"}, + &cmdVM.Shutdown{"vmshutdown"}, + &cmdVM.Reboot{"vmreboot"}, + &cmdVM.Attach{"vmattach"}, + &cmdVM.Create{"vmcreate"}, + &cmdVM.Edit{"vmedit"}, + &cmdVM.Del{"vmdel"}, + &cmdVM.AddAccess{"vmaddaccess"}, + &cmdVM.DelAccess{"vmdelaccess"}, + &cmdVM.ExportDir{"vmexportdir"}, + &cmdVM.ImportDir{"vmimportdir"}, + + &cmdTemplate.List{"tpllist"}, + &cmdTemplate.Create{"tplcreate"}, + &cmdTemplate.Edit{"tpledit"}, + &cmdTemplate.Del{"tpldel"}, + &cmdTemplate.ExportDisk{"tplexportdisk"}, } func main() { - var appname = path.Base(os.Args[0]) - u.PrintlnErr(appname+" version "+version.Get().String()) - - if err := exec.CheckRemoteViewer(); err != nil { - u.PrintlnErr(err.Error()) - return - } - - serverEnvVar, found := os.LookupEnv(g.ENV_NEXUS_SERVER) - if !found { - serverEnvVar = defaults.NexusServer - // u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_SERVER+"\" must be set!") - // u.PrintlnErr("It defines the nexus server to connect to along the port number.") - // u.PrintlnErr("Example: export "+g.ENV_NEXUS_SERVER+"=192.168.1.42:1077") - // os.Exit(1) - } - - var err error - - certEnvVar, found := os.LookupEnv(g.ENV_NEXUS_CERT) - if !found { - certEnvVar, err = defaults.CreateCert() - if err != nil { - u.PrintlnErr("Failed creating certificate from embedded certificate: ") - return - } - defer os.Remove(certEnvVar) - - go func(certFile string) { - // Wait on dedicated signals. - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) - - for { - sig := <-sigs // blocks on any of the above signals. - u.Println("Caught signal ("+sig.String()+")") - break; - } - - os.Remove(certFile) - os.Exit(1) - }(certEnvVar) - - // u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_CERT+"\" must be set!") - // u.PrintlnErr("It specifies the path to the public certificate required for encrypted communications (TLS) with the nexus server.") - // u.PrintlnErr("Example: export "+g.ENV_NEXUS_CERT+"=ca-cert.pem") - // os.Exit(1) - } - - if !u.FileExists(certEnvVar) { - u.PrintlnErr("Failed reading certificate \""+certEnvVar+"\"!") - return - } - - parts := strings.Split(serverEnvVar, ":") - hostname := parts[0] - - client := resty.New() - client.SetRootCertificate(certEnvVar) - host := "https://"+serverEnvVar - - g.Init(hostname, host, certEnvVar, client) - - // Checks the client version is compatible with the server's API. - if !cmdVersion.CheckServerCompatibility(appname) { - return - } - - if len(os.Args) < 2 { - u.PrintlnErr("USAGE: "+appname+" CMD") - u.PrintlnErr("CMD is the Command to run. Except for \"login\", all Commands require an access token.") - u.PrintlnErr("All commands besides login read the access token from the env. variable \"NEXUS_TOKEN\".") - u.PrintlnErr("To obtain an access token, use the \"login\" command.") - u.PrintlnErr("List of supported Commands:") - cmd.Help(cmdList, " ") - return - } - - cmdName := os.Args[1] - cmdArgs := os.Args[2:] - - found, cmd := cmd.Match(cmdName, cmdList) - if found { - if cmdName != "login" { - token, found := os.LookupEnv(g.ENV_NEXUS_TOKEN) - if !found || len(token) == 0 { - u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_TOKEN+"\" must be set!") - os.Exit(1) - } - client.SetHeader("Content-Type", "application/json") - client.SetHeader("Authorization", "Bearer "+token) - } - cmd.Run(cmdArgs) - return - } - - u.PrintlnErr("Invalid Command") + var appname = path.Base(os.Args[0]) + u.PrintlnErr(appname+" version "+version.Get().String()) + + if err := exec.CheckRemoteViewer(); err != nil { + u.PrintlnErr(err.Error()) + return + } + + serverEnvVar, found := os.LookupEnv(g.ENV_NEXUS_SERVER) + if !found { + serverEnvVar = defaults.NexusServer + // u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_SERVER+"\" must be set!") + // u.PrintlnErr("It defines the nexus server to connect to along the port number.") + // u.PrintlnErr("Example: export "+g.ENV_NEXUS_SERVER+"=192.168.1.42:1077") + // os.Exit(1) + } + + var err error + + certEnvVar, found := os.LookupEnv(g.ENV_NEXUS_CERT) + if !found { + certEnvVar, err = defaults.CreateCert() + if err != nil { + u.PrintlnErr("Failed creating certificate from embedded certificate: ") + return + } + defer os.Remove(certEnvVar) + + go func(certFile string) { + // Wait on dedicated signals. + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) + + for { + sig := <-sigs // blocks on any of the above signals. + u.Println("Caught signal ("+sig.String()+")") + break; + } + + os.Remove(certFile) + os.Exit(1) + }(certEnvVar) + + // u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_CERT+"\" must be set!") + // u.PrintlnErr("It specifies the path to the public certificate required for encrypted communications (TLS) with the nexus server.") + // u.PrintlnErr("Example: export "+g.ENV_NEXUS_CERT+"=ca-cert.pem") + // os.Exit(1) + } + + if !u.FileExists(certEnvVar) { + u.PrintlnErr("Failed reading certificate \""+certEnvVar+"\"!") + return + } + + parts := strings.Split(serverEnvVar, ":") + hostname := parts[0] + + client := resty.New() + client.SetRootCertificate(certEnvVar) + host := "https://"+serverEnvVar + + g.Init(hostname, host, certEnvVar, client) + + // Checks the client version is compatible with the server's API. + if !cmdVersion.CheckServerCompatibility(appname) { + return + } + + if len(os.Args) < 2 { + u.PrintlnErr("USAGE: "+appname+" CMD") + u.PrintlnErr("CMD is the Command to run. Except for \"login\", all Commands require an access token.") + u.PrintlnErr("All commands besides login read the access token from the env. variable \"NEXUS_TOKEN\".") + u.PrintlnErr("To obtain an access token, use the \"login\" command.") + u.PrintlnErr("List of supported Commands:") + cmd.Help(cmdList, " ") + return + } + + cmdName := os.Args[1] + cmdArgs := os.Args[2:] + + found, cmd := cmd.Match(cmdName, cmdList) + if found { + if cmdName != "login" { + token, found := os.LookupEnv(g.ENV_NEXUS_TOKEN) + if !found || len(token) == 0 { + u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_TOKEN+"\" must be set!") + os.Exit(1) + } + client.SetHeader("Content-Type", "application/json") + client.SetHeader("Authorization", "Bearer "+token) + } + cmd.Run(cmdArgs) + return + } + + u.PrintlnErr("Invalid Command") } \ No newline at end of file diff --git a/src/nexus-exam/nexus-exam.go b/src/nexus-exam/nexus-exam.go index c690739e8d2040cf563ab92d0699c6fca8d385f4..dcc7797ee07203eea5aef2ddc9d9059d001f0b21 100644 --- a/src/nexus-exam/nexus-exam.go +++ b/src/nexus-exam/nexus-exam.go @@ -1,83 +1,83 @@ package main import ( - "os" - "errors" - "strconv" - "nexus-client/exec" - u "nexus-client/utils" - g "nexus-client/globals" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/theme" - "fyne.io/fyne/v2/widget" - "fyne.io/fyne/v2/container" + "os" + "errors" + "strconv" + "nexus-client/exec" + u "nexus-client/utils" + g "nexus-client/globals" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "fyne.io/fyne/v2/container" ) const ( - windowTitle = "Live Exam" + windowTitle = "Live Exam" ) func main() { - if err := exec.CheckRemoteViewer(); err != nil { - u.PrintlnErr(err.Error()) - os.Exit(1) - } + if err := exec.CheckRemoteViewer(); err != nil { + u.PrintlnErr(err.Error()) + os.Exit(1) + } - server, found := os.LookupEnv(g.ENV_NEXUS_SERVER) - if !found { - u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_SERVER+"\" must be set!") - u.PrintlnErr("It defines the nexus server to connect to.") - u.PrintlnErr("Example: export "+g.ENV_NEXUS_SERVER+"=192.168.1.42") - os.Exit(1) - } + server, found := os.LookupEnv(g.ENV_NEXUS_SERVER) + if !found { + u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_SERVER+"\" must be set!") + u.PrintlnErr("It defines the nexus server to connect to.") + u.PrintlnErr("Example: export "+g.ENV_NEXUS_SERVER+"=192.168.1.42") + os.Exit(1) + } - pubCert, found := os.LookupEnv(g.ENV_NEXUS_CERT) - if !found { - u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_CERT+"\" must be set!") - u.PrintlnErr("It specifies the path to the public certificate required for encrypted communications (TLS) with the nexus server.") - u.PrintlnErr("Example: export "+g.ENV_NEXUS_CERT+"=ca-cert.pem") - os.Exit(1) - } + pubCert, found := os.LookupEnv(g.ENV_NEXUS_CERT) + if !found { + u.PrintlnErr("Environment variable \""+g.ENV_NEXUS_CERT+"\" must be set!") + u.PrintlnErr("It specifies the path to the public certificate required for encrypted communications (TLS) with the nexus server.") + u.PrintlnErr("Example: export "+g.ENV_NEXUS_CERT+"=ca-cert.pem") + os.Exit(1) + } - if !u.FileExists(pubCert) { - u.PrintlnErr("Failed reading certificate \""+pubCert+"\"!") - os.Exit(1) - } + if !u.FileExists(pubCert) { + u.PrintlnErr("Failed reading certificate \""+pubCert+"\"!") + os.Exit(1) + } - app := app.New() - app.Settings().SetTheme(theme.LightTheme()) + app := app.New() + app.Settings().SetTheme(theme.LightTheme()) - win := app.NewWindow(windowTitle) + win := app.NewWindow(windowTitle) - portEntry := widget.NewEntry() - portEntry.Validator = func(val string) error { - port, err := strconv.Atoi(val) - if err != nil { - return errors.New("Please enter an integer") - } - if port < 1024 || port > 65535 { - return errors.New("Please enter a value within [1024,65535]") - } - return nil - } + portEntry := widget.NewEntry() + portEntry.Validator = func(val string) error { + port, err := strconv.Atoi(val) + if err != nil { + return errors.New("Please enter an integer") + } + if port < 1024 || port > 65535 { + return errors.New("Please enter a value within [1024,65535]") + } + return nil + } - pwdEntry := widget.NewEntry() - pwdEntry.Password = true + pwdEntry := widget.NewEntry() + pwdEntry.Password = true - label := widget.NewLabel("Enter port and password to connect to your VM:") - - form := &widget.Form{ - Items: []*widget.FormItem { - {Text: "Port", Widget: portEntry}, - { Text: "Password", Widget: pwdEntry}, - }, - OnSubmit: func() { - port, _ := strconv.Atoi(portEntry.Text) - exec.RunRemoteViewer(server, pubCert, windowTitle, port, pwdEntry.Text, true) - }, - SubmitText: "Connect", - } + label := widget.NewLabel("Enter port and password to connect to your VM:") - win.SetContent(container.NewPadded(container.NewVBox(label, form))) - win.ShowAndRun() + form := &widget.Form{ + Items: []*widget.FormItem { + {Text: "Port", Widget: portEntry}, + { Text: "Password", Widget: pwdEntry}, + }, + OnSubmit: func() { + port, _ := strconv.Atoi(portEntry.Text) + exec.RunRemoteViewer(server, pubCert, windowTitle, port, pwdEntry.Text, true) + }, + SubmitText: "Connect", + } + + win.SetContent(container.NewPadded(container.NewVBox(label, form))) + win.ShowAndRun() } diff --git a/src/nexus-gui/nexus-gui.go b/src/nexus-gui/nexus-gui.go index ddcce996f0d395a5b04845707b695e1b1e3b45d0..cc2f2975f57e0556167a82db9280fb686e483075 100644 --- a/src/nexus-gui/nexus-gui.go +++ b/src/nexus-gui/nexus-gui.go @@ -1,63 +1,63 @@ package main import ( - "log" - //"fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/widget" + "log" + //"fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" ) func main() { - myapp := app.New() - win := myapp.NewWindow("nexus-client") - - vm1 := widget.NewLabel("Xubuntu 20.04") - vm1Status := widget.NewLabel("stopped") - vm2 := widget.NewLabel("Debian 11") - vm2Status := widget.NewLabel("stopped") - - win.SetContent( - container.NewHBox( - container.NewVBox( - container.NewHBox( - vm1, - vm1Status, - widget.NewButton("Start", func() { - vm1Status.SetText("started...") - }), - widget.NewButton("Stop", func() { - vm1Status.SetText("stopped") - }), - ), - container.NewHBox( - vm2, - vm2Status, - widget.NewButton("Start", func() { - vm2Status.SetText("started...") - }), - widget.NewButton("Stop", func() { - vm2Status.SetText("stopped") - }), - ), - ), - ), - ) - - win.ShowAndRun() + myapp := app.New() + win := myapp.NewWindow("nexus-client") + + vm1 := widget.NewLabel("Xubuntu 20.04") + vm1Status := widget.NewLabel("stopped") + vm2 := widget.NewLabel("Debian 11") + vm2Status := widget.NewLabel("stopped") + + win.SetContent( + container.NewHBox( + container.NewVBox( + container.NewHBox( + vm1, + vm1Status, + widget.NewButton("Start", func() { + vm1Status.SetText("started...") + }), + widget.NewButton("Stop", func() { + vm1Status.SetText("stopped") + }), + ), + container.NewHBox( + vm2, + vm2Status, + widget.NewButton("Start", func() { + vm2Status.SetText("started...") + }), + widget.NewButton("Stop", func() { + vm2Status.SetText("stopped") + }), + ), + ), + ), + ) + + win.ShowAndRun() } func mainChecks() { - myApp := app.New() - myWindow := myApp.NewWindow("Choice Widgets") - - check1 := widget.NewCheck("Opt1", func(value bool) { - log.Println("Opt1 set to", value) - }) - check2 := widget.NewCheck("Opt2", func(value bool) { - log.Println("Opt2 set to", value) - }) - - myWindow.SetContent(container.NewVBox(check1, check2)) - myWindow.ShowAndRun() + myApp := app.New() + myWindow := myApp.NewWindow("Choice Widgets") + + check1 := widget.NewCheck("Opt1", func(value bool) { + log.Println("Opt1 set to", value) + }) + check2 := widget.NewCheck("Opt2", func(value bool) { + log.Println("Opt2 set to", value) + }) + + myWindow.SetContent(container.NewVBox(check1, check2)) + myWindow.ShowAndRun() } \ No newline at end of file diff --git a/src/utils/csv.go b/src/utils/csv.go index 6c98c164795cc957b9dc13c622cad923bb78b014..552f5f3d83860fa01c32a793cb5dc29100576e4e 100644 --- a/src/utils/csv.go +++ b/src/utils/csv.go @@ -1,36 +1,36 @@ package utils import ( - "os" - "io" - "errors" - "encoding/csv" + "os" + "io" + "errors" + "encoding/csv" ) // Returns the specified column, as a slice of strings, from a CSV file. // Columns are numbered starting at 0. func ReadCSVColumn(csvFile string, column int) ([]string, error) { - file, err := os.Open(csvFile) + file, err := os.Open(csvFile) if err != nil { return nil, errors.New("Error: "+err.Error()) } - defer file.Close() + defer file.Close() reader := csv.NewReader(file) - var entries []string - for { - record, err := reader.Read() - if err == io.EOF { - break - } - if err != nil { - return nil, errors.New("Failed reading "+err.Error()) - } - if column >= len(record) { - return nil, errors.New("Failed reading \""+csvFile+"\": column out of bound") - } - entries = append(entries, record[column]) - } + var entries []string + for { + record, err := reader.Read() + if err == io.EOF { + break + } + if err != nil { + return nil, errors.New("Failed reading "+err.Error()) + } + if column >= len(record) { + return nil, errors.New("Failed reading \""+csvFile+"\": column out of bound") + } + entries = append(entries, record[column]) + } - return entries, nil + return entries, nil } diff --git a/src/utils/utils.go b/src/utils/utils.go index 45504047de0f93b3ed96df245ecb9ab0098704a9..5f9d2d1b39c5cd0ff4f9460103428a91f4e64337 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -1,44 +1,44 @@ package utils import ( - "os" - "io" - "fmt" - "io/fs" - "bytes" - "errors" - "net/mail" - "archive/tar" - "path/filepath" - "compress/gzip" - "github.com/google/uuid" + "os" + "io" + "fmt" + "io/fs" + "bytes" + "errors" + "net/mail" + "archive/tar" + "path/filepath" + "compress/gzip" + "github.com/google/uuid" ) func Print(a ...any) { - os.Stdout.WriteString(fmt.Sprint(a ...)) + os.Stdout.WriteString(fmt.Sprint(a ...)) } func Println(a ...any) { - Print(a...) - Print("\n") + Print(a...) + Print("\n") } func PrintErr(a ...any) { - os.Stderr.WriteString(fmt.Sprint(a ...)) + os.Stderr.WriteString(fmt.Sprint(a ...)) } func PrintlnErr(a ...any) { - PrintErr(a...) - PrintErr("\n") + PrintErr(a...) + PrintErr("\n") } // Returns true if the specified file exists, false otherwise. func FileExists(filename string) bool { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() } // Creates a tar.gz archive of dir and all its files and subdirectories. @@ -46,69 +46,69 @@ func FileExists(filename string) bool { // Source code slightly modified from: https://gist.github.com/mimoo/25fc9716e0f1353791f5908f94d6e726 func TarGzDir(dir, archive string) error { _, err := os.Stat(dir) - if errors.Is(err, fs.ErrNotExist) { - return errors.New("Error: \""+dir+"\" does not exist"); - } - - var buf bytes.Buffer - - gzWriter := gzip.NewWriter(&buf) - tarWriter := tar.NewWriter(gzWriter) - - // Walks through every file in the directory - filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error { - header, err := tar.FileInfoHeader(fi, file) - if err != nil { - return err - } - - header.Name = filepath.ToSlash(file) - - if err := tarWriter.WriteHeader(header); err != nil { - return err - } - // if not a dir, write file content - if !fi.IsDir() { - data, err := os.Open(file) - if err != nil { - return err - } - if _, err := io.Copy(tarWriter, data); err != nil { - return err - } - } - return nil - }) - - if err := tarWriter.Close(); err != nil { - return err - } - if err := gzWriter.Close(); err != nil { - return err - } - - fileToWrite, err := os.OpenFile(archive, os.O_CREATE|os.O_RDWR, os.FileMode(0750)) - if err != nil { - return err - } - if _, err := io.Copy(fileToWrite, &buf); err != nil { - return err - } - - return nil + if errors.Is(err, fs.ErrNotExist) { + return errors.New("Error: \""+dir+"\" does not exist"); + } + + var buf bytes.Buffer + + gzWriter := gzip.NewWriter(&buf) + tarWriter := tar.NewWriter(gzWriter) + + // Walks through every file in the directory + filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error { + header, err := tar.FileInfoHeader(fi, file) + if err != nil { + return err + } + + header.Name = filepath.ToSlash(file) + + if err := tarWriter.WriteHeader(header); err != nil { + return err + } + // if not a dir, write file content + if !fi.IsDir() { + data, err := os.Open(file) + if err != nil { + return err + } + if _, err := io.Copy(tarWriter, data); err != nil { + return err + } + } + return nil + }) + + if err := tarWriter.Close(); err != nil { + return err + } + if err := gzWriter.Close(); err != nil { + return err + } + + fileToWrite, err := os.OpenFile(archive, os.O_CREATE|os.O_RDWR, os.FileMode(0750)) + if err != nil { + return err + } + if _, err := io.Copy(fileToWrite, &buf); err != nil { + return err + } + + return nil } func GetRandomTempFilename() (string, error) { - tempDir := os.TempDir() - uuid, err := uuid.NewRandom() - if err != nil { - return "", errors.New("Failed creating random UUID: "+err.Error()) - } - randName := "temp_"+uuid.String() - return filepath.Join(tempDir, randName), nil + tempDir := os.TempDir() + uuid, err := uuid.NewRandom() + if err != nil { + return "", errors.New("Failed creating random UUID: "+err.Error()) + } + randName := "temp_"+uuid.String() + return filepath.Join(tempDir, randName), nil } func IsEmail(email string) bool { _, err := mail.ParseAddress(email) - return err == nil + return err == nil } diff --git a/src/version/version.go b/src/version/version.go index dd0aa763e899ef432e9ed69b4d6b808cec0dc57c..513ed595269292226bb8a787118bb80a018f0bac 100644 --- a/src/version/version.go +++ b/src/version/version.go @@ -1,80 +1,80 @@ package version import ( - "strings" - "strconv" + "strings" + "strconv" ) const ( - major = 1 - minor = 5 - bugfix = 3 + major = 1 + minor = 5 + bugfix = 3 ) type Version struct { - major int - minor int - bugfix int + major int + minor int + bugfix int } var version Version = FromInt(major, minor, bugfix) func FromInt(major, minor, bugfix int) Version { - return Version{major, minor, bugfix} + return Version{major, minor, bugfix} } // Format: "x.y.z" func FromString(s string) (Version, error) { - f := strings.Split(s, ".") - major, _ := strconv.Atoi(f[0]) - minor, _ := strconv.Atoi(f[1]) - bugfix, _ := strconv.Atoi(f[2]) - return Version{major, minor, bugfix}, nil + f := strings.Split(s, ".") + major, _ := strconv.Atoi(f[0]) + minor, _ := strconv.Atoi(f[1]) + bugfix, _ := strconv.Atoi(f[2]) + return Version{major, minor, bugfix}, nil } func Get() Version { - return version + return version } func (v Version) String() string { - return strconv.Itoa(v.major)+"."+strconv.Itoa(v.minor)+"."+strconv.Itoa(v.bugfix) + return strconv.Itoa(v.major)+"."+strconv.Itoa(v.minor)+"."+strconv.Itoa(v.bugfix) } func (v Version) StringWithoutBugfix() string { - return strconv.Itoa(v.major)+"."+strconv.Itoa(v.minor)+".x" + return strconv.Itoa(v.major)+"."+strconv.Itoa(v.minor)+".x" } // Returns true if v is older than candidate func (v Version) IsOlder(candidate Version) bool { - v1 := v.getMajorMinor() - v2 := candidate.getMajorMinor() - if v1 > v2 { - return false - } else if v1 < v2 { - return true - } else { - bugfix1 := v.getBugfix() - bugfix2 := candidate.getBugfix() - return bugfix1 < bugfix2 - } + v1 := v.getMajorMinor() + v2 := candidate.getMajorMinor() + if v1 > v2 { + return false + } else if v1 < v2 { + return true + } else { + bugfix1 := v.getBugfix() + bugfix2 := candidate.getBugfix() + return bugfix1 < bugfix2 + } } // Returns true if v is compatible with candidate func (v Version) IsCompatible(candidate Version) bool { - v1 := v.getMajorMinor() - v2 := candidate.getMajorMinor() - return v1 == v2 + v1 := v.getMajorMinor() + v2 := candidate.getMajorMinor() + return v1 == v2 } func (v Version) getMajorMinorAsString() string { - return strconv.Itoa(v.major)+strconv.Itoa(v.minor) + return strconv.Itoa(v.major)+strconv.Itoa(v.minor) } func (v Version) getMajorMinor() int { - n, _ := strconv.Atoi(v.getMajorMinorAsString()) - return n + n, _ := strconv.Atoi(v.getMajorMinorAsString()) + return n } func (v Version) getBugfix() int { - return v.bugfix + return v.bugfix }