diff options
Diffstat (limited to 'lib/xdsserver')
-rw-r--r-- | lib/xdsserver/apiv1-exec.go | 3 | ||||
-rw-r--r-- | lib/xdsserver/apiv1-sdks.go | 8 | ||||
-rw-r--r-- | lib/xdsserver/apiv1-updates.go | 40 | ||||
-rw-r--r-- | lib/xdsserver/apiv1.go | 3 | ||||
-rw-r--r-- | lib/xdsserver/sdk.go | 2 | ||||
-rw-r--r-- | lib/xdsserver/sdks.go | 2 | ||||
-rw-r--r-- | lib/xdsserver/webserver.go | 11 | ||||
-rw-r--r-- | lib/xdsserver/xds-server-update.go | 164 | ||||
-rw-r--r-- | lib/xdsserver/xdsserver.go | 50 |
9 files changed, 260 insertions, 23 deletions
diff --git a/lib/xdsserver/apiv1-exec.go b/lib/xdsserver/apiv1-exec.go index 57ea1f1..aea34e4 100644 --- a/lib/xdsserver/apiv1-exec.go +++ b/lib/xdsserver/apiv1-exec.go @@ -261,6 +261,7 @@ func (s *APIService) execCmd(c *gin.Context) { 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) + defer LockXdsUpdateCounter(s.Context, false) // Close client tty defer func() { if gdbPty != nil { @@ -330,8 +331,10 @@ func (s *APIService) execCmd(c *gin.Context) { // Start command execution s.Log.Infof("Execute [Cmd ID %s]: %v %v", execWS.CmdID, execWS.Cmd, execWS.Args) + LockXdsUpdateCounter(s.Context, true) err = execWS.Start() if err != nil { + LockXdsUpdateCounter(s.Context, false) common.APIError(c, err.Error()) return } diff --git a/lib/xdsserver/apiv1-sdks.go b/lib/xdsserver/apiv1-sdks.go index 7bb9767..a943da5 100644 --- a/lib/xdsserver/apiv1-sdks.go +++ b/lib/xdsserver/apiv1-sdks.go @@ -74,8 +74,13 @@ func (s *APIService) installSdk(c *gin.Context) { return } + //increment lock xds counter: it will be decremented, + //when SDK is installed in file sdk.go in function ExitCB + //at line 308 or when install is aborted in following function + LockXdsUpdateCounter(s.Context, true) sdk, err := s.sdks.Install(id, args.Filename, args.Force, args.Timeout, args.InstallArgs, sess) if err != nil { + LockXdsUpdateCounter(s.Context, false) common.APIError(c, err.Error()) return } @@ -103,6 +108,7 @@ func (s *APIService) abortInstallSdk(c *gin.Context) { return } + LockXdsUpdateCounter(s.Context, false) c.JSON(http.StatusOK, sdk) } @@ -123,10 +129,12 @@ func (s *APIService) removeSdk(c *gin.Context) { s.Log.Debugln("Remove SDK id ", id) + LockXdsUpdateCounter(s.Context, true) delEntry, err := s.sdks.Remove(id, -1, sess) if err != nil { common.APIError(c, err.Error()) return } + LockXdsUpdateCounter(s.Context, false) c.JSON(http.StatusOK, delEntry) } diff --git a/lib/xdsserver/apiv1-updates.go b/lib/xdsserver/apiv1-updates.go new file mode 100644 index 0000000..2b878a6 --- /dev/null +++ b/lib/xdsserver/apiv1-updates.go @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017-2018 "IoT.bzh" + * Author Clément Bénier <clement.benier@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 xdsserver + +import ( + "net/http" + + common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib" + "github.com/gin-gonic/gin" +) + +// getXdsSrvUpdate : return various information about pkg update +func (s *APIService) getXdsSrvUpdate(c *gin.Context) { + response := GetXdsSrvUpdate(s.Context) + c.JSON(http.StatusOK, response) +} + +// xdsSrvUpdate: update xds server package +func (s *APIService) xdsSrvUpdate(c *gin.Context) { + if err := UpdateXdsServer(s.Context); err != 0 { + common.APIError(c, "cannot update package") + return + } + c.JSON(http.StatusOK, "OK") +} diff --git a/lib/xdsserver/apiv1.go b/lib/xdsserver/apiv1.go index e0bfa7f..e394d9e 100644 --- a/lib/xdsserver/apiv1.go +++ b/lib/xdsserver/apiv1.go @@ -78,5 +78,8 @@ func NewAPIV1(ctx *Context) *APIService { s.apiRouter.POST("/targets/:id/terminals/:tid/signal", s.signalTgtTerm) s.apiRouter.POST("/targets/:id/terminals/:tid/signal/:sig", s.signalTgtTerm) + s.apiRouter.GET("/updates", s.getXdsSrvUpdate) + s.apiRouter.POST("/updates", s.xdsSrvUpdate) + return s } diff --git a/lib/xdsserver/sdk.go b/lib/xdsserver/sdk.go index 6cd2619..931db34 100644 --- a/lib/xdsserver/sdk.go +++ b/lib/xdsserver/sdk.go @@ -305,6 +305,8 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string, // Define callback for output s.installCmd.ExitCB = func(e *eows.ExecOverWS, code int, exitError error) { + defer LockXdsUpdateCounter(s.Context, false) + // paranoia data := e.UserData sdkID := (*data)["SDKID"].(string) diff --git a/lib/xdsserver/sdks.go b/lib/xdsserver/sdks.go index 5a8a1fa..685f19d 100644 --- a/lib/xdsserver/sdks.go +++ b/lib/xdsserver/sdks.go @@ -50,7 +50,7 @@ func SDKsConstructor(ctx *Context) (*SDKs, error) { scriptsDir := ctx.Config.FileConf.SdkScriptsDir if !common.Exists(scriptsDir) { - // allow to use scripts/sdk in debug mode + // allow to use scripts/sdk when debugging with vscode(EXEPATH=WORKSPACE) scriptsDir = filepath.Join(filepath.Dir(ctx.Config.FileConf.SdkScriptsDir), "scripts", "sdks") if !common.Exists(scriptsDir) { return &s, fmt.Errorf("scripts directory doesn't exist (%v)", scriptsDir) diff --git a/lib/xdsserver/webserver.go b/lib/xdsserver/webserver.go index 24456b9..a50b41e 100644 --- a/lib/xdsserver/webserver.go +++ b/lib/xdsserver/webserver.go @@ -84,6 +84,7 @@ func (s *WebServer) Serve() error { s.router.Use(gin.Recovery()) s.router.Use(s.middlewareXDSDetails()) s.router.Use(s.middlewareCORS()) + s.router.Use(s.lockRequest()) // Create REST API s.api = NewAPIV1(s.Context) @@ -172,6 +173,16 @@ func (s *WebServer) middlewareCORS() gin.HandlerFunc { } } +//lockRequest handles to increment/decrement xds package update +//to avoid updating xds-server when request is done +func (s *WebServer) lockRequest() gin.HandlerFunc { + return func(c *gin.Context) { + LockXdsUpdateCounter(s.Context, true) + c.Next() + LockXdsUpdateCounter(s.Context, false) + } +} + // socketHandler is the handler for the "main" websocket connection func (s *WebServer) socketHandler(c *gin.Context) { diff --git a/lib/xdsserver/xds-server-update.go b/lib/xdsserver/xds-server-update.go new file mode 100644 index 0000000..cd9d6b1 --- /dev/null +++ b/lib/xdsserver/xds-server-update.go @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Clément Bénier <clement.benier@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 xdsserver + +import ( + "os/exec" + "path" + "path/filepath" + "strings" + sc "sync" + "time" + + common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib" + "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xdsconfig" + "gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xsapiv1" +) + +const ( + scriptXdsSrvUpdate = "updateXdsServerPackage" + scriptGetXdsSrvUpdate = "getXdsServerPackage" + scriptXdsSrvRestart = "restartXdsServer" +) + +//LockXdsUpdate allows to lock xds-server avoiding restart +type LockXdsUpdate struct { + sc.Mutex + LockCpt int +} + +//LockXdsUpdateCounter Inc/decrement lock counter +func LockXdsUpdateCounter(ctx *Context, inc bool) { + if inc { + //paranoia lock count > 10 + if ctx.lockXdsSrvUpdate.LockCpt > 10 { + ctx.Log.Errorf("lock counter value is abnormally high: LockCpt=%v", ctx.lockXdsSrvUpdate.LockCpt) + } + ctx.lockXdsSrvUpdate.Lock() + ctx.lockXdsSrvUpdate.LockCpt++ + ctx.lockXdsSrvUpdate.Unlock() + } else { + //paranoia lock count < 0 + if ctx.lockXdsSrvUpdate.LockCpt <= 0 { + ctx.Log.Errorf("lock counter value is lower than 0: LockCpt=%v", ctx.lockXdsSrvUpdate.LockCpt) + return + } + ctx.lockXdsSrvUpdate.Lock() + ctx.lockXdsSrvUpdate.LockCpt-- + ctx.lockXdsSrvUpdate.Unlock() + } +} + +//MonitorUpdates try to update xds-server package at first +// then monitor updates +func MonitorUpdates(ctx *Context) { + UpdateXdsServer(ctx) //try to update at startup + + updateTime, err := time.ParseDuration(ctx.Config.FileConf.XdsSrvUpdateTime) + if err != nil { + ctx.Log.Errorf("Wrong format type of XdsSrvUpdateTime\n"+ + "err=%v \n"+ + "Valid time units are ns, us, ms, s, m, h\n"+ + "Here an example: 1h10m10s\n"+ + "So, default value is set %v", err, xdsconfig.DefaultXdsSrvUpdateTime) + updateTime, _ = time.ParseDuration(xdsconfig.DefaultXdsSrvUpdateTime) + } + ctx.Log.Infof("Update time for package xds-server is %v", updateTime) + go func(ctx *Context) { + for { + currentUpdateTime := updateTime + for currentUpdateTime > 0 { + time.Sleep(currentUpdateTime) + currentUpdateTime = UpdateXdsServer(ctx) + } + } + }(ctx) +} + +func getScriptsDir(ctx *Context) string { + scriptsDir := ctx.Config.FileConf.XdsUtilsScriptsDir + if !common.Exists(scriptsDir) { + // allow to use scripts/xds-utils when debugging with vscode(EXEPATH=WORKSPACE) + scriptsDir = filepath.Join(filepath.Dir(ctx.Config.FileConf.XdsUtilsScriptsDir), "scripts", "xds-utils") + if !common.Exists(scriptsDir) { + ctx.Log.Errorf("scripts directory doesn't exist (%v)", scriptsDir) + } + } + return scriptsDir +} + +// UpdateXdsServer launches update package xds-server script +func UpdateXdsServer(ctx *Context) time.Duration { + timeToRestartIfBusy := 0 * time.Minute + scriptsDir := getScriptsDir(ctx) + + ctx.Log.Infof("Trying to update xds-server package, "+ + "xds-utils scripts dir: %s", scriptsDir) + + //launch xds-server update package script + cmd := exec.Command(path.Join(scriptsDir, scriptXdsSrvUpdate)) + _, err := cmd.CombinedOutput() + if err != nil { + ctx.Log.Errorf("Cannot update xds-server package err=%v", err) + return 0 + } + + //launch xds-server restart script + cmd = exec.Command(path.Join(scriptsDir, scriptXdsSrvRestart)) + ctx.lockXdsSrvUpdate.Lock() + if ctx.lockXdsSrvUpdate.LockCpt == 0 { //no action in progress + _, err = cmd.CombinedOutput() + if err != nil { + ctx.Log.Errorf("Cannot restart xds-server service err=%v", err) + return 0 + } + } else { + timeToRestartIfBusy = 1 * time.Minute + ctx.Log.Infof("Cannot restart xds-server service because "+ + "xds-server has an action in progress, trying to restart in a %v", timeToRestartIfBusy) + } + ctx.lockXdsSrvUpdate.Unlock() + + return timeToRestartIfBusy +} + +// GetXdsSrvUpdate gets information about package +func GetXdsSrvUpdate(ctx *Context) xsapiv1.XdsSrvUpdate { + var xdsSrvUpdate xsapiv1.XdsSrvUpdate + scriptsDir := getScriptsDir(ctx) + + //exec getXdsSrvUpdate script + cmd := exec.Command(path.Join(scriptsDir, scriptGetXdsSrvUpdate)) + stdout, err := cmd.CombinedOutput() + if err != nil { + ctx.Log.Errorf("Cannot get xds-server package information err=%v", err) + return xdsSrvUpdate + } + + //stdout is formatting with 'version: xxxxx' + outputs := strings.Split(string(stdout[:]), "\n") + installedVersion := strings.Split(outputs[0], ": ")[1] + candidateVersion := strings.Split(outputs[1], ": ")[1] + ctx.Log.Infof("XdsSrvUpdate: candidateVersion:%v installedVersion:%v", candidateVersion, installedVersion) + xdsSrvUpdate = xsapiv1.XdsSrvUpdate{ + CurrentVersion: installedVersion, + NewerVersion: candidateVersion, + UpdateTime: ctx.Config.FileConf.XdsSrvUpdateTime, + } + return xdsSrvUpdate +} diff --git a/lib/xdsserver/xdsserver.go b/lib/xdsserver/xdsserver.go index 1079eba..e96ab57 100644 --- a/lib/xdsserver/xdsserver.go +++ b/lib/xdsserver/xdsserver.go @@ -37,22 +37,23 @@ const cookieMaxAge = "3600" // Context holds the XDS server context type Context struct { - ProgName string - Cli *cli.Context - Config *xdsconfig.Config - Log *logrus.Logger - LogLevelSilly bool - LogSillyf func(format string, args ...interface{}) - SThg *st.SyncThing - SThgCmd *exec.Cmd - SThgInotCmd *exec.Cmd - mfolders *Folders - sdks *SDKs - targets *Targets - WWWServer *WebServer - sessions *Sessions - events *Events - Exit chan os.Signal + ProgName string + Cli *cli.Context + Config *xdsconfig.Config + Log *logrus.Logger + LogLevelSilly bool + LogSillyf func(format string, args ...interface{}) + SThg *st.SyncThing + SThgCmd *exec.Cmd + SThgInotCmd *exec.Cmd + mfolders *Folders + sdks *SDKs + targets *Targets + WWWServer *WebServer + sessions *Sessions + events *Events + lockXdsSrvUpdate LockXdsUpdate + Exit chan os.Signal } // NewXdsServer Create a new instance of XDS server @@ -83,12 +84,13 @@ func NewXdsServer(cliCtx *cli.Context) *Context { // Define default configuration ctx := Context{ - ProgName: cliCtx.App.Name, - Cli: cliCtx, - Log: log, - LogLevelSilly: logSilly, - LogSillyf: sillyFunc, - Exit: make(chan os.Signal, 1), + ProgName: cliCtx.App.Name, + Cli: cliCtx, + Log: log, + LogLevelSilly: logSilly, + LogSillyf: sillyFunc, + lockXdsSrvUpdate: LockXdsUpdate{LockCpt: 0}, + Exit: make(chan os.Signal, 1), } // register handler on SIGTERM / exit @@ -206,6 +208,10 @@ func (ctx *Context) Run() (int, error) { // Sessions manager ctx.sessions = ClientSessionsConstructor(ctx, cookieMaxAge) + // Check if a new package version is available + // and monitor updates + MonitorUpdates(ctx) + // Run Web Server until exit requested (blocking call) if err = ctx.WWWServer.Serve(); err != nil { ctx.Log.Println(err) |