Initial main commit.
[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"`
19         CmdTimeout int    `json:"timeout"` // command completion timeout in Second
20 }
21
22 // MakeOutMsg Message send on each output (stdout+stderr) of make command
23 type MakeOutMsg struct {
24         CmdID     string `json:"cmdID"`
25         Timestamp string `json:timestamp`
26         Stdout    string `json:"stdout"`
27         Stderr    string `json:"stderr"`
28 }
29
30 // MakeExitMsg Message send on make command exit
31 type MakeExitMsg struct {
32         CmdID     string `json:"cmdID"`
33         Timestamp string `json:timestamp`
34         Code      int    `json:"code"`
35         Error     error  `json:"error"`
36 }
37
38 // Event name send in WS
39 const MakeOutEvent = "make:output"
40 const MakeExitEvent = "make:exit"
41
42 var makeCommandID = 1
43
44 func (s *APIService) buildMake(c *gin.Context) {
45         var args MakeArgs
46
47         if c.BindJSON(&args) != nil {
48                 common.APIError(c, "Invalid arguments")
49                 return
50         }
51
52         sess := s.sessions.Get(c)
53         if sess == nil {
54                 common.APIError(c, "Unknown sessions")
55                 return
56         }
57         sop := sess.IOSocket
58         if sop == nil {
59                 common.APIError(c, "Websocket not established")
60                 return
61         }
62
63         // Allow to pass id in url (/make/:id) or as JSON argument
64         id := c.Param("id")
65         if id == "" {
66                 id = args.ID
67         }
68         if id == "" {
69                 common.APIError(c, "Invalid id")
70                 return
71         }
72
73         prj := s.cfg.GetFolderFromID(id)
74         if prj == nil {
75                 common.APIError(c, "Unknown id")
76                 return
77         }
78
79         execTmo := args.CmdTimeout
80         if execTmo == 0 {
81                 // TODO get default timeout from config.json file
82                 execTmo = 24 * 60 * 60 // 1 day
83         }
84
85         cmd := "cd " + prj.GetFullPath(args.RPath) + " && make"
86         if args.Args != "" {
87                 cmd += " " + args.Args
88         }
89
90         // Define callback for output
91         var oCB common.EmitOutputCB
92         oCB = func(sid string, id int, stdout, stderr string) {
93                 // IO socket can be nil when disconnected
94                 so := s.sessions.IOSocketGet(sid)
95                 if so == nil {
96                         s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%d", MakeOutEvent, sid, id)
97                         return
98                 }
99                 s.log.Debugf("%s emitted - WS sid %s - id:%d", MakeOutEvent, sid, id)
100
101                 // FIXME replace by .BroadcastTo a room
102                 err := (*so).Emit(MakeOutEvent, MakeOutMsg{
103                         CmdID:     strconv.Itoa(id),
104                         Timestamp: time.Now().String(),
105                         Stdout:    stdout,
106                         Stderr:    stderr,
107                 })
108                 if err != nil {
109                         s.log.Errorf("WS Emit : %v", err)
110                 }
111         }
112
113         // Define callback for output
114         eCB := func(sid string, id int, code int, err error) {
115                 s.log.Debugf("Command [Cmd ID %d] exited: code %d, error: %v", id, code, err)
116
117                 // IO socket can be nil when disconnected
118                 so := s.sessions.IOSocketGet(sid)
119                 if so == nil {
120                         s.log.Infof("%s not emitted - WS closed (id:%d", MakeExitEvent, id)
121                         return
122                 }
123
124                 // FIXME replace by .BroadcastTo a room
125                 e := (*so).Emit(MakeExitEvent, MakeExitMsg{
126                         CmdID:     strconv.Itoa(id),
127                         Timestamp: time.Now().String(),
128                         Code:      code,
129                         Error:     err,
130                 })
131                 if e != nil {
132                         s.log.Errorf("WS Emit : %v", e)
133                 }
134         }
135
136         cmdID := makeCommandID
137         makeCommandID++
138
139         s.log.Debugf("Execute [Cmd ID %d]: %v", cmdID, cmd)
140         err := common.ExecPipeWs(cmd, sop, sess.ID, cmdID, execTmo, s.log, oCB, eCB)
141         if err != nil {
142                 common.APIError(c, err.Error())
143                 return
144         }
145
146         c.JSON(http.StatusOK,
147                 gin.H{
148                         "status": "OK",
149                         "cmdID":  cmdID,
150                 })
151 }