/* * 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 }