diff --git a/client/cmdVM/vmAttachSync.go b/client/cmdVM/vmAttachSync.go new file mode 100644 index 0000000000000000000000000000000000000000..193fc4f49df37680b8fcef92b94f49bc85418a8a --- /dev/null +++ b/client/cmdVM/vmAttachSync.go @@ -0,0 +1,63 @@ +package cmdVM + +import ( + "sync" + + u "gitedu.hesge.ch/flg_projects/nexus_vdi/nexus/client/utils" + nc "gitedu.hesge.ch/flg_projects/nexus_vdi/nexus/libclient/nexusclient" +) + +type AttachSync struct { + Name string +} + +func (cmd *AttachSync) GetName() string { + return cmd.Name +} + +func (cmd *AttachSync) GetDesc() []string { + return []string{ + "Attaches to one or more VMs in order to use their desktop environment.", + "If not the VM's owner: requires VM_ATTACH VM access capability or VM_ATTACH_ANY user capability."} +} + +func (cmd *AttachSync) PrintUsage() { + printRegexUsage(cmd) + printRegexUsageDetails() +} + +func (cmd *AttachSync) Run(nc *nc.NexusClient, args []string) int { + argc := len(args) + if argc < 1 { + cmd.PrintUsage() + return 1 + } + + credsList, err := getFilteredAttachVMsCreds(nc, args) + if err != nil { + u.PrintlnErr(err) + return 1 + } + + if len(credsList) == 0 { + u.PrintlnErr("No matching VMs.") + } + + // Use wait groups to wait until all viewers threads have completed. + var wg sync.WaitGroup + wg.Add(len(credsList)) + + for _, creds := range credsList { + go func() { + err := nc.VMAttachFromCreds(creds, false) + if err != nil { + u.PrintlnErr("Failed attach to VM: " + err.Error()) + } + wg.Done() + }() + } + + wg.Wait() + + return 0 +} diff --git a/client/nexus-cli/nexus-cli.go b/client/nexus-cli/nexus-cli.go index 3c33d28b3cd61557a2cec4e5bfa6b5de1cbd3cd1..2ba93cf86b81c48c338ecd86085fcbd2a8c3b993 100644 --- a/client/nexus-cli/nexus-cli.go +++ b/client/nexus-cli/nexus-cli.go @@ -49,7 +49,7 @@ var cmdList = []cmd.Command{ &cmdMisc.HelpHeader{Name: "!â•â•â•â•â•â•¡ VM commands ╞â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•"}, &cmdVM.AddAccess{Name: "vmaddaccess"}, - &cmdVM.Attach{Name: "vmattach"}, + &cmdVM.AttachSync{Name: "vmattach"}, // &cmdVM.AttachSingle{Name: "vmattachsingle"}, // for testing the route only // &cmdVM.AttachFromPwd{Name: "vmattachfrompwd"}, // for testing the route only &cmdVM.Create{Name: "vmcreate"}, @@ -119,7 +119,7 @@ func run() int { found, cmd := cmd.Match(cmdName, cmdList) if found { - exitFn = func() { nc.Close() } + exitFn = func() { nc.Cleanup() } // This thread acts as a signal handler for SIGINT or SIGTERM. // When one of these signals is received, the temporary certificate file is deleted. diff --git a/client/nexus-exam/nexus-exam.go b/client/nexus-exam/nexus-exam.go index 62283c7592fa2adedda249631b64947bf6f831d7..1e2b026ca747e974895c011934f29d4cb42c92c2 100644 --- a/client/nexus-exam/nexus-exam.go +++ b/client/nexus-exam/nexus-exam.go @@ -48,8 +48,8 @@ func exit(code int) { os.Exit(code) } -func attachVM(parent fyne.Window, certificateFile string, pwd string) { - err := client.VMAttachFromPwd(pwd, certificateFile, true) +func attachVM(parent fyne.Window, pwd string) { + err := client.VMAttachFromPwd(pwd, true) if err != nil { errorPopup(parent, "Failed attaching to VM: "+err.Error()) return @@ -102,7 +102,6 @@ func refreshToken(parent fyne.Window) { for { if isServerUp { if !isAuthenticated { - // Logins and obtains a JWT token. u.Println(time.Now(), ": initiate authentication") _, err = client.Authenticate(nexus_exam_user, nexus_exam_pwd) if err != nil { @@ -118,7 +117,7 @@ func refreshToken(parent fyne.Window) { } } else { u.Println(time.Now(), ": attempt to refresh token") - client.RefreshAuthToken() + client.RefreshToken() if err != nil { statusLabel.SetText("ERROR: failed to refresh token") u.PrintlnErr(time.Now(), ": failed to refresh token: "+err.Error()) @@ -164,11 +163,7 @@ func run() int { abortWindow(err.Error()) } - certFile, err := client.GetVMAttachCertificate() - if err != nil { - abortWindow(err.Error()) - } - exitFn = func() { os.Remove(certFile) } + exitFn = func() { client.Cleanup() } // This thread acts as a signal handler for SIGINT or SIGTERM. // When one of these signals is received, the temporary certificate file is deleted. @@ -212,7 +207,7 @@ func run() int { {Text: "Password", Widget: pwdEntry}, }, OnSubmit: func() { - attachVM(win, certFile, pwdEntry.Text) + attachVM(win, pwdEntry.Text) }, SubmitText: "Connect", } diff --git a/client/nexush/nexush.go b/client/nexush/nexush.go index 5adbdf96870a64381f9eb6cd138c76043c4a0034..2f4755e8c3aa0ae948c6d9a6f2b0ef8aee4fa7b4 100644 --- a/client/nexush/nexush.go +++ b/client/nexush/nexush.go @@ -138,7 +138,7 @@ func run() int { return 1 } - exitFn = func() { nc.Close() } + exitFn = func() { nc.Cleanup() } // This thread acts as a signal handler for SIGINT or SIGTERM. // When one of these signals is received, the temporary certificate file is deleted. diff --git a/libclient/client.go b/libclient/client.go index adfa41fd04e092b2e64cfeb3a8b57b366ff31275..0ffbd6fdad878de651bdc700c0b9974cf3a858d5 100644 --- a/libclient/client.go +++ b/libclient/client.go @@ -48,7 +48,7 @@ func New(host string) (*NexusClient, error) { // Authenticate a user. // curl --cacert ca-cert.pem -X POST https://localhost:8000/login -H 'Content-Type: application/json' -d '{"email": "johndoe@nexus.org", "pwd":"pipomolo"}' // Returns an JWT token if authentication succeeded or an error if it failed. -// IMPORTANT: caller MUST call the Close() function before exiting! +// IMPORTANT: caller MUST call the Cleanup() function before exiting! func (nc *NexusClient) Authenticate(user, pwd string) (string, error) { loginArgs := ¶ms.Login{user, pwd} resp, err := nc.client.R().SetBody(loginArgs).Post("/login") @@ -76,7 +76,7 @@ func (nc *NexusClient) Authenticate(user, pwd string) (string, error) { // "Authenticate" by using an already established connection using a previously received token. // Useful when wanting to access the API without authenticating. -// IMPORTANT: caller MUST call the Close() function before exiting! +// IMPORTANT: caller MUST call the Cleanup() function before exiting! func (nc *NexusClient) AuthenticateWithToken(token string) error { nc.client.SetAuthToken(token) // Download the CA certificate required by Spice clients @@ -88,9 +88,9 @@ func (nc *NexusClient) AuthenticateWithToken(token string) error { return nil } -// Releases/deletes resources allocated by Authenticate and AuthenticateWithToken. +// Cleanup resources allocated by Authenticate and AuthenticateWithToken. // IMPORTANT: caller MUST call this function before exiting! -func (nc *NexusClient) Close() { +func (nc *NexusClient) Cleanup() { if nc.certFile != "" { os.Remove(nc.certFile) } @@ -135,6 +135,8 @@ func (nc *NexusClient) getVMAttachCertFile() string { // The certificate file is saved in the OS' temporary directory. // Its name is randomly generated and garanteed to be unique. func (nc *NexusClient) downloadVMAttachCert() (string, error) { + nc.Cleanup() + uniqPostfix, err := uuid.NewRandom() if err != nil { return "", errors.New("Failed saving CA certificate, code 1:\n" + err.Error())