diff options
author | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2017-11-29 08:54:00 +0100 |
---|---|---|
committer | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2017-11-29 11:10:30 +0100 |
commit | 2f7828d01f4c4ca2909f95f098627cd5475ed225 (patch) | |
tree | b5e71920b813b95cae3e32044be08b99223348ec /lib/apiv1 | |
parent | 5caebfb4b7c3b73988f067082b219ce5b7f409ba (diff) |
Refit source files to have a public xs-apiv1 lib package.
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
Diffstat (limited to 'lib/apiv1')
-rw-r--r-- | lib/apiv1/apiv1.go | 63 | ||||
-rw-r--r-- | lib/apiv1/config.go | 40 | ||||
-rw-r--r-- | lib/apiv1/events.go | 162 | ||||
-rw-r--r-- | lib/apiv1/exec.go | 416 | ||||
-rw-r--r-- | lib/apiv1/folders.go | 131 | ||||
-rw-r--r-- | lib/apiv1/make.go | 215 | ||||
-rw-r--r-- | lib/apiv1/sdks.go | 29 | ||||
-rw-r--r-- | lib/apiv1/version.go | 26 |
8 files changed, 0 insertions, 1082 deletions
diff --git a/lib/apiv1/apiv1.go b/lib/apiv1/apiv1.go deleted file mode 100644 index fffed2d..0000000 --- a/lib/apiv1/apiv1.go +++ /dev/null @@ -1,63 +0,0 @@ -package apiv1 - -import ( - "github.com/Sirupsen/logrus" - "github.com/gin-gonic/gin" - - "github.com/iotbzh/xds-server/lib/crosssdk" - "github.com/iotbzh/xds-server/lib/model" - "github.com/iotbzh/xds-server/lib/session" - "github.com/iotbzh/xds-server/lib/xdsconfig" -) - -// APIService . -type APIService struct { - router *gin.Engine - apiRouter *gin.RouterGroup - sessions *session.Sessions - cfg *xdsconfig.Config - mfolders *model.Folders - sdks *crosssdk.SDKs - log *logrus.Logger -} - -// New creates a new instance of API service -func New(r *gin.Engine, sess *session.Sessions, cfg *xdsconfig.Config, mfolders *model.Folders, sdks *crosssdk.SDKs) *APIService { - s := &APIService{ - router: r, - sessions: sess, - apiRouter: r.Group("/api/v1"), - cfg: cfg, - mfolders: mfolders, - sdks: sdks, - log: cfg.Log, - } - - s.apiRouter.GET("/version", s.getVersion) - - s.apiRouter.GET("/config", s.getConfig) - s.apiRouter.POST("/config", s.setConfig) - - s.apiRouter.GET("/folders", s.getFolders) - s.apiRouter.GET("/folders/:id", s.getFolder) - s.apiRouter.PUT("/folders/:id", s.updateFolder) - s.apiRouter.POST("/folders", s.addFolder) - s.apiRouter.POST("/folders/sync/:id", s.syncFolder) - s.apiRouter.DELETE("/folders/:id", s.delFolder) - - s.apiRouter.GET("/sdks", s.getSdks) - s.apiRouter.GET("/sdks/:id", s.getSdk) - - s.apiRouter.POST("/make", s.buildMake) - s.apiRouter.POST("/make/:id", s.buildMake) - - s.apiRouter.POST("/exec", s.execCmd) - s.apiRouter.POST("/exec/:id", s.execCmd) - s.apiRouter.POST("/signal", s.execSignalCmd) - - s.apiRouter.GET("/events", s.eventsList) - s.apiRouter.POST("/events/register", s.eventsRegister) - s.apiRouter.POST("/events/unregister", s.eventsUnRegister) - - return s -} diff --git a/lib/apiv1/config.go b/lib/apiv1/config.go deleted file mode 100644 index 4b53217..0000000 --- a/lib/apiv1/config.go +++ /dev/null @@ -1,40 +0,0 @@ -package apiv1 - -import ( - "net/http" - "sync" - - "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" - "github.com/iotbzh/xds-server/lib/xdsconfig" -) - -var confMut sync.Mutex - -// GetConfig returns server configuration -func (s *APIService) getConfig(c *gin.Context) { - confMut.Lock() - defer confMut.Unlock() - - c.JSON(http.StatusOK, s.cfg) -} - -// SetConfig sets server configuration -func (s *APIService) setConfig(c *gin.Context) { - // FIXME - must be tested - c.JSON(http.StatusNotImplemented, "Not implemented") - - var cfgArg xdsconfig.Config - - if c.BindJSON(&cfgArg) != nil { - common.APIError(c, "Invalid arguments") - return - } - - confMut.Lock() - defer confMut.Unlock() - - s.log.Debugln("SET config: ", cfgArg) - - common.APIError(c, "Not Supported") -} diff --git a/lib/apiv1/events.go b/lib/apiv1/events.go deleted file mode 100644 index d837571..0000000 --- a/lib/apiv1/events.go +++ /dev/null @@ -1,162 +0,0 @@ -package apiv1 - -import ( - "net/http" - "strings" - "time" - - "github.com/iotbzh/xds-server/lib/folder" - - "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" -) - -// EventArgs is the parameters (json format) of /events/register command -type EventRegisterArgs struct { - Name string `json:"name"` - ProjectID string `json:"filterProjectID"` -} - -type EventUnRegisterArgs struct { - Name string `json:"name"` - ID int `json:"id"` -} - -// EventMsg Message send -type EventMsg struct { - Time string `json:"time"` - Type string `json:"type"` - Folder folder.FolderConfig `json:"folder"` -} - -// EventEvent Event send in WS when an internal event (eg. Syncthing event is received) -const ( - // EventTypePrefix Used as event prefix - EventTypePrefix = "event:" // following by event type - - // Supported Events type - EVTAll = EventTypePrefix + "all" - EVTFolderChange = EventTypePrefix + "folder-change" // type EventMsg with Data type apiv1.??? - EVTFolderStateChange = EventTypePrefix + "folder-state-change" // type EventMsg with Data type apiv1.??? -) - -// eventsList Registering for events that will be send over a WS -func (s *APIService) eventsList(c *gin.Context) { - -} - -// eventsRegister Registering for events that will be send over a WS -func (s *APIService) eventsRegister(c *gin.Context) { - var args EventRegisterArgs - - if c.BindJSON(&args) != nil { - common.APIError(c, "Invalid arguments") - return - } - - sess := s.sessions.Get(c) - if sess == nil { - common.APIError(c, "Unknown sessions") - return - } - - evType := strings.TrimPrefix(EVTFolderStateChange, EventTypePrefix) - if args.Name != evType { - common.APIError(c, "Unsupported event name") - return - } - - /* XXX - to be removed if no plan to support "generic" event - var cbFunc st.EventsCB - cbFunc = func(ev st.Event, data *st.EventsCBData) { - - evid, _ := strconv.Atoi((*data)["id"].(string)) - ssid := (*data)["sid"].(string) - so := s.sessions.IOSocketGet(ssid) - if so == nil { - s.log.Infof("Event %s not emitted - sid: %s", ev.Type, ssid) - - // Consider that client disconnected, so unregister this event - s.mfolders.SThg.Events.UnRegister(ev.Type, evid) - return - } - - msg := EventMsg{ - Time: ev.Time, - Type: ev.Type, - Data: ev.Data, - } - - if err := (*so).Emit(EVTAll, msg); err != nil { - s.log.Errorf("WS Emit Event : %v", err) - } - - if err := (*so).Emit(EventTypePrefix+ev.Type, msg); err != nil { - s.log.Errorf("WS Emit Event : %v", err) - } - } - - data := make(st.EventsCBData) - data["sid"] = sess.ID - - id, err := s.mfolders.SThg.Events.Register(args.Name, cbFunc, args.ProjectID, &data) - */ - - var cbFunc folder.EventCB - cbFunc = func(cfg *folder.FolderConfig, data *folder.EventCBData) { - ssid := (*data)["sid"].(string) - so := s.sessions.IOSocketGet(ssid) - if so == nil { - //s.log.Infof("Event %s not emitted - sid: %s", ev.Type, ssid) - - // Consider that client disconnected, so unregister this event - // SEB FIXMEs.mfolders.RegisterEventChange(ev.Type) - return - } - - msg := EventMsg{ - Time: time.Now().String(), - Type: evType, - Folder: *cfg, - } - - s.log.Debugf("WS Emit %s - Status=%10s, IsInSync=%6v, ID=%s", - EventTypePrefix+evType, cfg.Status, cfg.IsInSync, cfg.ID) - - if err := (*so).Emit(EventTypePrefix+evType, msg); err != nil { - s.log.Errorf("WS Emit Folder StateChanged event : %v", err) - } - } - data := make(folder.EventCBData) - data["sid"] = sess.ID - - prjID, err := s.mfolders.ResolveID(args.ProjectID) - if err != nil { - common.APIError(c, err.Error()) - return - } - if err = s.mfolders.RegisterEventChange(prjID, &cbFunc, &data); err != nil { - common.APIError(c, err.Error()) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "OK"}) -} - -// eventsRegister Registering for events that will be send over a WS -func (s *APIService) eventsUnRegister(c *gin.Context) { - var args EventUnRegisterArgs - - if c.BindJSON(&args) != nil || args.Name == "" || args.ID < 0 { - common.APIError(c, "Invalid arguments") - return - } - /* TODO - if err := s.mfolders.SThg.Events.UnRegister(args.Name, args.ID); err != nil { - common.APIError(c, err.Error()) - return - } - c.JSON(http.StatusOK, gin.H{"status": "OK"}) - */ - common.APIError(c, "Not implemented yet") -} diff --git a/lib/apiv1/exec.go b/lib/apiv1/exec.go deleted file mode 100644 index baf431f..0000000 --- a/lib/apiv1/exec.go +++ /dev/null @@ -1,416 +0,0 @@ -package apiv1 - -import ( - "fmt" - "net/http" - "os" - "regexp" - "strconv" - "strings" - "time" - - "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" - "github.com/iotbzh/xds-common/golib/eows" - "github.com/kr/pty" -) - -type ( - // ExecArgs JSON parameters of /exec command - ExecArgs struct { - ID string `json:"id" binding:"required"` - SdkID string `json:"sdkID"` // sdk ID to use for setting env - CmdID string `json:"cmdID"` // command unique ID - Cmd string `json:"cmd" binding:"required"` - Args []string `json:"args"` - Env []string `json:"env"` - RPath string `json:"rpath"` // relative path into project - TTY bool `json:"tty"` // Use a tty, specific to gdb --tty option - TTYGdbserverFix bool `json:"ttyGdbserverFix"` // Set to true to activate gdbserver workaround about inferior output - ExitImmediate bool `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization) - CmdTimeout int `json:"timeout"` // command completion timeout in Second - } - - // ExecRes JSON result of /exec command - ExecRes struct { - Status string `json:"status"` // status OK - CmdID string `json:"cmdID"` // command unique ID - } - - // ExecSigRes JSON result of /signal command - ExecSigRes struct { - Status string `json:"status"` // status OK - CmdID string `json:"cmdID"` // command unique ID - } - - // ExecInMsg Message used to received input characters (stdin) - ExecInMsg struct { - CmdID string `json:"cmdID"` - Timestamp string `json:"timestamp"` - Stdin string `json:"stdin"` - } - - // ExecOutMsg Message used to send output characters (stdout+stderr) - ExecOutMsg struct { - CmdID string `json:"cmdID"` - Timestamp string `json:"timestamp"` - Stdout string `json:"stdout"` - Stderr string `json:"stderr"` - } - - // ExecExitMsg Message sent when executed command exited - ExecExitMsg struct { - CmdID string `json:"cmdID"` - Timestamp string `json:"timestamp"` - Code int `json:"code"` - Error error `json:"error"` - } - - // ExecSignalArgs JSON parameters of /exec/signal command - ExecSignalArgs struct { - CmdID string `json:"cmdID" binding:"required"` // command id - Signal string `json:"signal" binding:"required"` // signal number - } -) - -const ( - // ExecInEvent Event send in WS when characters are sent (stdin) - ExecInEvent = "exec:input" - - // ExecOutEvent Event send in WS when characters are received (stdout or stderr) - ExecOutEvent = "exec:output" - - // ExecExitEvent Event send in WS when program exited - ExecExitEvent = "exec:exit" - - // ExecInferiorInEvent Event send in WS when characters are sent to an inferior (used by gdb inferior/tty) - ExecInferiorInEvent = "exec:inferior-input" - - // ExecInferiorOutEvent Event send in WS when characters are received by an inferior - ExecInferiorOutEvent = "exec:inferior-output" -) - -var execCommandID = 1 - -// ExecCmd executes remotely a command -func (s *APIService) execCmd(c *gin.Context) { - var gdbPty, gdbTty *os.File - var err error - var args ExecArgs - if c.BindJSON(&args) != nil { - common.APIError(c, "Invalid arguments") - return - } - - // TODO: add permission ? - - // Retrieve session info - sess := s.sessions.Get(c) - if sess == nil { - common.APIError(c, "Unknown sessions") - return - } - sop := sess.IOSocket - if sop == nil { - common.APIError(c, "Websocket not established") - return - } - - // Allow to pass id in url (/exec/:id) or as JSON argument - idArg := c.Param("id") - if idArg == "" { - idArg = args.ID - } - if idArg == "" { - common.APIError(c, "Invalid id") - return - } - id, err := s.mfolders.ResolveID(idArg) - if err != nil { - common.APIError(c, err.Error()) - return - } - f := s.mfolders.Get(id) - if f == nil { - common.APIError(c, "Unknown id") - return - } - fld := *f - prj := fld.GetConfig() - - // Build command line - cmd := []string{} - // Setup env var regarding Sdk ID (used for example to setup cross toolchain) - if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); len(envCmd) > 0 { - cmd = append(cmd, envCmd...) - cmd = append(cmd, "&&") - } else { - // It's an error if no envcmd found while a sdkid has been provided - if args.SdkID != "" { - common.APIError(c, "Unknown sdkid") - return - } - } - - cmd = append(cmd, "cd", "\""+fld.GetFullPath(args.RPath)+"\"") - // FIXME - add 'exec' prevents to use syntax: - // xds-exec -l debug -c xds-config.env -- "cd build && cmake .." - // but exec is mandatory to allow to pass correctly signals - // As workaround, exec is set for now on client side (eg. in xds-gdb) - //cmd = append(cmd, "&&", "exec", args.Cmd) - cmd = append(cmd, "&&", args.Cmd) - - // Process command arguments - cmdArgs := make([]string, len(args.Args)+1) - - // Copy and Translate path from client to server - for _, aa := range args.Args { - if strings.Contains(aa, prj.ClientPath) { - cmdArgs = append(cmdArgs, fld.ConvPathCli2Svr(aa)) - } else { - cmdArgs = append(cmdArgs, aa) - } - } - - // Allocate pts if tty if used - if args.TTY { - gdbPty, gdbTty, err = pty.Open() - if err != nil { - common.APIError(c, err.Error()) - return - } - - s.log.Debugf("Client command tty: %v %v\n", gdbTty.Name(), gdbTty.Name()) - cmdArgs = append(cmdArgs, "--tty="+gdbTty.Name()) - } - - // Unique ID for each commands - if args.CmdID == "" { - args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(execCommandID) - execCommandID++ - } - - // Create new execution over WS context - execWS := eows.New(strings.Join(cmd, " "), cmdArgs, sop, sess.ID, args.CmdID) - execWS.Log = s.log - - // Append client project dir to environment - execWS.Env = append(args.Env, "CLIENT_PROJECT_DIR="+prj.ClientPath) - - // Set command execution timeout - if args.CmdTimeout == 0 { - // 0 : default timeout - // TODO get default timeout from config.json file - execWS.CmdExecTimeout = 24 * 60 * 60 // 1 day - } else { - execWS.CmdExecTimeout = args.CmdTimeout - } - - // Define callback for input (stdin) - execWS.InputEvent = ExecInEvent - execWS.InputCB = func(e *eows.ExecOverWS, stdin string) (string, error) { - s.log.Debugf("STDIN <<%v>>", strings.Replace(stdin, "\n", "\\n", -1)) - - // Handle Ctrl-D - if len(stdin) == 1 && stdin == "\x04" { - // Close stdin - errMsg := fmt.Errorf("close stdin: %v", stdin) - return "", errMsg - } - - // Set correct path - data := e.UserData - prjID := (*data)["ID"].(string) - f := s.mfolders.Get(prjID) - if f == nil { - s.log.Errorf("InputCB: Cannot get folder ID %s", prjID) - } else { - // Translate paths from client to server - stdin = (*f).ConvPathCli2Svr(stdin) - } - - return stdin, nil - } - - // Define callback for output (stdout+stderr) - execWS.OutputCB = func(e *eows.ExecOverWS, stdout, stderr string) { - // IO socket can be nil when disconnected - so := s.sessions.IOSocketGet(e.Sid) - if so == nil { - s.log.Infof("%s not emitted: WS closed (sid:%s, msgid:%s)", ExecOutEvent, e.Sid, e.CmdID) - return - } - - // Retrieve project ID and RootPath - data := e.UserData - prjID := (*data)["ID"].(string) - gdbServerTTY := (*data)["gdbServerTTY"].(string) - - f := s.mfolders.Get(prjID) - if f == nil { - s.log.Errorf("OutputCB: Cannot get folder ID %s", prjID) - } else { - // Translate paths from server to client - stdout = (*f).ConvPathSvr2Cli(stdout) - stderr = (*f).ConvPathSvr2Cli(stderr) - } - - s.log.Debugf("%s emitted - WS sid[4:] %s - id:%s - prjID:%s", ExecOutEvent, e.Sid[4:], e.CmdID, prjID) - if stdout != "" { - s.log.Debugf("STDOUT <<%v>>", strings.Replace(stdout, "\n", "\\n", -1)) - } - if stderr != "" { - s.log.Debugf("STDERR <<%v>>", strings.Replace(stderr, "\n", "\\n", -1)) - } - - // FIXME replace by .BroadcastTo a room - err := (*so).Emit(ExecOutEvent, ExecOutMsg{ - CmdID: e.CmdID, - Timestamp: time.Now().String(), - Stdout: stdout, - Stderr: stderr, - }) - if err != nil { - s.log.Errorf("WS Emit : %v", err) - } - - // XXX - Workaround due to gdbserver bug that doesn't redirect - // inferior output (https://bugs.eclipse.org/bugs/show_bug.cgi?id=437532#c13) - if gdbServerTTY == "workaround" && len(stdout) > 1 && stdout[0] == '&' { - - // Extract and cleanup string like &"bla bla\n" - re := regexp.MustCompile("&\"(.*)\"") - rer := re.FindAllStringSubmatch(stdout, -1) - out := "" - if rer != nil && len(rer) > 0 { - for _, o := range rer { - if len(o) >= 1 { - out = strings.Replace(o[1], "\\n", "\n", -1) - out = strings.Replace(out, "\\r", "\r", -1) - out = strings.Replace(out, "\\t", "\t", -1) - - s.log.Debugf("STDOUT INFERIOR: <<%v>>", out) - err := (*so).Emit(ExecInferiorOutEvent, ExecOutMsg{ - CmdID: e.CmdID, - Timestamp: time.Now().String(), - Stdout: out, - Stderr: "", - }) - if err != nil { - s.log.Errorf("WS Emit : %v", err) - } - } - } - } else { - s.log.Errorf("INFERIOR out parsing error: stdout=<%v>", stdout) - } - } - } - - // Define callback for output - execWS.ExitCB = func(e *eows.ExecOverWS, code int, err error) { - s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", e.CmdID, code, err) - - // Close client tty - defer func() { - if gdbPty != nil { - gdbPty.Close() - } - if gdbTty != nil { - gdbTty.Close() - } - }() - - // IO socket can be nil when disconnected - so := s.sessions.IOSocketGet(e.Sid) - if so == nil { - s.log.Infof("%s not emitted - WS closed (id:%s)", ExecExitEvent, e.CmdID) - return - } - - // Retrieve project ID and RootPath - data := e.UserData - prjID := (*data)["ID"].(string) - exitImm := (*data)["ExitImmediate"].(bool) - - // XXX - workaround to be sure that Syncthing detected all changes - if err := s.mfolders.ForceSync(prjID); err != nil { - s.log.Errorf("Error while syncing folder %s: %v", prjID, err) - } - if !exitImm { - // Wait end of file sync - // FIXME pass as argument - tmo := 60 - for t := tmo; t > 0; t-- { - s.log.Debugf("Wait file in-sync for %s (%d/%d)", prjID, t, tmo) - if sync, err := s.mfolders.IsFolderInSync(prjID); sync || err != nil { - if err != nil { - s.log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err) - } - break - } - time.Sleep(time.Second) - } - s.log.Debugf("OK file are synchronized.") - } - - // FIXME replace by .BroadcastTo a room - errSoEmit := (*so).Emit(ExecExitEvent, ExecExitMsg{ - CmdID: e.CmdID, - Timestamp: time.Now().String(), - Code: code, - Error: err, - }) - if errSoEmit != nil { - s.log.Errorf("WS Emit : %v", errSoEmit) - } - } - - // User data (used within callbacks) - data := make(map[string]interface{}) - data["ID"] = prj.ID - data["ExitImmediate"] = args.ExitImmediate - if args.TTY && args.TTYGdbserverFix { - data["gdbServerTTY"] = "workaround" - } else { - data["gdbServerTTY"] = "" - } - execWS.UserData = &data - - // Start command execution - s.log.Infof("Execute [Cmd ID %s]: %v %v", execWS.CmdID, execWS.Cmd, execWS.Args) - - err = execWS.Start() - if err != nil { - common.APIError(c, err.Error()) - return - } - - c.JSON(http.StatusOK, ExecRes{Status: "OK", CmdID: execWS.CmdID}) -} - -// ExecCmd executes remotely a command -func (s *APIService) execSignalCmd(c *gin.Context) { - var args ExecSignalArgs - - if c.BindJSON(&args) != nil { - common.APIError(c, "Invalid arguments") - return - } - - s.log.Debugf("Signal %s for command ID %s", args.Signal, args.CmdID) - - e := eows.GetEows(args.CmdID) - if e == nil { - common.APIError(c, "unknown cmdID") - return - } - - err := e.Signal(args.Signal) - if err != nil { - common.APIError(c, err.Error()) - return - } - - c.JSON(http.StatusOK, ExecSigRes{Status: "OK", CmdID: args.CmdID}) -} diff --git a/lib/apiv1/folders.go b/lib/apiv1/folders.go deleted file mode 100644 index 073445c..0000000 --- a/lib/apiv1/folders.go +++ /dev/null @@ -1,131 +0,0 @@ -package apiv1 - -import ( - "net/http" - "os" - - "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" - "github.com/iotbzh/xds-server/lib/folder" -) - -// getFolders returns all folders configuration -func (s *APIService) getFolders(c *gin.Context) { - c.JSON(http.StatusOK, s.mfolders.GetConfigArr()) -} - -// getFolder returns a specific folder configuration -func (s *APIService) getFolder(c *gin.Context) { - id, err := s.mfolders.ResolveID(c.Param("id")) - if err != nil { - common.APIError(c, err.Error()) - return - } - f := s.mfolders.Get(id) - if f == nil { - common.APIError(c, "Invalid id") - return - } - - c.JSON(http.StatusOK, (*f).GetConfig()) -} - -// addFolder adds a new folder to server config -func (s *APIService) addFolder(c *gin.Context) { - var cfgArg folder.FolderConfig - if c.BindJSON(&cfgArg) != nil { - common.APIError(c, "Invalid arguments") - return - } - - s.log.Debugln("Add folder config: ", cfgArg) - - newFld, err := s.mfolders.Add(cfgArg) - if err != nil { - common.APIError(c, err.Error()) - return - } - - // Create xds-project.conf file - // FIXME: move to folders.createUpdate func (but gin context needed) - fld := s.mfolders.Get(newFld.ID) - prjConfFile := (*fld).GetFullPath("xds-project.conf") - if !common.Exists(prjConfFile) { - fd, err := os.OpenFile(prjConfFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) - if err != nil { - common.APIError(c, err.Error()) - return - } - fd.WriteString("# XDS project settings\n") - fd.WriteString("export XDS_SERVER_URL=" + c.Request.Host + "\n") - fd.WriteString("export XDS_PROJECT_ID=" + newFld.ID + "\n") - if newFld.DefaultSdk == "" { - sdks := s.sdks.GetAll() - newFld.DefaultSdk = sdks[0].ID - } - fd.WriteString("export XDS_SDK_ID=" + newFld.DefaultSdk + "\n") - fd.Close() - } - - c.JSON(http.StatusOK, newFld) -} - -// syncFolder force synchronization of folder files -func (s *APIService) syncFolder(c *gin.Context) { - id, err := s.mfolders.ResolveID(c.Param("id")) - if err != nil { - common.APIError(c, err.Error()) - return - } - s.log.Debugln("Sync folder id: ", id) - - err = s.mfolders.ForceSync(id) - if err != nil { - common.APIError(c, err.Error()) - return - } - - c.JSON(http.StatusOK, "") -} - -// delFolder deletes folder from server config -func (s *APIService) delFolder(c *gin.Context) { - id, err := s.mfolders.ResolveID(c.Param("id")) - if err != nil { - common.APIError(c, err.Error()) - return - } - - s.log.Debugln("Delete folder id ", id) - - delEntry, err := s.mfolders.Delete(id) - if err != nil { - common.APIError(c, err.Error()) - return - } - c.JSON(http.StatusOK, delEntry) -} - -// updateFolder update some field of a folder -func (s *APIService) updateFolder(c *gin.Context) { - id, err := s.mfolders.ResolveID(c.Param("id")) - if err != nil { - common.APIError(c, err.Error()) - return - } - - s.log.Debugln("Update folder id ", id) - - var cfgArg folder.FolderConfig - if c.BindJSON(&cfgArg) != nil { - common.APIError(c, "Invalid arguments") - return - } - - upFld, err := s.mfolders.Update(id, cfgArg) - if err != nil { - common.APIError(c, err.Error()) - return - } - c.JSON(http.StatusOK, upFld) -} diff --git a/lib/apiv1/make.go b/lib/apiv1/make.go deleted file mode 100644 index 6e0c7d6..0000000 --- a/lib/apiv1/make.go +++ /dev/null @@ -1,215 +0,0 @@ -package apiv1 - -import ( - "net/http" - "strings" - - "time" - - "strconv" - - "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" -) - -// MakeArgs is the parameters (json format) of /make command -type MakeArgs struct { - ID string `json:"id"` - SdkID string `json:"sdkID"` // sdk ID to use for setting env - CmdID string `json:"cmdID"` // command unique ID - Args []string `json:"args"` // args to pass to make command - Env []string `json:"env"` - RPath string `json:"rpath"` // relative path into project - ExitImmediate bool `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization) - CmdTimeout int `json:"timeout"` // command completion timeout in Second -} - -// MakeOutMsg Message send on each output (stdout+stderr) of make command -type MakeOutMsg struct { - CmdID string `json:"cmdID"` - Timestamp string `json:"timestamp"` - Stdout string `json:"stdout"` - Stderr string `json:"stderr"` -} - -// MakeExitMsg Message send on make command exit -type MakeExitMsg struct { - CmdID string `json:"cmdID"` - Timestamp string `json:"timestamp"` - Code int `json:"code"` - Error error `json:"error"` -} - -// MakeOutEvent Event send in WS when characters are received on stdout/stderr -const MakeOutEvent = "make:output" - -// MakeExitEvent Event send in WS when command exited -const MakeExitEvent = "make:exit" - -var makeCommandID = 1 - -func (s *APIService) buildMake(c *gin.Context) { - var args MakeArgs - - if c.BindJSON(&args) != nil { - common.APIError(c, "Invalid arguments") - return - } - - sess := s.sessions.Get(c) - if sess == nil { - common.APIError(c, "Unknown sessions") - return - } - sop := sess.IOSocket - if sop == nil { - common.APIError(c, "Websocket not established") - return - } - - // Allow to pass id in url (/make/:id) or as JSON argument - idArg := c.Param("id") - if idArg == "" { - idArg = args.ID - } - if idArg == "" { - common.APIError(c, "Invalid id") - return - } - id, err := s.mfolders.ResolveID(idArg) - if err != nil { - common.APIError(c, err.Error()) - return - } - pf := s.mfolders.Get(id) - if pf == nil { - common.APIError(c, "Unknown id") - return - } - folder := *pf - prj := folder.GetConfig() - - execTmo := args.CmdTimeout - if execTmo == 0 { - // TODO get default timeout from config.json file - execTmo = 24 * 60 * 60 // 1 day - } - - // TODO merge all code below with exec.go - - // Define callback for output - var oCB common.EmitOutputCB - oCB = func(sid string, cmdID string, stdout, stderr string, data *map[string]interface{}) { - // IO socket can be nil when disconnected - so := s.sessions.IOSocketGet(sid) - if so == nil { - s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%s", MakeOutEvent, sid, cmdID) - return - } - - // Retrieve project ID and RootPath - prjID := (*data)["ID"].(string) - prjRootPath := (*data)["RootPath"].(string) - - // Cleanup any references to internal rootpath in stdout & stderr - stdout = strings.Replace(stdout, prjRootPath, "", -1) - stderr = strings.Replace(stderr, prjRootPath, "", -1) - - s.log.Debugf("%s emitted - WS sid %s - id:%d - prjID:%s", MakeOutEvent, sid, id, prjID) - - // FIXME replace by .BroadcastTo a room - err := (*so).Emit(MakeOutEvent, MakeOutMsg{ - CmdID: cmdID, - Timestamp: time.Now().String(), - Stdout: stdout, - Stderr: stderr, - }) - if err != nil { - s.log.Errorf("WS Emit : %v", err) - } - } - - // Define callback for output - eCB := func(sid string, cmdID string, code int, err error, data *map[string]interface{}) { - s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", cmdID, code, err) - - // IO socket can be nil when disconnected - so := s.sessions.IOSocketGet(sid) - if so == nil { - s.log.Infof("%s not emitted - WS closed (id:%s", MakeExitEvent, cmdID) - return - } - - // Retrieve project ID and RootPath - prjID := (*data)["ID"].(string) - exitImm := (*data)["ExitImmediate"].(bool) - - // XXX - workaround to be sure that Syncthing detected all changes - if err := s.mfolders.ForceSync(prjID); err != nil { - s.log.Errorf("Error while syncing folder %s: %v", prjID, err) - } - if !exitImm { - // Wait end of file sync - // FIXME pass as argument - tmo := 60 - for t := tmo; t > 0; t-- { - s.log.Debugf("Wait file insync for %s (%d/%d)", prjID, t, tmo) - if sync, err := s.mfolders.IsFolderInSync(prjID); sync || err != nil { - if err != nil { - s.log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err) - } - break - } - time.Sleep(time.Second) - } - } - - // FIXME replace by .BroadcastTo a room - e := (*so).Emit(MakeExitEvent, MakeExitMsg{ - CmdID: id, - Timestamp: time.Now().String(), - Code: code, - Error: err, - }) - if e != nil { - s.log.Errorf("WS Emit : %v", e) - } - } - - // Unique ID for each commands - if args.CmdID == "" { - args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(makeCommandID) - makeCommandID++ - } - cmd := []string{} - - // Retrieve env command regarding Sdk ID - if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); len(envCmd) > 0 { - cmd = append(cmd, envCmd...) - cmd = append(cmd, "&&") - } - - cmd = append(cmd, "cd", folder.GetFullPath(args.RPath), "&&", "make") - if len(args.Args) > 0 { - cmd = append(cmd, args.Args...) - } - - s.log.Debugf("Execute [Cmd ID %d]: %v", args.CmdID, cmd) - - data := make(map[string]interface{}) - data["ID"] = prj.ID - data["RootPath"] = prj.RootPath - data["ExitImmediate"] = args.ExitImmediate - - err = common.ExecPipeWs(cmd, args.Env, sop, sess.ID, args.CmdID, execTmo, s.log, oCB, eCB, &data) - if err != nil { - common.APIError(c, err.Error()) - return - } - - c.JSON(http.StatusOK, - gin.H{ - "status": "OK", - "cmdID": args.CmdID, - }) -} diff --git a/lib/apiv1/sdks.go b/lib/apiv1/sdks.go deleted file mode 100644 index f67a0ef..0000000 --- a/lib/apiv1/sdks.go +++ /dev/null @@ -1,29 +0,0 @@ -package apiv1 - -import ( - "net/http" - - "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" -) - -// getSdks returns all SDKs configuration -func (s *APIService) getSdks(c *gin.Context) { - c.JSON(http.StatusOK, s.sdks.GetAll()) -} - -// getSdk returns a specific Sdk configuration -func (s *APIService) getSdk(c *gin.Context) { - id, err := s.sdks.ResolveID(c.Param("id")) - if err != nil { - common.APIError(c, err.Error()) - return - } - sdk := s.sdks.Get(id) - if sdk.Profile == "" { - common.APIError(c, "Invalid id") - return - } - - c.JSON(http.StatusOK, sdk) -} diff --git a/lib/apiv1/version.go b/lib/apiv1/version.go deleted file mode 100644 index 8f928ec..0000000 --- a/lib/apiv1/version.go +++ /dev/null @@ -1,26 +0,0 @@ -package apiv1 - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -type version struct { - ID string `json:"id"` - Version string `json:"version"` - APIVersion string `json:"apiVersion"` - VersionGitTag string `json:"gitTag"` -} - -// getInfo : return various information about server -func (s *APIService) getVersion(c *gin.Context) { - response := version{ - ID: s.cfg.ServerUID, - Version: s.cfg.Version, - APIVersion: s.cfg.APIVersion, - VersionGitTag: s.cfg.VersionGitTag, - } - - c.JSON(http.StatusOK, response) -} |