Use go module as dependency tool instead of glide
[src/xds/xds-agent.git] / lib / agent / apiv1-exec.go
1 /*
2  * Copyright (C) 2017-2018 "IoT.bzh"
3  * Author Sebastien Douheret <sebastien@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package agent
19
20 import (
21         "net/http"
22
23         "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1"
24         common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
25         "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
26         "github.com/franciscocpg/reflectme"
27         "github.com/gin-gonic/gin"
28         uuid "github.com/satori/go.uuid"
29 )
30
31 // ExecCmd executes remotely a command
32 func (s *APIService) execCmd(c *gin.Context) {
33
34         args := xaapiv1.ExecArgs{}
35         if err := c.BindJSON(&args); err != nil {
36                 s.Log.Warningf("/exec invalid args, err=%v", err)
37                 common.APIError(c, "Invalid arguments")
38                 return
39         }
40
41         // First get Project ID to retrieve Server ID and send command to right server
42         iid := c.Param("id")
43         if iid == "" {
44                 iid = args.ID
45         }
46         id, err := s.projects.ResolveID(iid)
47         if err != nil {
48                 common.APIError(c, err.Error())
49                 return
50         }
51         prj := s.projects.Get(id)
52         if prj == nil {
53                 common.APIError(c, "Unknown id")
54                 return
55         }
56
57         svr := (*prj).GetServer()
58         if svr == nil {
59                 common.APIError(c, "Cannot identify XDS Server")
60                 return
61         }
62
63         // Retrieve session info
64         sess := s.sessions.Get(c)
65         if sess == nil {
66                 common.APIError(c, "Unknown sessions")
67                 return
68         }
69         sock := sess.IOSocket
70         if sock == nil {
71                 common.APIError(c, "Websocket not established")
72                 return
73         }
74
75         // Forward input events from client to XDSServer through WS
76         // TODO use XDSServer events names definition
77         evtInList := []string{
78                 xaapiv1.ExecInEvent,
79                 xaapiv1.ExecInferiorInEvent,
80         }
81         for _, evName := range evtInList {
82                 evN := evName
83                 err := (*sock).On(evN, func(stdin string) {
84                         s.LogSillyf("EXEC EVENT IN (%s) <<%v>>", evN, stdin)
85                         svr.EventEmit(evN, stdin)
86                 })
87                 if err != nil {
88                         msgErr := "Error while registering WS for " + evN + " event"
89                         s.Log.Errorf(msgErr, ", err: %v", err)
90                         common.APIError(c, msgErr)
91                         return
92                 }
93         }
94
95         // Forward output events from XDSServer to client through WS
96         // TODO use XDSServer events names definition
97         var fwdFuncID []uuid.UUID
98         evtOutList := []string{
99                 xaapiv1.ExecOutEvent,
100                 xaapiv1.ExecInferiorOutEvent,
101         }
102         for _, evName := range evtOutList {
103                 evN := evName
104                 fwdFunc := func(pData interface{}, evData interface{}) error {
105                         sid := pData.(string)
106                         // IO socket can be nil when disconnected
107                         so := s.sessions.IOSocketGet(sid)
108                         if so == nil {
109                                 s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
110                                 return nil
111                         }
112
113                         // Add sessionID to event Data
114                         reflectme.SetField(evData, "sessionID", sid)
115
116                         s.LogSillyf("EXEC EVENT OUT (%s) <<%v>>", evN, evData)
117
118                         // Forward event to Client/Dashboard
119                         (*so).Emit(evN, evData)
120                         return nil
121                 }
122                 id, err := svr.EventOn(evN, sess.ID, fwdFunc)
123                 if err != nil {
124                         common.APIError(c, err.Error())
125                         return
126                 }
127                 fwdFuncID = append(fwdFuncID, id)
128         }
129
130         // Handle Exit event separately to cleanup registered listener
131         var exitFuncID uuid.UUID
132         exitFunc := func(privD interface{}, evData interface{}) error {
133                 evN := xaapiv1.ExecExitEvent
134
135                 pData := privD.(map[string]string)
136                 sid := pData["sessID"]
137                 prjID := pData["prjID"]
138
139                 // Add sessionID to event Data
140                 reflectme.SetField(evData, "sessionID", sid)
141
142                 // IO socket can be nil when disconnected
143                 so := s.sessions.IOSocketGet(sid)
144                 if so != nil {
145                         (*so).Emit(evN, evData)
146                 } else {
147                         s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
148                 }
149
150                 prj := s.projects.Get(prjID)
151                 if prj != nil {
152                         evD := evData.(map[string]interface{})
153                         cmdIDData, cmdIDExist := evD["cmdID"]
154                         svr := (*prj).GetServer()
155                         if svr != nil && cmdIDExist {
156                                 svr.CommandDelete(cmdIDData.(string))
157                         } else {
158                                 s.Log.Infof("%s: cannot retrieve server for sid=%s, prjID=%s, evD=%v", evN, sid, prjID, evD)
159                         }
160                 } else {
161                         s.Log.Infof("%s: cannot retrieve project for sid=%s, prjID=%s", evN, sid, prjID)
162                 }
163
164                 // cleanup listener
165                 for i, evName := range evtOutList {
166                         svr.EventOff(evName, fwdFuncID[i])
167                 }
168                 svr.EventOff(evN, exitFuncID)
169
170                 return nil
171         }
172
173         prjCfg := (*prj).GetProject()
174         privData := map[string]string{"sessID": sess.ID, "prjID": prjCfg.ID}
175         exitFuncID, err = svr.EventOn(xaapiv1.ExecExitEvent, privData, exitFunc)
176         if err != nil {
177                 common.APIError(c, err.Error())
178                 return
179         }
180
181         // Forward back command to right server
182         res := xsapiv1.ExecResult{}
183         xsArgs := &xsapiv1.ExecArgs{
184                 ID:               args.ID,
185                 SdkID:            args.SdkID,
186                 CmdID:            args.CmdID,
187                 Cmd:              args.Cmd,
188                 Args:             args.Args,
189                 Env:              args.Env,
190                 RPath:            args.RPath,
191                 TTY:              args.TTY,
192                 TTYGdbserverFix:  args.TTYGdbserverFix,
193                 LdLibPathNoReset: args.LdLibPathNoReset,
194                 ExitImmediate:    args.ExitImmediate,
195                 CmdTimeout:       args.CmdTimeout,
196         }
197         if err := svr.CommandExec(xsArgs, &res); err != nil {
198                 common.APIError(c, err.Error())
199                 return
200         }
201
202         // Add command to running commands list
203         if err := svr.CommandAdd(res.CmdID, xsArgs); err != nil {
204                 common.APIError(c, err.Error())
205                 return
206         }
207
208         c.JSON(http.StatusOK, xaapiv1.ExecResult{Status: res.Status, CmdID: res.CmdID})
209 }
210
211 // execSignalCmd executes remotely the signal command
212 func (s *APIService) execSignalCmd(c *gin.Context) {
213
214         args := xaapiv1.ExecSignalArgs{}
215         if err := c.BindJSON(&args); err != nil {
216                 s.Log.Warningf("/signal invalid args, err=%v", err)
217                 common.APIError(c, "Invalid arguments")
218                 return
219         }
220
221         // Retrieve on which xds-server the command is running
222         var svr *XdsServer
223         var dataCmd interface{}
224         for _, svr = range s.xdsServers {
225                 dataCmd = svr.CommandGet(args.CmdID)
226                 if dataCmd != nil {
227                         break
228                 }
229         }
230         if dataCmd == nil {
231                 common.APIError(c, "Cannot retrieve XDS Server for this cmdID")
232                 return
233         }
234
235         // Forward back command to right server
236         res := xsapiv1.ExecSigResult{}
237         xsArgs := &xsapiv1.ExecSignalArgs{
238                 CmdID:  args.CmdID,
239                 Signal: args.Signal,
240         }
241         if err := svr.CommandSignal(xsArgs, &res); err != nil {
242                 common.APIError(c, err.Error())
243                 return
244         }
245
246         c.JSON(http.StatusOK, xaapiv1.ExecSignalResult{Status: res.Status, CmdID: res.CmdID})
247 }