aboutsummaryrefslogtreecommitdiffstats
path: root/lib/agent
diff options
context:
space:
mode:
authorSebastien Douheret <sebastien.douheret@iot.bzh>2018-02-23 18:50:31 +0100
committerSebastien Douheret <sebastien.douheret@iot.bzh>2018-02-23 19:15:02 +0100
commitbefb10519daefcbee85950a4e574678fe4b7f402 (patch)
treeb4f95889cfacbdf9909c3ccd11dc1f0f8bedb417 /lib/agent
parent38e0baedab9da4516cdc97b526392cbc2c7da67e (diff)
Added target and terminal support.v1.1.0
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
Diffstat (limited to 'lib/agent')
-rw-r--r--lib/agent/apiv1-browse.go2
-rw-r--r--lib/agent/apiv1-config.go2
-rw-r--r--lib/agent/apiv1-targets.go172
-rw-r--r--lib/agent/apiv1.go8
-rw-r--r--lib/agent/webserver.go12
-rw-r--r--lib/agent/xdsserver.go69
6 files changed, 254 insertions, 11 deletions
diff --git a/lib/agent/apiv1-browse.go b/lib/agent/apiv1-browse.go
index dcfe79b..f1b25e8 100644
--- a/lib/agent/apiv1-browse.go
+++ b/lib/agent/apiv1-browse.go
@@ -37,7 +37,7 @@ func (s *APIService) browseFS(c *gin.Context) {
response := apiDirectory{
Dir: []directory{
- directory{Name: "TODO SEB"},
+ directory{Name: "TODO"},
},
}
diff --git a/lib/agent/apiv1-config.go b/lib/agent/apiv1-config.go
index b24dc21..cfecd82 100644
--- a/lib/agent/apiv1-config.go
+++ b/lib/agent/apiv1-config.go
@@ -66,7 +66,7 @@ func (s *APIService) setConfig(c *gin.Context) {
}
}
- // Add new XDS Server
+ // Add new / unconnected XDS Server
for _, svr := range cfgArg.Servers {
if svr.Connected && svr.ID != "" {
continue
diff --git a/lib/agent/apiv1-targets.go b/lib/agent/apiv1-targets.go
new file mode 100644
index 0000000..5a7862a
--- /dev/null
+++ b/lib/agent/apiv1-targets.go
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 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 (
+ "fmt"
+ "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"
+ uuid "github.com/satori/go.uuid"
+)
+
+// targetsPassthroughInit Declare passthrough routes for targets
+func (s *APIService) targetsPassthroughInit(svr *XdsServer) error {
+ svr.PassthroughGet("/targets")
+ svr.PassthroughGet("/targets/:id")
+ svr.PassthroughPost("/targets")
+ svr.PassthroughDelete("/targets/:id")
+
+ svr.PassthroughGet("/targets/:id/terminals")
+ svr.PassthroughGet("/targets/:id/terminals/:tid")
+ svr.PassthroughPost("/targets/:id/terminals")
+ svr.PassthroughPut("/targets/:id/terminals/:tid")
+ svr.PassthroughDelete("/targets/:id/terminals/:tid")
+
+ svr.apiRouter.POST("/targets/:id/terminals/:tid/open", s.TargetTerminalOpen)
+
+ svr.PassthroughPost("/targets/:id/terminals/:tid/close")
+ svr.PassthroughPost("/targets/:id/terminals/:tid/resize")
+ svr.PassthroughPost("/targets/:id/terminals/:tid/signal")
+ svr.PassthroughPost("/targets/:id/terminals/:tid/signal/:sig")
+
+ return nil
+}
+
+// GetServerFromTargetID Retrieve XDS Server definition from a target ID
+func (s *APIService) GetServerFromTargetID(targetID, termID string) (*XdsServer, string, error) {
+
+ // FIXME add cache (but take care to support partial term ID)
+ for _, svr := range s.xdsServers {
+ term := xsapiv1.TerminalConfig{}
+ if err := svr.CommandTgtTerminalGet(targetID, termID, &term); err == nil {
+ return svr, term.ID, nil
+ }
+ }
+ return nil, "", fmt.Errorf("Cannot identify XDS Server")
+}
+
+// TargetTerminalOpen Open a target terminal/console
+func (s *APIService) TargetTerminalOpen(c *gin.Context) {
+
+ // First retrieve Server ID and send command to right server
+ targetID := c.Param("id")
+ svr, termID, err := s.GetServerFromTargetID(targetID, c.Param("tid"))
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ // Retrieve session info
+ sess := s.sessions.Get(c)
+ if sess == nil {
+ common.APIError(c, "Unknown sessions")
+ return
+ }
+ sock := sess.IOSocket
+ if sock == nil {
+ common.APIError(c, "Websocket not established")
+ return
+ }
+
+ // Forward input events from client to XDSServer through WS
+ err = (*sock).On(xsapiv1.TerminalInEvent, func(stdin string) {
+ s.LogSillyf("TARGET TERMINAL EVENT IN (%s) <<%v>>", xsapiv1.TerminalInEvent, stdin)
+ svr.EventEmit(xaapiv1.TerminalInEvent, stdin)
+ })
+ if err != nil {
+ msgErr := "Error while registering WS for " + xsapiv1.TerminalInEvent + " event"
+ s.Log.Errorf(msgErr, ", err: %v", err)
+ common.APIError(c, msgErr)
+ return
+ }
+
+ // Forward output events from XDSServer to client through WS
+ var outFwdFuncID uuid.UUID
+ outFwdFunc := 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)", xaapiv1.TerminalOutEvent, sid)
+ return nil
+ }
+
+ // Add sessionID to event Data
+ reflectme.SetField(evData, "sessionID", sid)
+
+ s.LogSillyf("TARGET TERMINAL EVENT OUT (%s) <<%v>>", xaapiv1.TerminalOutEvent, evData)
+
+ // Forward event to Client/Dashboard
+ (*so).Emit(xaapiv1.TerminalOutEvent, evData)
+ return nil
+ }
+ outFwdFuncID, err = svr.EventOn(xsapiv1.TerminalOutEvent, sess.ID, outFwdFunc)
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ // Handle Exit event separately to cleanup registered listener
+ var exitFuncID uuid.UUID
+ exitFunc := func(privD interface{}, evData interface{}) error {
+ evN := xaapiv1.TerminalExitEvent
+
+ pData := privD.(map[string]string)
+ sid := pData["sessID"]
+
+ // 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)
+ }
+
+ // cleanup listener
+ svr.EventOff(xaapiv1.TerminalOutEvent, outFwdFuncID)
+ svr.EventOff(evN, exitFuncID)
+
+ return nil
+ }
+
+ privData := map[string]string{
+ "sessID": sess.ID,
+ }
+ exitFuncID, err = svr.EventOn(xaapiv1.TerminalExitEvent, privData, exitFunc)
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ // Forward back command to right server
+ res := xsapiv1.TerminalConfig{}
+ if err := svr.CommandTgtTerminalOpen(targetID, termID, &res); err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ c.JSON(http.StatusOK, res)
+}
diff --git a/lib/agent/apiv1.go b/lib/agent/apiv1.go
index 1921f98..3ca84b9 100644
--- a/lib/agent/apiv1.go
+++ b/lib/agent/apiv1.go
@@ -49,7 +49,7 @@ func NewAPIV1(ctx *Context) *APIService {
s.apiRouter.GET("/config", s.getConfig)
s.apiRouter.POST("/config", s.setConfig)
- s.apiRouter.GET("/browse", s.browseFS)
+ // s.apiRouter.GET("/browse", s.browseFS)
s.apiRouter.GET("/projects", s.getProjects)
s.apiRouter.GET("/projects/:id", s.getProject)
@@ -118,6 +118,7 @@ func (s *APIService) AddXdsServer(cfg xdsconfig.XDSServerConf) (*XdsServer, erro
// Declare passthrough routes
s.sdksPassthroughInit(svr)
+ s.targetsPassthroughInit(svr)
// Register callback on Connection
svr.ConnectOn(func(server *XdsServer) error {
@@ -125,9 +126,9 @@ func (s *APIService) AddXdsServer(cfg xdsconfig.XDSServerConf) (*XdsServer, erro
// Add server to list
s.xdsServers[server.ID] = svr
- // Register event forwarder
+ // Register events forwarder
if err := s.sdksEventsForwardInit(server); err != nil {
- s.Log.Errorf("XDS Server %v - sdk event forwarding error: %v", server.ID, err)
+ s.Log.Errorf("XDS Server %v - sdk events forwarding error: %v", server.ID, err)
}
// Load projects
@@ -164,7 +165,6 @@ func (s *APIService) DelXdsServer(id string) error {
s.xdsServers[id].Close()
return nil
}
-}
// UpdateXdsServer Update XDS Server configuration settings
func (s *APIService) UpdateXdsServer(cfg xaapiv1.ServerCfg) error {
diff --git a/lib/agent/webserver.go b/lib/agent/webserver.go
index 2f24c6c..4deb738 100644
--- a/lib/agent/webserver.go
+++ b/lib/agent/webserver.go
@@ -246,13 +246,15 @@ func (s *WebServer) socketHandler(c *gin.Context) {
}
s.sIOServer.On("connection", func(so socketio.Socket) {
- s.Log.Debugf("WS Connected (WSID=%s, SID=%s)", so.Id(), sess.ID)
- s.sessions.UpdateIOSocket(sess.ID, &so)
+ sessID := sess.ID
+
+ s.Log.Debugf("WS Connected (sessID=%s, WS SID=%s)", sessID, so.Id())
+ s.sessions.UpdateIOSocket(sessID, &so)
so.On("disconnection", func() {
- s.Log.Debugf("WS disconnected (WSID=%s, SID=%s)", so.Id(), sess.ID)
- s.events.UnRegister(xaapiv1.EVTAll, sess.ID)
- s.sessions.UpdateIOSocket(sess.ID, nil)
+ s.Log.Debugf("WS disconnected (sessID=%s, WS SID=%s)", sessID, so.Id())
+ s.events.UnRegister(xaapiv1.EVTAll, sessID)
+ s.sessions.UpdateIOSocket(sessID, nil)
})
})
diff --git a/lib/agent/xdsserver.go b/lib/agent/xdsserver.go
index 1c715f6..c08bfb1 100644
--- a/lib/agent/xdsserver.go
+++ b/lib/agent/xdsserver.go
@@ -208,6 +208,22 @@ func (xs *XdsServer) CommandSignal(args *xsapiv1.ExecSignalArgs, res *xsapiv1.Ex
return xs.client.Post("/signal", args, res)
}
+// CommandTgtTerminalGet Send GET request to retrieve info of a target terminals
+func (xs *XdsServer) CommandTgtTerminalGet(targetID, termID string, res *xsapiv1.TerminalConfig) error {
+ return xs.client.Get("/targets/"+targetID+"/terminals/"+termID, res)
+}
+
+// CommandTgtTerminalOpen Send POST request to open a target terminal
+func (xs *XdsServer) CommandTgtTerminalOpen(targetID string, termID string, res *xsapiv1.TerminalConfig) error {
+ var empty interface{}
+ return xs.client.Post("/targets/"+targetID+"/terminals/"+termID+"/open", &empty, res)
+}
+
+// CommandTgtTerminalSignal Send POST request to send a signal to a target terminal
+func (xs *XdsServer) CommandTgtTerminalSignal(args *xsapiv1.TerminalSignalArgs, res *xsapiv1.TerminalConfig) error {
+ return xs.client.Post("/signal", args, res)
+}
+
// SetAPIRouterGroup .
func (xs *XdsServer) SetAPIRouterGroup(r *gin.RouterGroup) {
xs.apiRouter = r
@@ -290,6 +306,56 @@ func (xs *XdsServer) PassthroughPost(url string) {
})
}
+// PassthroughPut Used to declare a route that sends directly a PUT request to XDS Server
+func (xs *XdsServer) PassthroughPut(url string) {
+ if xs.apiRouter == nil {
+ xs.Log.Errorf("apiRouter not set !")
+ return
+ }
+
+ xs.apiRouter.PUT(url, func(c *gin.Context) {
+ var err error
+ var data interface{}
+
+ // Get raw body
+ body, err := c.GetRawData()
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ // Take care of param (eg. id in /projects/:id)
+ nURL := url
+ if strings.Contains(url, ":") {
+ nURL = strings.TrimPrefix(c.Request.URL.Path, xs.APIURL)
+ }
+
+ // Send Put request
+ response, err := xs.client.HTTPPutWithRes(nURL, string(body))
+ if err != nil {
+ goto httpError
+ }
+ if response.StatusCode != 200 {
+ err = fmt.Errorf(response.Status)
+ return
+ }
+ err = json.Unmarshal(xs.client.ResponseToBArray(response), &data)
+ if err != nil {
+ goto httpError
+ }
+
+ c.JSON(http.StatusOK, data)
+ return
+
+ /* Handle error case */
+ httpError:
+ if strings.Contains(err.Error(), "connection refused") {
+ xs._Disconnected()
+ }
+ common.APIError(c, err.Error())
+ })
+}
+
// PassthroughDelete Used to declare a route that sends directly a Delete request to XDS Server
func (xs *XdsServer) PassthroughDelete(url string) {
if xs.apiRouter == nil {
@@ -660,6 +726,9 @@ func (xs *XdsServer) _SocketConnect() error {
// _Disconnected Set XDS Server as disconnected
func (xs *XdsServer) _Disconnected() error {
// Clear all register events as socket is closed
+ xs.sockEventsLock.Lock()
+ defer xs.sockEventsLock.Unlock()
+
for k := range xs.sockEvents {
delete(xs.sockEvents, k)
}