in agent/pluginmanager/acspluginmanager/acspluginmanager.go [873:1039]
func (pm *PluginManager) installFromOnline(onlineInfo *PluginInfo, timeout time.Duration, localArch string) (*Fetched, ExitingError) {
ctx := context.Background()
var cancel context.CancelFunc
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
filePath := filepath.Join(pm.pluginRoot, onlineInfo.Name+".zip")
log.GetLogger().Infof("Downloading package from [%s], save to [%s] ", onlineInfo.Url, filePath)
const maxRetries = 3
const retryDelay = 3 * time.Second
var err error
if timeout == 0 {
err = func(remoteUrl string, localPath string) error {
var err2 error
for retry := maxRetries; retry > 0; retry-- {
err2 = util.HttpDownlod(remoteUrl, localPath)
if err2 == nil {
break
}
time.Sleep(retryDelay)
}
return err2
}(onlineInfo.Url, filePath)
} else {
err = func(ctx context.Context, remoteUrl string, localPath string) error {
var err2 error
for retry := maxRetries; retry > 0; retry-- {
err2 = util.HttpDownloadContext(ctx, remoteUrl, localPath)
if err2 == nil {
break
}
delayTimer := time.NewTimer(retryDelay)
select {
case <-ctx.Done():
return ctx.Err()
case <-delayTimer.C:
continue
}
}
return err2
}(ctx, onlineInfo.Url, filePath)
}
if err != nil {
return nil, NewDownloadExitingError(err, fmt.Sprintf("Downloading package failed, plugin.Url is [%s], err is [%s]", onlineInfo.Url, err.Error()))
}
log.GetLogger().Infoln("Check MD5...")
// TODO-FIXME: Calculating the MD5 checksum for plugin package is also a
// time-consuming procedure, which should be cancelable when timed out
md5Checksum, err := util.ComputeMd5(filePath)
if err != nil {
return nil, NewMD5CheckExitingError(err, fmt.Sprintf("Compute md5 of plugin file[%s] err, plugin.Url is [%s], err is [%s]", filePath, onlineInfo.Url, err.Error()))
}
if strings.ToLower(md5Checksum) != strings.ToLower(onlineInfo.Md5) {
log.GetLogger().Errorf("Md5 not match, onlineInfo.Md5[%s], package file md5[%s]\n", onlineInfo.Md5, md5Checksum)
return nil, NewMD5CheckExitingError(errors.New("Md5 not macth"), fmt.Sprintf("Md5 not match, onlineInfo.Md5 is [%s], real md5 is [%s], plugin.Url is [%s]", onlineInfo.Md5, md5Checksum, onlineInfo.Url))
}
unzipdir := filepath.Join(pm.pluginRoot, onlineInfo.Name, onlineInfo.Version)
pathutil.MakeSurePath(unzipdir)
log.GetLogger().Infoln("Unzip package...")
if err := zipfile.UnzipContext(ctx, filePath, unzipdir, false); err != nil {
return nil, NewUnzipExitingError(err, fmt.Sprintf("Unzip package err, plugin.Url is [%s], err is [%s]", onlineInfo.Url, err.Error()))
}
os.RemoveAll(filePath)
config_path := filepath.Join(unzipdir, "config.json")
if !fileutil.CheckFileIsExist(config_path) {
err := errors.New(fmt.Sprintf("File config.json not exist, %s.", config_path))
return nil, NewPluginFormatExitingError(err, fmt.Sprintf("File config.json not exist, %s.", config_path))
}
config := pluginConfig{}
if content, err := fuzzyjson.UnmarshalFile(config_path, &config); err != nil {
return nil, NewUnmarshalExitingError(err, fmt.Sprintf("Unmarshal config.json err, config.json is [%s], err is [%s]", string(content), err.Error()))
}
if config.HeartbeatInterval <= 0 {
config.HeartbeatInterval = 60
}
if config.PluginType() != onlineInfo.PluginType() {
tip := fmt.Sprintf("config.PluginType[%s] not match to pluginType[%s]", config.PluginType(), onlineInfo.PluginType())
return nil, NewPluginFormatExitingError(errors.New(tip), tip)
}
// 接口返回的插件信息中没有HeartbeatInterval字段,需要以插件包中的config.json为准
onlineInfo.HeartbeatInterval = config.HeartbeatInterval
onlineInfo.SetPluginType(config.PluginType())
onlineInfo.AddSysTag = config.AddSysTag
if config.PluginType() == PLUGIN_COMMANDER {
commanderInfoPath := filepath.Join(unzipdir, "axt-commander.json")
if !fileutil.CheckFileIsExist(commanderInfoPath) {
err := errors.New(fmt.Sprintf("File axt-commander.json not exist, %s.", commanderInfoPath))
return nil, NewPluginFormatExitingError(err, fmt.Sprintf("File axt-commander.json not exist, %s.", commanderInfoPath))
}
commanderInfo := CommanderInfo{}
if content, err := fuzzyjson.UnmarshalFile(commanderInfoPath, &commanderInfo); err != nil {
return nil, NewUnmarshalExitingError(err, fmt.Sprintf("Unmarshal axt-commander.json err, axt-commander.json is [%s], err is [%s]", string(content), err.Error()))
}
onlineInfo.CommanderInfo = commanderInfo
}
cmdPath := filepath.Join(unzipdir, config.RunPath)
// 检查系统类型和架构是否符合
if strings.ToLower(onlineInfo.OSType) != "both" && strings.ToLower(onlineInfo.OSType) != osutil.GetOsType() {
err := errors.New("Plugin ostype not suit for this system")
return nil, NewPluginFormatExitingError(err, fmt.Sprintf("Plugin ostype[%s] not suit for this system[%s], plugin.Url is [%s]", onlineInfo.OSType, osutil.GetOsType(), onlineInfo.Url))
}
if strings.ToLower(onlineInfo.Arch) != "all" && strings.ToLower(onlineInfo.Arch) != localArch {
err := errors.New("Plugin arch not suit for this system")
return nil, NewPluginFormatExitingError(err, fmt.Sprintf("Plugin arch[%s] not suit for this system[%s], plugin.Url is [%s]", onlineInfo.Arch, localArch, onlineInfo.Url))
}
if !fileutil.CheckFileIsExist(cmdPath) {
log.GetLogger().Infoln("Cmd file not exist: ", cmdPath)
err := errors.New("Cmd file not exist: " + cmdPath)
return nil, NewPluginFormatExitingError(err, fmt.Sprintf("Executable file not exist, %s.", cmdPath))
}
if osutil.GetOsType() != osutil.OSWin {
if err = os.Chmod(cmdPath, os.FileMode(0o744)); err != nil {
return nil, NewExecutablePermissionExitingError(err, fmt.Sprintf("Make plugin file executable err, plugin.Url is [%s], err is [%s]", onlineInfo.Url, err.Error()))
}
}
// update INSTALLEDPLUGINS file
var envPrePluginDir string
pluginIndex, pluginInfo, err := getInstalledPluginByName(onlineInfo.Name)
if err != nil {
return nil, NewLoadInstalledPluginsExitingError(err)
}
if pluginIndex != -1 && pluginInfo.IsRemoved {
// Actually from the database remove the record of removed plugin
if err = deleteInstalledPluginByIndex(pluginIndex); err != nil {
return nil, NewDumpInstalledPluginsExitingError(err)
}
pluginIndex = -1
}
if pluginIndex == -1 {
_, err = insertNewInstalledPlugin(onlineInfo)
} else {
envPrePluginDir = filepath.Join(pm.pluginRoot, pluginInfo.Name, pluginInfo.Version)
err = updateInstalledPlugin(pluginIndex, onlineInfo)
}
if err != nil {
return nil, NewDumpInstalledPluginsExitingError(err)
}
executionTimeoutInSeconds := 60
if t, err := strconv.Atoi(config.Timeout); err == nil {
executionTimeoutInSeconds = t
}
return &Fetched{
PluginName: config.Name,
PluginVersion: config.Version,
PluginType: config.PluginType(),
AddSysTag: config.AddSysTag,
Entrypoint: cmdPath,
ExecutionTimeoutInSeconds: executionTimeoutInSeconds,
EnvPluginDir: filepath.Join(pm.pluginRoot, config.Name, config.Version),
EnvPrePluginDir: envPrePluginDir,
}, nil
}