d43f2031cbd9d07d93461fc4f69b85c6183ec6ee
[src/xds/xds-server.git] / lib / xdsserver / apiv1-make.go
1 /*
2  * Copyright (C) 2017 "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 xdsserver
19
20 import (
21         "github.com/gin-gonic/gin"
22         common "github.com/iotbzh/xds-common/golib"
23 )
24
25 /* TODO: Deprecated - should be removed
26 // MakeArgs is the parameters (json format) of /make command
27 type MakeArgs struct {
28         ID            string   `json:"id"`
29         SdkID         string   `json:"sdkID"` // sdk ID to use for setting env
30         CmdID         string   `json:"cmdID"` // command unique ID
31         Args          []string `json:"args"`  // args to pass to make command
32         Env           []string `json:"env"`
33         RPath         string   `json:"rpath"`         // relative path into project
34         ExitImmediate bool     `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization)
35         CmdTimeout    int      `json:"timeout"`       // command completion timeout in Second
36 }
37
38 // MakeOutMsg Message send on each output (stdout+stderr) of make command
39 type MakeOutMsg struct {
40         CmdID     string `json:"cmdID"`
41         Timestamp string `json:"timestamp"`
42         Stdout    string `json:"stdout"`
43         Stderr    string `json:"stderr"`
44 }
45
46 // MakeExitMsg Message send on make command exit
47 type MakeExitMsg struct {
48         CmdID     string `json:"cmdID"`
49         Timestamp string `json:"timestamp"`
50         Code      int    `json:"code"`
51         Error     error  `json:"error"`
52 }
53
54 // MakeOutEvent Event send in WS when characters are received on stdout/stderr
55 const MakeOutEvent = "make:output"
56
57 // MakeExitEvent Event send in WS when command exited
58 const MakeExitEvent = "make:exit"
59
60 var makeCommandID = 1
61 */
62
63 func (s *APIService) buildMake(c *gin.Context) {
64         common.APIError(c, "/make route is not longer supported, use /exec instead")
65
66         /*
67                 var args MakeArgs
68
69                 if c.BindJSON(&args) != nil {
70                         common.APIError(c, "Invalid arguments")
71                         return
72                 }
73
74                 sess := s.sessions.Get(c)
75                 if sess == nil {
76                         common.APIError(c, "Unknown sessions")
77                         return
78                 }
79                 sop := sess.IOSocket
80                 if sop == nil {
81                         common.APIError(c, "Websocket not established")
82                         return
83                 }
84
85                 // Allow to pass id in url (/make/:id) or as JSON argument
86                 idArg := c.Param("id")
87                 if idArg == "" {
88                         idArg = args.ID
89                 }
90                 if idArg == "" {
91                         common.APIError(c, "Invalid id")
92                         return
93                 }
94                 id, err := s.mfolders.ResolveID(idArg)
95                 if err != nil {
96                         common.APIError(c, err.Error())
97                         return
98                 }
99                 pf := s.mfolders.Get(id)
100                 if pf == nil {
101                         common.APIError(c, "Unknown id")
102                         return
103                 }
104                 folder := *pf
105                 prj := folder.GetConfig()
106
107                 execTmo := args.CmdTimeout
108                 if execTmo == 0 {
109                         // TODO get default timeout from config.json file
110                         execTmo = 24 * 60 * 60 // 1 day
111                 }
112
113                 // TODO merge all code below with exec.go
114
115                 // Define callback for output
116                 var oCB common.EmitOutputCB
117                 oCB = func(sid string, cmdID string, stdout, stderr string, data *map[string]interface{}) {
118                         // IO socket can be nil when disconnected
119                         so := s.sessions.IOSocketGet(sid)
120                         if so == nil {
121                                 s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%s", MakeOutEvent, sid, cmdID)
122                                 return
123                         }
124
125                         // Retrieve project ID and RootPath
126                         prjID := (*data)["ID"].(string)
127                         prjRootPath := (*data)["RootPath"].(string)
128
129                         // Cleanup any references to internal rootpath in stdout & stderr
130                         stdout = strings.Replace(stdout, prjRootPath, "", -1)
131                         stderr = strings.Replace(stderr, prjRootPath, "", -1)
132
133                         s.log.Debugf("%s emitted - WS sid %s - id:%d - prjID:%s", MakeOutEvent, sid, id, prjID)
134
135                         // FIXME replace by .BroadcastTo a room
136                         err := (*so).Emit(MakeOutEvent, MakeOutMsg{
137                                 CmdID:     cmdID,
138                                 Timestamp: time.Now().String(),
139                                 Stdout:    stdout,
140                                 Stderr:    stderr,
141                         })
142                         if err != nil {
143                                 s.log.Errorf("WS Emit : %v", err)
144                         }
145                 }
146
147                 // Define callback for output
148                 eCB := func(sid string, cmdID string, code int, err error, data *map[string]interface{}) {
149                         s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", cmdID, code, err)
150
151                         // IO socket can be nil when disconnected
152                         so := s.sessions.IOSocketGet(sid)
153                         if so == nil {
154                                 s.log.Infof("%s not emitted - WS closed (id:%s", MakeExitEvent, cmdID)
155                                 return
156                         }
157
158                         // Retrieve project ID and RootPath
159                         prjID := (*data)["ID"].(string)
160                         exitImm := (*data)["ExitImmediate"].(bool)
161
162                         // XXX - workaround to be sure that Syncthing detected all changes
163                         if err := s.mfolders.ForceSync(prjID); err != nil {
164                                 s.log.Errorf("Error while syncing folder %s: %v", prjID, err)
165                         }
166                         if !exitImm {
167                                 // Wait end of file sync
168                                 // FIXME pass as argument
169                                 tmo := 60
170                                 for t := tmo; t > 0; t-- {
171                                         s.log.Debugf("Wait file insync for %s (%d/%d)", prjID, t, tmo)
172                                         if sync, err := s.mfolders.IsFolderInSync(prjID); sync || err != nil {
173                                                 if err != nil {
174                                                         s.log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err)
175                                                 }
176                                                 break
177                                         }
178                                         time.Sleep(time.Second)
179                                 }
180                         }
181
182                         // FIXME replace by .BroadcastTo a room
183                         e := (*so).Emit(MakeExitEvent, MakeExitMsg{
184                                 CmdID:     id,
185                                 Timestamp: time.Now().String(),
186                                 Code:      code,
187                                 Error:     err,
188                         })
189                         if e != nil {
190                                 s.log.Errorf("WS Emit : %v", e)
191                         }
192                 }
193
194                 // Unique ID for each commands
195                 if args.CmdID == "" {
196                         args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(makeCommandID)
197                         makeCommandID++
198                 }
199                 cmd := []string{}
200
201                 // Retrieve env command regarding Sdk ID
202                 if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); len(envCmd) > 0 {
203                         cmd = append(cmd, envCmd...)
204                         cmd = append(cmd, "&&")
205                 }
206
207                 cmd = append(cmd, "cd", folder.GetFullPath(args.RPath), "&&", "make")
208                 if len(args.Args) > 0 {
209                         cmd = append(cmd, args.Args...)
210                 }
211
212                 s.log.Debugf("Execute [Cmd ID %d]: %v", args.CmdID, cmd)
213
214                 data := make(map[string]interface{})
215                 data["ID"] = prj.ID
216                 data["RootPath"] = prj.RootPath
217                 data["ExitImmediate"] = args.ExitImmediate
218
219                 err = common.ExecPipeWs(cmd, args.Env, sop, sess.ID, args.CmdID, execTmo, s.log, oCB, eCB, &data)
220                 if err != nil {
221                         common.APIError(c, err.Error())
222                         return
223                 }
224
225                 c.JSON(http.StatusOK,
226                         gin.H{
227                                 "status": "OK",
228                                 "cmdID":  args.CmdID,
229                         })
230         */
231 }