func()

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
}