Add Cross SDKs support (part 2)
[src/xds/xds-server.git] / lib / apiv1 / make.go
1 package apiv1
2
3 import (
4         "net/http"
5
6         "time"
7
8         "strconv"
9
10         "github.com/gin-gonic/gin"
11         "github.com/iotbzh/xds-server/lib/common"
12 )
13
14 // MakeArgs is the parameters (json format) of /make command
15 type MakeArgs struct {
16         ID         string `json:"id"`
17         RPath      string `json:"rpath"`   // relative path into project
18         Args       string `json:"args"`    // args to pass to make command
19         SdkID      string `json:"sdkid"`   // sdk ID to use for setting env
20         CmdTimeout int    `json:"timeout"` // command completion timeout in Second
21 }
22
23 // MakeOutMsg Message send on each output (stdout+stderr) of make command
24 type MakeOutMsg struct {
25         CmdID     string `json:"cmdID"`
26         Timestamp string `json:"timestamp"`
27         Stdout    string `json:"stdout"`
28         Stderr    string `json:"stderr"`
29 }
30
31 // MakeExitMsg Message send on make command exit
32 type MakeExitMsg struct {
33         CmdID     string `json:"cmdID"`
34         Timestamp string `json:"timestamp"`
35         Code      int    `json:"code"`
36         Error     error  `json:"error"`
37 }
38
39 // MakeOutEvent Event send in WS when characters are received on stdout/stderr
40 const MakeOutEvent = "make:output"
41
42 // MakeExitEvent Event send in WS when command exited
43 const MakeExitEvent = "make:exit"
44
45 var makeCommandID = 1
46
47 func (s *APIService) buildMake(c *gin.Context) {
48         var args MakeArgs
49
50         if c.BindJSON(&args) != nil {
51                 common.APIError(c, "Invalid arguments")
52                 return
53         }
54
55         sess := s.sessions.Get(c)
56         if sess == nil {
57                 common.APIError(c, "Unknown sessions")
58                 return
59         }
60         sop := sess.IOSocket
61         if sop == nil {
62                 common.APIError(c, "Websocket not established")
63                 return
64         }
65
66         // Allow to pass id in url (/make/:id) or as JSON argument
67         id := c.Param("id")
68         if id == "" {
69                 id = args.ID
70         }
71         if id == "" {
72                 common.APIError(c, "Invalid id")
73                 return
74         }
75
76         prj := s.mfolder.GetFolderFromID(id)
77         if prj == nil {
78                 common.APIError(c, "Unknown id")
79                 return
80         }
81
82         execTmo := args.CmdTimeout
83         if execTmo == 0 {
84                 // TODO get default timeout from config.json file
85                 execTmo = 24 * 60 * 60 // 1 day
86         }
87
88         cmd := "cd " + prj.GetFullPath(args.RPath) + " && make"
89         if args.Args != "" {
90                 cmd += " " + args.Args
91         }
92
93         // Define callback for output
94         var oCB common.EmitOutputCB
95         oCB = func(sid string, id int, stdout, stderr string) {
96                 // IO socket can be nil when disconnected
97                 so := s.sessions.IOSocketGet(sid)
98                 if so == nil {
99                         s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%d", MakeOutEvent, sid, id)
100                         return
101                 }
102                 s.log.Debugf("%s emitted - WS sid %s - id:%d", MakeOutEvent, sid, id)
103
104                 // FIXME replace by .BroadcastTo a room
105                 err := (*so).Emit(MakeOutEvent, MakeOutMsg{
106                         CmdID:     strconv.Itoa(id),
107                         Timestamp: time.Now().String(),
108                         Stdout:    stdout,
109                         Stderr:    stderr,
110                 })
111                 if err != nil {
112                         s.log.Errorf("WS Emit : %v", err)
113                 }
114         }
115
116         // Define callback for output
117         eCB := func(sid string, id int, code int, err error) {
118                 s.log.Debugf("Command [Cmd ID %d] exited: code %d, error: %v", id, code, err)
119
120                 // IO socket can be nil when disconnected
121                 so := s.sessions.IOSocketGet(sid)
122                 if so == nil {
123                         s.log.Infof("%s not emitted - WS closed (id:%d", MakeExitEvent, id)
124                         return
125                 }
126
127                 // FIXME replace by .BroadcastTo a room
128                 e := (*so).Emit(MakeExitEvent, MakeExitMsg{
129                         CmdID:     strconv.Itoa(id),
130                         Timestamp: time.Now().String(),
131                         Code:      code,
132                         Error:     err,
133                 })
134                 if e != nil {
135                         s.log.Errorf("WS Emit : %v", e)
136                 }
137         }
138
139         cmdID := makeCommandID
140         makeCommandID++
141
142         // Retrieve env command regarding Sdk ID
143         if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); envCmd != "" {
144                 cmd = envCmd + " && " + cmd
145         }
146
147         s.log.Debugf("Execute [Cmd ID %d]: %v", cmdID, cmd)
148         err := common.ExecPipeWs(cmd, sop, sess.ID, cmdID, execTmo, s.log, oCB, eCB)
149         if err != nil {
150                 common.APIError(c, err.Error())
151                 return
152         }
153
154         c.JSON(http.StatusOK,
155                 gin.H{
156                         "status": "OK",
157                         "cmdID":  cmdID,
158                 })
159 }