Use go module as dependency tool instead of glide
[src/xds/xds-agent.git] / lib / agent / apiv1-exec.go
index c199267..e711a19 100644 (file)
@@ -1,39 +1,39 @@
+/*
+ * Copyright (C) 2017-2018 "IoT.bzh"
+ * Author Sebastien Douheret <sebastien@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package agent
 
 import (
-       "encoding/json"
-       "io/ioutil"
        "net/http"
 
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1"
+       common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
+       "github.com/franciscocpg/reflectme"
        "github.com/gin-gonic/gin"
-       "github.com/iotbzh/xds-agent/lib/apiv1"
-       common "github.com/iotbzh/xds-common/golib"
        uuid "github.com/satori/go.uuid"
 )
 
-var execCmdID = 1
-var fwdFuncID []uuid.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
        }
@@ -72,16 +72,34 @@ 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) {
+                       s.LogSillyf("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
+               }
        }
 
-       for _, evName := range evtList {
+       // Forward output events from XDSServer to client through WS
+       // TODO use XDSServer events names definition
+       var fwdFuncID []uuid.UUID
+       evtOutList := []string{
+               xaapiv1.ExecOutEvent,
+               xaapiv1.ExecInferiorOutEvent,
+       }
+       for _, evName := range evtOutList {
                evN := evName
                fwdFunc := func(pData interface{}, evData interface{}) error {
                        sid := pData.(string)
@@ -92,6 +110,11 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
                                return nil
                        }
 
+                       // Add sessionID to event Data
+                       reflectme.SetField(evData, "sessionID", sid)
+
+                       s.LogSillyf("EXEC EVENT OUT (%s) <<%v>>", evN, evData)
+
                        // Forward event to Client/Dashboard
                        (*so).Emit(evN, evData)
                        return nil
@@ -106,45 +129,119 @@ 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)
 
                // IO socket can be nil when disconnected
                so := s.sessions.IOSocketGet(sid)
-               if so == nil {
+               if so != nil {
+                       (*so).Emit(evN, evData)
+               } else {
                        s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
-                       return nil
                }
 
-               (*so).Emit(evN, evData)
+               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,
+               LdLibPathNoReset: args.LdLibPathNoReset,
+               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})
 }