Fixed /exec input stream and /signal.
[src/xds/xds-agent.git] / lib / agent / apiv1-exec.go
index 3cb4d23..5fdfb9d 100644 (file)
@@ -1,37 +1,22 @@
 package agent
 
 import (
-       "encoding/json"
-       "io/ioutil"
        "net/http"
 
        "github.com/franciscocpg/reflectme"
        "github.com/gin-gonic/gin"
-       "github.com/iotbzh/xds-agent/lib/apiv1"
+       "github.com/iotbzh/xds-agent/lib/xaapiv1"
        common "github.com/iotbzh/xds-common/golib"
+       "github.com/iotbzh/xds-server/lib/xsapiv1"
        uuid "github.com/satori/go.uuid"
 )
 
 // ExecCmd executes remotely a command
 func (s *APIService) execCmd(c *gin.Context) {
-       s._execRequest("/exec", c)
-}
-
-// execSignalCmd executes remotely a command
-func (s *APIService) execSignalCmd(c *gin.Context) {
-       s._execRequest("/signal", c)
-}
-
-func (s *APIService) _execRequest(cmd string, c *gin.Context) {
-       data, err := c.GetRawData()
-       if err != nil {
-               common.APIError(c, err.Error())
-       }
 
-       args := apiv1.ExecArgs{}
-       // XXX - we cannot use c.BindJSON, so directly unmarshall it
-       // (see https://github.com/gin-gonic/gin/issues/1078)
-       if err := json.Unmarshal(data, &args); err != nil {
+       args := xaapiv1.ExecArgs{}
+       if err := c.BindJSON(&args); err != nil {
+               s.Log.Warningf("/exec invalid args, err=%v", err)
                common.APIError(c, "Invalid arguments")
                return
        }
@@ -70,17 +55,36 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
                return
        }
 
-       // Forward XDSServer WS events to client WS
-       // TODO removed static event name list and get it from XDSServer
-       evtList := []string{
-               apiv1.ExecInEvent,
-               apiv1.ExecOutEvent,
-               apiv1.ExecInferiorInEvent,
-               apiv1.ExecInferiorOutEvent,
+       // Forward input events from client to XDSServer through WS
+       // TODO use XDSServer events names definition
+       evtInList := []string{
+               xaapiv1.ExecInEvent,
+               xaapiv1.ExecInferiorInEvent,
+       }
+       for _, evName := range evtInList {
+               evN := evName
+               err := (*sock).On(evN, func(stdin string) {
+                       if s.LogLevelSilly {
+                               s.Log.Debugf("EXEC EVENT IN (%s) <<%v>>", evN, stdin)
+                       }
+                       svr.EventEmit(evN, stdin)
+               })
+               if err != nil {
+                       msgErr := "Error while registering WS for " + evN + " event"
+                       s.Log.Errorf(msgErr, ", err: %v", err)
+                       common.APIError(c, msgErr)
+                       return
+               }
        }
 
+       // Forward output events from XDSServer to client through WS
+       // TODO use XDSServer events names definition
        var fwdFuncID []uuid.UUID
-       for _, evName := range evtList {
+       evtOutList := []string{
+               xaapiv1.ExecOutEvent,
+               xaapiv1.ExecInferiorOutEvent,
+       }
+       for _, evName := range evtOutList {
                evN := evName
                fwdFunc := func(pData interface{}, evData interface{}) error {
                        sid := pData.(string)
@@ -94,6 +98,10 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
                        // Add sessionID to event Data
                        reflectme.SetField(evData, "sessionID", sid)
 
+                       if s.LogLevelSilly {
+                               s.Log.Debugf("EXEC EVENT OUT (%s) <<%v>>", evN, evData)
+                       }
+
                        // Forward event to Client/Dashboard
                        (*so).Emit(evN, evData)
                        return nil
@@ -108,9 +116,12 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
 
        // Handle Exit event separately to cleanup registered listener
        var exitFuncID uuid.UUID
-       exitFunc := func(pData interface{}, evData interface{}) error {
-               evN := apiv1.ExecExitEvent
-               sid := pData.(string)
+       exitFunc := func(privD interface{}, evData interface{}) error {
+               evN := xaapiv1.ExecExitEvent
+
+               pData := privD.(map[string]string)
+               sid := pData["sessID"]
+               prjID := pData["prjID"]
 
                // Add sessionID to event Data
                reflectme.SetField(evData, "sessionID", sid)
@@ -123,32 +134,100 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
                        s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
                }
 
+               prj := s.projects.Get(prjID)
+               if prj != nil {
+                       evD := evData.(map[string]interface{})
+                       cmdIDData, cmdIDExist := evD["cmdID"]
+                       svr := (*prj).GetServer()
+                       if svr != nil && cmdIDExist {
+                               svr.CommandDelete(cmdIDData.(string))
+                       } else {
+                               s.Log.Infof("%s: cannot retrieve server for sid=%s, prjID=%s, evD=%v", evN, sid, prjID, evD)
+                       }
+               } else {
+                       s.Log.Infof("%s: cannot retrieve project for sid=%s, prjID=%s", evN, sid, prjID)
+               }
+
                // cleanup listener
-               for i, evName := range evtList {
+               for i, evName := range evtOutList {
                        svr.EventOff(evName, fwdFuncID[i])
                }
                svr.EventOff(evN, exitFuncID)
 
                return nil
        }
-       exitFuncID, err = svr.EventOn(apiv1.ExecExitEvent, sess.ID, exitFunc)
+
+       prjCfg := (*prj).GetProject()
+       privData := map[string]string{"sessID": sess.ID, "prjID": prjCfg.ID}
+       exitFuncID, err = svr.EventOn(xaapiv1.ExecExitEvent, privData, exitFunc)
        if err != nil {
                common.APIError(c, err.Error())
                return
        }
 
        // Forward back command to right server
-       response, err := svr.SendCommand(cmd, data)
-       if err != nil {
+       res := xsapiv1.ExecResult{}
+       xsArgs := &xsapiv1.ExecArgs{
+               ID:              args.ID,
+               SdkID:           args.SdkID,
+               CmdID:           args.CmdID,
+               Cmd:             args.Cmd,
+               Args:            args.Args,
+               Env:             args.Env,
+               RPath:           args.RPath,
+               TTY:             args.TTY,
+               TTYGdbserverFix: args.TTYGdbserverFix,
+               ExitImmediate:   args.ExitImmediate,
+               CmdTimeout:      args.CmdTimeout,
+       }
+       if err := svr.CommandExec(xsArgs, &res); err != nil {
                common.APIError(c, err.Error())
                return
        }
 
-       // Decode response
-       body, err := ioutil.ReadAll(response.Body)
-       if err != nil {
-               common.APIError(c, "Cannot read response body")
+       // Add command to running commands list
+       if err := svr.CommandAdd(res.CmdID, xsArgs); err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       c.JSON(http.StatusOK, xaapiv1.ExecResult{Status: res.Status, CmdID: res.CmdID})
+}
+
+// execSignalCmd executes remotely the signal command
+func (s *APIService) execSignalCmd(c *gin.Context) {
+
+       args := xaapiv1.ExecSignalArgs{}
+       if err := c.BindJSON(&args); err != nil {
+               s.Log.Warningf("/signal invalid args, err=%v", err)
+               common.APIError(c, "Invalid arguments")
                return
        }
-       c.JSON(http.StatusOK, string(body))
+
+       // Retrieve on which xds-server the command is running
+       var svr *XdsServer
+       var dataCmd interface{}
+       for _, svr = range s.xdsServers {
+               dataCmd = svr.CommandGet(args.CmdID)
+               if dataCmd != nil {
+                       break
+               }
+       }
+       if dataCmd == nil {
+               common.APIError(c, "Cannot retrieve XDS Server for this cmdID")
+               return
+       }
+
+       // Forward back command to right server
+       res := xsapiv1.ExecSigResult{}
+       xsArgs := &xsapiv1.ExecSignalArgs{
+               CmdID:  args.CmdID,
+               Signal: args.Signal,
+       }
+       if err := svr.CommandSignal(xsArgs, &res); err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       c.JSON(http.StatusOK, xaapiv1.ExecSignalResult{Status: res.Status, CmdID: res.CmdID})
 }