agent/pluginmanager/pluginmanager.go (517 lines of code) (raw):
package pluginmanager
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"math/rand"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/aliyun/aliyun_assist_client/thirdparty/sirupsen/logrus"
"github.com/aliyun/aliyun_assist_client/agent/log"
"github.com/aliyun/aliyun_assist_client/agent/metrics"
"github.com/aliyun/aliyun_assist_client/agent/pluginmodel"
"github.com/aliyun/aliyun_assist_client/agent/taskengine/timermanager"
"github.com/aliyun/aliyun_assist_client/agent/util"
"github.com/aliyun/aliyun_assist_client/agent/util/osutil"
"github.com/aliyun/aliyun_assist_client/common/fileutil"
"github.com/aliyun/aliyun_assist_client/common/pathutil"
)
/*
健康检查:两种并行的周期上报插件状态的方式
1. 调用 acs-plugin-manager --status,通过常驻插件的 --status 接口获得插件状态
2. 读取常驻插件目录下的 heartbeat 文件获得常驻插件的心跳时间戳,通过心跳时间戳判断插件状态
通过heartbeat判断插件状态后可以进行懒上报,只在插件状态变化时进行上报;默认每次都上报,受服务端返回的reportType字段控制
lastPluginStatusRecord 用来记录上一次各插件的状态
常驻插件升级:
1. 读取installed_plugins文件获得所有插件信息,检查是否有已安装的常驻插件
2. 常驻插件的版本信息上报服务端
3. 遍历服务端响应的升级列表
1. 执行 `acs-plugin-manager --exec --plugin <pluginName> --pluginVersion <pluginVersion> --params --upgrade`。升级插件
*/
var (
pluginHealthScanInterval = 15 * 60 // 通过常驻插件的 --status 接口获取插件状态的时间间隔
pluginHealthPullInterval = 5 * 60 // 通过读取常驻插件心跳时间戳判断插件状态的时间间隔
avoidTime = 60 // 单位秒, pluginHealthPull 与 pluginHealthScan 的相距时间需要大于avoidTime,避免短时间内重复上报
lastPluginHealthCheckTime int64 // 记录上一次插件状态检查的时间,避免两种插件状态上报方式同时上报
pluginHealthCheckTimeMut sync.Mutex
lazyReport bool
lastPluginStatusRecord = map[string]string{}
// pluginUpdateCheckIntervalSeconds 常驻插件检查升级的间隔时间
pluginUpdateCheckInterval = 30 * 60
pluginListReportInterval = 3600 * 24
LocalManager pluginmodel.LocalManager = &ShimManager{}
)
type UpdateHandler func(name, version string) bool
var (
pluginHealthScanTimer *timermanager.Timer
pluginHealthPullTimer *timermanager.Timer
pluginListReportTimer *timermanager.Timer
pluginUpdateTimer *timermanager.Timer
updateHandler UpdateHandler
updateHandlerLock sync.Mutex
)
func InitPluginCheckTimer() {
var err error
loadIntervalConf()
timerManager := timermanager.GetTimerManager()
if pluginHealthScanTimer, err = timerManager.CreateTimerInSeconds(pluginHealthCheckScan, pluginHealthScanInterval); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginHealthScanTimer err: ", err.Error())
} else {
go func() {
// shuffle timer in 1 minutes
mills := rand.Intn(60 * 1000)
time.Sleep(time.Duration(mills) * time.Millisecond)
if _, err = pluginHealthScanTimer.Run(); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginHealthScanTimer run err: ", err.Error())
}
}()
}
if pluginHealthPullTimer, err = timerManager.CreateTimerInSeconds(pluginHealthCheckPull, pluginHealthPullInterval); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginHealthPullTimer err: ", err.Error())
} else {
go func() {
mills := rand.Intn(60 * 1000)
// make sure that pluginHealthCheckScan is earlier than pluginHealthPullTimer
time.Sleep(time.Duration(60000+mills) * time.Millisecond)
if _, err = pluginHealthPullTimer.Run(); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginHealthPullTimer run err: ", err.Error())
}
}()
}
if pluginUpdateTimer, err = timerManager.CreateTimerInSeconds(pluginUpdateCheck, pluginUpdateCheckInterval); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginUpdateTimer err: ", err.Error())
} else {
go func() {
if _, err = pluginUpdateTimer.Run(); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginUpdateTimer run err: ", err.Error())
}
}()
}
if pluginListReportTimer, err = timerManager.CreateTimerInSeconds(pluginLocalListReport, pluginListReportInterval); err != nil {
log.GetLogger().Error("InitPluginCheckTimer: pluginListReportTimer err: ", err.Error())
} else {
go func() {
mills := rand.Intn(60 * 1000)
time.Sleep(time.Duration(180000+mills) * time.Millisecond)
log.GetLogger().Info("InitPluginCheckTimer:pluginLocalListReport timer run")
_, err = pluginListReportTimer.Run()
if err != nil {
log.GetLogger().Error("InitPluginCheckTimer:pluginLocalListReport timer run err: ", err.Error())
}
}()
}
}
func SetUpdateHandler(h UpdateHandler) {
updateHandlerLock.Lock()
defer updateHandlerLock.Unlock()
updateHandler = h
}
func getUpdateHandler() UpdateHandler {
updateHandlerLock.Lock()
defer updateHandlerLock.Unlock()
return updateHandler
}
func pluginHealthCheckScan() {
pluginHealthCheckTimeMut.Lock()
lastPluginHealthCheckTime = time.Now().Unix()
pluginHealthCheckTimeMut.Unlock()
log.GetLogger().Info("pluginHealthCheckScan: start")
// 1.检查插件列表,如果没有插件就不需要健康检查
pluginInfoList, err := _findAllInstalledPlugins()
if err != nil {
log.GetLogger().WithError(err).Error("pluginHealthCheckScan: loadPlugins err: " + err.Error())
return
}
if len(pluginInfoList) == 0 {
log.GetLogger().Infof("pluginHealthCheckScan: there is no plugin")
return
}
// 2.将插件状态发送给服务端
pluginStatusRequest := PluginStatusResquest{
Plugin: []PluginStatus{},
}
persistPluginCount := 0
pluginInfoMap := make(map[string]*PluginInfo)
for _, pluginInfo := range pluginInfoList {
if pluginInfo.IsRemoved {
continue
}
pluginInfoMap[pluginInfo.Name] = &pluginInfo
if pluginInfo.PluginType() == PLUGIN_ONCE {
pluginStatus := PluginStatus{
Name: pluginInfo.Name,
Status: ONCE_INSTALLED,
Version: pluginInfo.Version,
}
// 太长的名称和版本号字段进行截断
if len(pluginStatus.Name) > PLUGIN_NAME_MAXLEN {
pluginStatus.Name = pluginStatus.Name[:PLUGIN_NAME_MAXLEN]
}
if len(pluginStatus.Version) > PLUGIN_VERSION_MAXLEN {
pluginStatus.Version = pluginStatus.Version[:PLUGIN_VERSION_MAXLEN]
}
pluginStatusRequest.Plugin = append(pluginStatusRequest.Plugin, pluginStatus)
} else if pluginInfo.PluginType() == PLUGIN_PERSIST {
persistPluginCount += 1
}
}
if persistPluginCount > 0 {
// 调用acs-plugin-manager模块的 status接口,批量获取常驻插件状态(包括已删除的常驻插件)
mixedOutput := bytes.Buffer{}
cmd := "acs-plugin-manager"
arguments := []string{"--status"}
_, _, err = syncRunKillGroup("", cmd, arguments, &mixedOutput, &mixedOutput, 120)
if err != nil {
log.GetLogger().Errorf("pluginHealthCheckScan: cmd run err: %s, cmd[%s %s] output[%s]", err.Error(), cmd, strings.Join(arguments, " "), mixedOutput.String())
return
}
content := mixedOutput.Bytes()
pluginStatusList := []PluginStatus{}
if err := json.Unmarshal(content, &pluginStatusList); err != nil {
log.GetLogger().Errorf("pluginHealthCheckScan: json.Unmarshal pluginStatusList error: %s, content: %s", err.Error(), string(content))
}
if len(pluginStatusList) == 0 {
log.GetLogger().Infof("pluginHealthCheckScan: there is no persist plugin, content[%s]", string(content))
}
for _, pluginInfo := range pluginStatusList {
if pluginInfo.Status == REMOVED {
continue
}
pluginStatus := PluginStatus{
Name: pluginInfo.Name,
Version: pluginInfo.Version,
Status: pluginInfo.Status,
}
// 太长的名称和版本号字段进行截断
if len(pluginStatus.Name) > PLUGIN_NAME_MAXLEN {
pluginStatus.Name = pluginStatus.Name[:PLUGIN_NAME_MAXLEN]
}
if len(pluginStatus.Version) > PLUGIN_VERSION_MAXLEN {
pluginStatus.Version = pluginStatus.Version[:PLUGIN_VERSION_MAXLEN]
}
if pluginInfo.Status != PERSIST_RUNNING && pluginInfo.Status != REMOVED {
// // 状态异常的常驻插件本次不上报,acs-plugin-manager调用--start拉起后会单独上报该插件的状态
log.GetLogger().Warnf("plugin[%s] is not running, try to start it", pluginInfo.Name)
go func(pluginName string, mp map[string]*PluginInfo) {
randSleep := rand.Intn(10 * 1000)
time.Sleep(time.Duration(randSleep) * time.Millisecond)
command := "acs-plugin-manager"
arguments := []string{"-e", "--local", "-P", pluginName, "-p", "--start"}
timeout := 60
if pluginInfoPtr, ok := mp[pluginName]; ok && pluginInfoPtr.Timeout != "" {
if t, err := strconv.Atoi(pluginInfoPtr.Timeout); err == nil {
timeout = t
}
}
syncRunKillGroup("", command, arguments, nil, nil, timeout)
}(pluginInfo.Name, pluginInfoMap)
} else {
// 状态正常的常驻插件进行上报
pluginStatusRequest.Plugin = append(pluginStatusRequest.Plugin, pluginStatus)
}
}
}
if len(pluginStatusRequest.Plugin) == 0 {
log.GetLogger().Infof("pluginHealthCheckScan: there is no plugin need report status")
return
}
requestPayloadBytes, err := json.Marshal(pluginStatusRequest)
if err != nil {
log.GetLogger().WithError(err).Error("pluginHealthCheckScan: pluginStatusList marshal err: " + err.Error())
return
}
requestPayload := string(requestPayloadBytes)
url := util.GetPluginHealthService()
resp, err := util.HttpPost(url, requestPayload, "")
for i := 0; i < 3 && err != nil; i++ {
log.GetLogger().Infof("pluginHealthCheckScan: upload pluginStatusList fail, need retry: %s", requestPayload)
time.Sleep(time.Duration(2) * time.Second)
resp, err = util.HttpPost(url, requestPayload, "")
}
if err != nil {
log.GetLogger().WithError(err).Error("pluginHealthCheckScan: post pluginStatusList fail")
return
}
pluginStatusResp, err := parsePluginHealthCheck(resp)
if err != nil {
log.GetLogger().WithError(err).Errorf("pluginHealthCheckScan: parse PluginStatusResponse from resp fail: %s", resp)
return
}
// 设置下次状态检查周期
if pluginStatusResp.PullInterval > 0 && pluginStatusResp.PullInterval != pluginHealthPullInterval {
pluginHealthPullInterval = pluginStatusResp.PullInterval
}
if pluginStatusResp.ScanInterval > 0 && pluginStatusResp.ScanInterval != pluginHealthScanInterval {
pluginHealthScanInterval = pluginStatusResp.ScanInterval
}
if err := refreshTimer(pluginHealthScanTimer, pluginHealthScanInterval); err != nil {
log.GetLogger().Errorf("pluginHealthCheckScan: refresh pluginHealthScanTimer nextInterval [%d] second failed: %s", pluginHealthScanInterval, err.Error())
} else {
log.GetLogger().Infof("pluginHealthCheckScan: refresh pluginHealthScanTimer nextInterval [%d] second", pluginHealthScanInterval)
}
if pluginStatusResp.ReportType == NORMAL_REPORT && lazyReport {
lazyReport = false
log.GetLogger().Info("pluginHealthCheckScan: lazyReport switch to [off]")
} else if pluginStatusResp.ReportType == LAZY_REPORT && !lazyReport {
lazyReport = true
log.GetLogger().Info("pluginHealthCheckScan: lazyReport switch to [on]")
}
// if flowReport {
// // 有拉起插件的动作,需要重置pluginHealthPullTimer以便及时向服务端更新拉起后的状态
// // 但是如果interval太晚(晚于pluginHealthPullInterval 或者 pluginHealthScanInterval)就不需要重置pluginHealthPullTimer了
// interval := 60
// if pluginStatusResp.RefreshInterval > 0 {
// interval = pluginStatusResp.RefreshInterval
// }
// if interval < pluginHealthPullInterval && interval < pluginHealthScanInterval {
// pluginHealthPullTimer.Reset(time.Duration(interval) * time.Second)
// }
// }
log.GetLogger().Info("pluginHealthCheckScan success")
}
func pluginHealthCheckPull() {
pluginHealthCheckTimeMut.Lock()
lastTime := lastPluginHealthCheckTime
pluginHealthCheckTimeMut.Unlock()
now := time.Now().Unix()
needWait := int64(avoidTime) - (now - lastTime)
if needWait > 0 {
log.GetLogger().Infof("pluginHealthCheckPull: last pluginHealthCheckScan started [%d] seconds ago, need wait [%d] second for avoidTime[%d]", now-lastTime, needWait, avoidTime)
time.Sleep(time.Duration(needWait) * time.Second)
now = time.Now().Unix()
}
remainTime := lastTime + int64(pluginHealthScanInterval) - now
if remainTime < int64(avoidTime) {
log.GetLogger().Infof("pluginHealthCheckPull: next pluginHealthCheckScan will start in [%d] seconds, less than avoidTime[%d], so cancel this pluginHealthCheckPull", remainTime, avoidTime)
return
}
log.GetLogger().Info("pluginHealthCheckPull: start")
// 1.检查插件列表,如果没有插件就不需要健康检查
pluginInfoList, err := _findAllInstalledPlugins()
if err != nil {
log.GetLogger().Error("pluginHealthCheckPull: loadPlugins err: " + err.Error())
return
}
if len(pluginInfoList) == 0 {
log.GetLogger().Infof("pluginHealthCheckPull: there is no plugin")
return
}
// 2.获取插件状态
pluginStatusRequest := PluginStatusResquest{
Plugin: []PluginStatus{},
}
pluginDir, err := pathutil.GetPluginPath()
if err != nil {
log.GetLogger().Error("pluginHealthCheckPull: getPluginPath err: ", err.Error())
return
}
curPluginStatusRecord := map[string]string{}
for _, pluginInfo := range pluginInfoList {
if pluginInfo.PluginType() == PLUGIN_PERSIST && !pluginInfo.IsRemoved {
// 常驻型插件且未被删除:检查并读取插件目录下的heartbeat文件
heartbeatPath := filepath.Join(pluginDir, pluginInfo.Name, pluginInfo.Version, "heartbeat")
if fileutil.CheckFileIsExist(heartbeatPath) {
content, err := ioutil.ReadFile(heartbeatPath)
if err != nil {
log.GetLogger().Errorf("pluginHealthCheckPull: Read heartbeat file err, heartbeat[%s], err: %s", heartbeatPath, err.Error())
continue
}
timestampStr := strings.TrimSpace(string(content))
timestamp, err := strconv.ParseInt(timestampStr, 10, 0)
if err != nil {
log.GetLogger().Errorf("pluginHealthCheckPull: Parse heartbeat file err, heartbeat[%s], content[%s] err: %s", heartbeatPath, timestampStr, err.Error())
continue
}
status := PERSIST_RUNNING
if now-timestamp > int64(pluginInfo.HeartbeatInterval+5) {
status = PERSIST_FAIL
}
curPluginStatusRecord[pluginInfo.Name] = status
pluginStatus := PluginStatus{
Name: pluginInfo.Name,
Status: status,
Version: pluginInfo.Version,
}
if len(pluginStatus.Name) > PLUGIN_NAME_MAXLEN {
pluginStatus.Name = pluginStatus.Name[:PLUGIN_NAME_MAXLEN]
}
if len(pluginStatus.Version) > PLUGIN_VERSION_MAXLEN {
pluginStatus.Version = pluginStatus.Version[:PLUGIN_VERSION_MAXLEN]
}
pluginStatusRequest.Plugin = append(pluginStatusRequest.Plugin, pluginStatus)
}
}
}
if len(pluginStatusRequest.Plugin) == 0 {
log.GetLogger().Infof("pluginHealthCheckPull: there is no persist plugin with heartbeat")
return
}
willReport := true
if lazyReport {
// 如果lazyReport为true,对比本次上报的插件状态和上次的插件状态是否一致,如果不一致才上报
willReport = false
// 数量是否一致
if len(curPluginStatusRecord) != len(lastPluginStatusRecord) {
willReport = true
} else {
for k, v := range curPluginStatusRecord {
// 插件项目是否一致
if _, ok := lastPluginStatusRecord[k]; !ok {
willReport = true
break
}
// 同一插件的状态是否一致
if v != lastPluginStatusRecord[k] {
willReport = true
break
}
}
}
}
if !willReport {
return
}
lastPluginStatusRecord = curPluginStatusRecord
requestPayloadBytes, err := json.Marshal(pluginStatusRequest)
if err != nil {
log.GetLogger().WithError(err).Error("pluginHealthCheckPull fail: pluginStatusList marshal fail")
return
}
requestPayload := string(requestPayloadBytes)
url := util.GetPluginHealthService()
resp, err := util.HttpPost(url, requestPayload, "")
if err != nil {
log.GetLogger().WithError(err).Error("pluginHealthCheckPull fail: post pluginStatusList fail")
return
}
pluginStatusResp, err := parsePluginHealthCheck(resp)
if err != nil {
log.GetLogger().WithError(err).Errorf("pluginHealthCheckPull fail: parse PluginStatusResponse from resp fail: %s", resp)
return
}
// 设置下次状态检查周期
if pluginStatusResp.PullInterval > 0 {
pluginHealthPullInterval = pluginStatusResp.PullInterval
}
if err := refreshTimer(pluginHealthPullTimer, pluginHealthPullInterval); err != nil {
log.GetLogger().Errorf("pluginHealthCheckPull: refresh pluginHealthPullTimer nextInterval [%d] second failed: %s", pluginHealthPullInterval, err.Error())
} else {
log.GetLogger().Infof("pluginHealthCheckPull: refresh pluginHealthPullTimer nextInterval [%d] second", pluginHealthPullInterval)
}
if pluginStatusResp.ScanInterval > 0 {
pluginHealthScanInterval = pluginStatusResp.ScanInterval
}
// pluginStatusResp.ReportType 代表是否开启懒上报
if pluginStatusResp.ReportType == NORMAL_REPORT && lazyReport {
lazyReport = false
log.GetLogger().Info("pluginHealthCheckPull: lazyReport switch to [off]")
} else if pluginStatusResp.ReportType == LAZY_REPORT && !lazyReport {
lazyReport = true
log.GetLogger().Info("pluginHealthCheckPull: lazyReport switch to [on]")
}
log.GetLogger().Info("pluginHealthCheckPull success")
}
func pluginUpdateCheck() {
log.GetLogger().Info("pluginUpdateCheck start")
// get upgradable plugin list
upgradables, err := LocalManager.FindUpgradable(log.GetLogger())
if err != nil || len(upgradables) == 0 {
log.GetLogger().WithError(err).Info("pluginUpdateCheck cancel: there is no persist plugin")
return
}
// request for update check
osType := osutil.GetOsType()
arch, _ := GetArch()
updateChecks := make([]PluginUpdateCheck, 0, len(upgradables))
for _, u := range upgradables {
updateChecks = append(updateChecks, PluginUpdateCheck{
Name: u.Name(),
Version: u.Version(),
})
}
pluginUpdateCheckRequest := PluginUpdateCheckRequest{
Os: osType,
Arch: arch,
Plugin: updateChecks,
}
requestPayloadBytes, err := json.Marshal(pluginUpdateCheckRequest)
if err != nil {
log.GetLogger().WithError(err).Error("pluginUpdateCheck fail: pluginUpdateCheckRequest marshal fail")
return
}
requestPayload := string(requestPayloadBytes)
url := util.GetPluginUpdateCheckService()
resp, err := util.HttpPost(url, requestPayload, "")
for i := 0; i < 2 && err != nil; i++ {
log.GetLogger().Infof("request updateCheck fail, need retry: %s", requestPayload)
time.Sleep(time.Duration(3) * time.Second)
resp, err = util.HttpPost(url, requestPayload, "")
}
if err != nil {
log.GetLogger().WithError(err).Error("pluginUpdateCheck fail: post pluginStatusList fail")
return
}
// update plugins
var pluginUpdateCheckResp PluginUpdateCheckResponse
pluginUpdateCheckResp, err = parsePluginUpdateCheck(resp)
if err != nil {
log.GetLogger().WithError(err).Errorf("pluginUpdateCheck fail: parse pluginUpdateInfo from resp fail: %s", resp)
return
}
for _, plugin := range pluginUpdateCheckResp.Plugin {
updateOneLogger := log.GetLogger().WithFields(logrus.Fields{
"name": plugin.Name,
"version": plugin.Version,
"timeout": plugin.Timeout,
})
err := LocalManager.Update(updateOneLogger, &RemotePlugin{pui: &plugin})
if err != nil {
updateOneLogger.WithError(err).Error("pluginUpdateCheck: failed to update one plugin to new version in specified time")
}
}
log.GetLogger().Infof("pluginUpdateCheck done, updated [%d] plugins", len(pluginUpdateCheckResp.Plugin))
if pluginUpdateCheckResp.NextInterval > 0 {
pluginUpdateCheckInterval = pluginUpdateCheckResp.NextInterval
}
if err := refreshTimer(pluginUpdateTimer, pluginUpdateCheckInterval); err != nil {
log.GetLogger().Errorf("pluginUpdateCheck: refresh pluginUpdateTimer nextInterval [%d] second failed: %s", pluginUpdateCheckInterval, err.Error())
} else {
log.GetLogger().Errorf("pluginUpdateCheck: refresh pluginUpdateTimer nextInterval [%d] second", pluginUpdateCheckInterval)
}
}
func pluginLocalListReport() {
log.GetLogger().Info("pluginLocalListReport: start")
pluginInfoList, err := _findAllInstalledPlugins()
if err != nil {
log.GetLogger().Error("pluginLocalListReport: loadPlugins err: ", err.Error())
return
}
nameList := []string{}
versionList := []string{}
osList := []string{}
archList := []string{}
for _, p := range pluginInfoList {
if p.IsRemoved {
continue
}
p.OSType = strings.ToLower(p.OSType)
p.Arch = strings.ToLower(p.Arch)
nameList = append(nameList, p.Name)
versionList = append(versionList, p.Version)
osList = append(osList, p.OSType)
archList = append(archList, p.Arch)
}
if len(nameList) == 0 {
log.GetLogger().Info("pluginLocalListReport: no plugin need to report")
return
}
pluginData := map[string][]string{
"name": nameList,
"version": versionList,
"os": osList,
"arch": archList,
}
pluginDataMarshal, err := json.Marshal(&pluginData)
if err != nil {
log.GetLogger().Error("pluginLocalListReport: Marshal err: ", err.Error())
return
}
localArch, _ := GetArch()
metrics.GetPluginLocalListEvent(
"pluginList", string(pluginDataMarshal),
"localArch", localArch,
"localOsType", osutil.GetOsType(),
).ReportEvent()
}
func parsePluginHealthCheck(content string) (PluginStatusResponse, error) {
// 从check_update接口的响应数据中解析出插件升级信息
pluginStatusResp := PluginStatusResponse{}
if err := json.Unmarshal([]byte(content), &pluginStatusResp); err != nil {
log.GetLogger().Errorf("parse pluginUpdateCheck fail: %s", content)
return pluginStatusResp, err
}
return pluginStatusResp, nil
}
func parsePluginUpdateCheck(content string) (PluginUpdateCheckResponse, error) {
// 从check_update接口的响应数据中解析出插件升级信息
pluginUpdateCheckResp := PluginUpdateCheckResponse{}
if err := json.Unmarshal([]byte(content), &pluginUpdateCheckResp); err != nil {
log.GetLogger().Errorf("parse pluginUpdateCheck fail: %s", content)
return pluginUpdateCheckResp, err
}
return pluginUpdateCheckResp, nil
}
func refreshTimer(timer *timermanager.Timer, nextInterval int) error {
mutableSchedule, ok := timer.Schedule.(*timermanager.MutableScheduled)
if !ok {
return errors.New("Unexpected schedule type of heartbeat timer")
}
mutableSchedule.SetInterval(time.Duration(nextInterval) * time.Second)
timer.RefreshTimer()
return nil
}
func _findAllInstalledPlugins() ([]PluginInfo, error) {
installedPlugins, err := LoadInstalledPlugins()
if err != nil {
return nil, err
}
defer installedPlugins.Close()
_, pluginInfoList, err := installedPlugins.FindAll()
return pluginInfoList, err
}