Fixed events definition and callback processing.
[src/xds/xds-agent.git] / lib / agent / apiv1-exec.go
1 package agent
2
3 import (
4         "encoding/json"
5         "io/ioutil"
6         "net/http"
7
8         "github.com/gin-gonic/gin"
9         "github.com/iotbzh/xds-agent/lib/apiv1"
10         common "github.com/iotbzh/xds-common/golib"
11         uuid "github.com/satori/go.uuid"
12 )
13
14 var execCmdID = 1
15 var fwdFuncID []uuid.UUID
16
17 // ExecCmd executes remotely a command
18 func (s *APIService) execCmd(c *gin.Context) {
19         s._execRequest("/exec", c)
20 }
21
22 // execSignalCmd executes remotely a command
23 func (s *APIService) execSignalCmd(c *gin.Context) {
24         s._execRequest("/signal", c)
25 }
26
27 func (s *APIService) _execRequest(cmd string, c *gin.Context) {
28         data, err := c.GetRawData()
29         if err != nil {
30                 common.APIError(c, err.Error())
31         }
32
33         args := apiv1.ExecArgs{}
34         // XXX - we cannot use c.BindJSON, so directly unmarshall it
35         // (see https://github.com/gin-gonic/gin/issues/1078)
36         if err := json.Unmarshal(data, &args); err != nil {
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 XDSServer WS events to client WS
76         // TODO removed static event name list and get it from XDSServer
77         evtList := []string{
78                 apiv1.ExecInEvent,
79                 apiv1.ExecOutEvent,
80                 apiv1.ExecInferiorInEvent,
81                 apiv1.ExecInferiorOutEvent,
82         }
83
84         for _, evName := range evtList {
85                 evN := evName
86                 fwdFunc := func(pData interface{}, evData interface{}) error {
87                         sid := pData.(string)
88                         // IO socket can be nil when disconnected
89                         so := s.sessions.IOSocketGet(sid)
90                         if so == nil {
91                                 s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
92                                 return nil
93                         }
94
95                         // Forward event to Client/Dashboard
96                         (*so).Emit(evN, evData)
97                         return nil
98                 }
99                 id, err := svr.EventOn(evN, sess.ID, fwdFunc)
100                 if err != nil {
101                         common.APIError(c, err.Error())
102                         return
103                 }
104                 fwdFuncID = append(fwdFuncID, id)
105         }
106
107         // Handle Exit event separately to cleanup registered listener
108         var exitFuncID uuid.UUID
109         exitFunc := func(pData interface{}, evData interface{}) error {
110                 evN := apiv1.ExecExitEvent
111                 sid := pData.(string)
112
113                 // IO socket can be nil when disconnected
114                 so := s.sessions.IOSocketGet(sid)
115                 if so == nil {
116                         s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
117                         return nil
118                 }
119
120                 (*so).Emit(evN, evData)
121
122                 // cleanup listener
123                 for i, evName := range evtList {
124                         svr.EventOff(evName, fwdFuncID[i])
125                 }
126                 svr.EventOff(evN, exitFuncID)
127
128                 return nil
129         }
130         exitFuncID, err = svr.EventOn(apiv1.ExecExitEvent, sess.ID, exitFunc)
131         if err != nil {
132                 common.APIError(c, err.Error())
133                 return
134         }
135
136         // Forward back command to right server
137         response, err := svr.SendCommand(cmd, data)
138         if err != nil {
139                 common.APIError(c, err.Error())
140                 return
141         }
142
143         // Decode response
144         body, err := ioutil.ReadAll(response.Body)
145         if err != nil {
146                 common.APIError(c, "Cannot read response body")
147                 return
148         }
149         c.JSON(http.StatusOK, string(body))
150 }