Fixed Syncthing folder status events and exec command.
[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         common "github.com/iotbzh/xds-common/golib"
10         uuid "github.com/satori/go.uuid"
11 )
12
13 // ExecArgs Only define used fields
14 type ExecArgs struct {
15         ID    string `json:"id" binding:"required"`
16         CmdID string `json:"cmdID"` // command unique ID
17 }
18
19 var execCmdID = 1
20
21 // ExecCmd executes remotely a command
22 func (s *APIService) execCmd(c *gin.Context) {
23         s._execRequest("/exec", c)
24 }
25
26 // execSignalCmd executes remotely a command
27 func (s *APIService) execSignalCmd(c *gin.Context) {
28         s._execRequest("/signal", c)
29 }
30
31 func (s *APIService) _execRequest(cmd string, c *gin.Context) {
32         data, err := c.GetRawData()
33         if err != nil {
34                 common.APIError(c, err.Error())
35         }
36
37         args := ExecArgs{}
38         // XXX - we cannot use c.BindJSON, so directly unmarshall it
39         // (see https://github.com/gin-gonic/gin/issues/1078)
40         if err := json.Unmarshal(data, &args); err != nil {
41                 common.APIError(c, "Invalid arguments")
42                 return
43         }
44
45         // First get Project ID to retrieve Server ID and send command to right server
46         id := c.Param("id")
47         if id == "" {
48                 id = args.ID
49         }
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                 "exec:input",
79                 "exec:output",
80                 "exec:inferior-input",
81                 "exec:inferior-output",
82         }
83         so := *sock
84         fwdFuncID := []uuid.UUID{}
85         for _, evName := range evtList {
86                 evN := evName
87                 fwdFunc := func(evData interface{}) {
88                         // Forward event to Client/Dashboard
89                         so.Emit(evN, evData)
90                 }
91                 id, err := svr.EventOn(evN, fwdFunc)
92                 if err != nil {
93                         common.APIError(c, err.Error())
94                         return
95                 }
96                 fwdFuncID = append(fwdFuncID, id)
97         }
98
99         // Handle Exit event separately to cleanup registered listener
100         var exitFuncID uuid.UUID
101         exitFunc := func(evData interface{}) {
102                 so.Emit("exec:exit", evData)
103
104                 // cleanup listener
105                 for i, evName := range evtList {
106                         svr.EventOff(evName, fwdFuncID[i])
107                 }
108                 svr.EventOff("exec:exit", exitFuncID)
109         }
110         exitFuncID, err = svr.EventOn("exec:exit", exitFunc)
111         if err != nil {
112                 common.APIError(c, err.Error())
113                 return
114         }
115
116         // Forward back command to right server
117         response, err := svr.SendCommand(cmd, data)
118         if err != nil {
119                 common.APIError(c, err.Error())
120                 return
121         }
122
123         // Decode response
124         body, err := ioutil.ReadAll(response.Body)
125         if err != nil {
126                 common.APIError(c, "Cannot read response body")
127                 return
128         }
129         c.JSON(http.StatusOK, string(body))
130
131 }