X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=lib%2Fagent%2Fapiv1-exec.go;h=d74269323bd3619474d869b33518c533b532ec16;hb=4695ce02620407a5074125eaa5b257dd0e73758a;hp=37070f7afab871cbef6dbf3ad447e6d9aaa3f064;hpb=4695555e178bcabe54c5bf82117c9c4cef5440b5;p=src%2Fxds%2Fxds-agent.git diff --git a/lib/agent/apiv1-exec.go b/lib/agent/apiv1-exec.go index 37070f7..d742693 100644 --- a/lib/agent/apiv1-exec.go +++ b/lib/agent/apiv1-exec.go @@ -1,53 +1,53 @@ +/* + * Copyright (C) 2017-2018 "IoT.bzh" + * Author Sebastien Douheret + * + * 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/lib/xaapiv1" + common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib" + "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1" + "github.com/franciscocpg/reflectme" "github.com/gin-gonic/gin" - common "github.com/iotbzh/xds-common/golib" uuid "github.com/satori/go.uuid" ) -// ExecArgs Only define used fields -type ExecArgs struct { - ID string `json:"id" binding:"required"` - CmdID string `json:"cmdID"` // command unique ID -} - -var execCmdID = 1 - // 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 := 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 } // First get Project ID to retrieve Server ID and send command to right server - id := c.Param("id") - if id == "" { - id = args.ID + iid := c.Param("id") + if iid == "" { + iid = args.ID + } + id, err := s.projects.ResolveID(iid) + if err != nil { + common.APIError(c, err.Error()) + return } - prj := s.projects.Get(id) if prj == nil { common.APIError(c, "Unknown id") @@ -72,23 +72,54 @@ 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{ - "exec:input", - "exec:output", - "exec:inferior-input", - "exec:inferior-output", + // 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 + } + } + + // 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, } - so := *sock - fwdFuncID := []uuid.UUID{} - for _, evName := range evtList { + for _, evName := range evtOutList { evN := evName - fwdFunc := func(evData interface{}) { + fwdFunc := func(pData interface{}, evData interface{}) error { + sid := pData.(string) + // 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)", evN, sid) + 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) + (*so).Emit(evN, evData) + return nil } - id, err := svr.EventOn(evN, fwdFunc) + id, err := svr.EventOn(evN, sess.ID, fwdFunc) if err != nil { common.APIError(c, err.Error()) return @@ -98,34 +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(evData interface{}) { - so.Emit("exec:exit", evData) + 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 { + (*so).Emit(evN, evData) + } else { + 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("exec:exit", exitFuncID) + svr.EventOff(evN, exitFuncID) + + return nil } - exitFuncID, err = svr.EventOn("exec:exit", 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 + } + + // 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, string(body)) + c.JSON(http.StatusOK, xaapiv1.ExecSignalResult{Status: res.Status, CmdID: res.CmdID}) }