in agent/taskengine/host/processor.go [105:364]
func (p *HostProcessor) Prepare(commandContent string) error {
taskLogger := log.GetLogger().WithFields(logrus.Fields{
"TaskId": p.TaskId,
"Phase": "HostProcessor-Preparing",
})
if langutil.NeedTransformEncoding() {
commandContent = langutil.UTF8ToLocal(commandContent)
}
p.CommandContent = commandContent
var processCmd *process.ProcessCmd
var processCmdErr error
switch p.TerminationMode {
case "ProcessTree":
groupName := fmt.Sprintf("%s-%d", p.TaskId, time.Now().Unix())
processCmd, processCmdErr = process.NewProcessCmdWithProcessTree(groupName)
case "", "Process":
processCmd = process.NewProcessCmd()
default:
processCmdErr = fmt.Errorf("unknown terminate mode %s", p.TerminationMode)
}
taskLogger.Info("Terminate mode: ", p.TerminationMode)
if processCmdErr != nil {
return taskerrors.NewCreateProcessCollectionError(processCmdErr)
}
p.processCmd = processCmd
useScriptFile := true
var whyNoScriptFile error
defer func() {
if !useScriptFile {
metrics.GetTaskWarnEvent(
"taskid", p.TaskId,
"warnmsg", "NotUseScriptFile",
"reason", whyNoScriptFile.Error(),
).ReportEvent()
}
}()
var scriptDir string
var scriptFileExtension string
switch p.CommandType {
case "RunBatScript":
scriptFileExtension = ".bat"
case "RunPowerShellScript":
scriptFileExtension = ".ps1"
case "RunShellScript":
scriptFileExtension = ".sh"
if p.Username != "" {
scriptDir = "/tmp"
}
default:
return taskerrors.NewUnknownCommandTypeError()
}
var launcherParams []string
// If launcher exists, scriptFileExtension is different
environmentParamExist := false
if p.Launcher != "" {
// If exists {{ACS::ScriptFileName}}, while without Ext
extensionMatches := scriptFileRe.FindStringSubmatch(p.Launcher)
if len(extensionMatches) == 2 {
scriptFileExtension = extensionMatches[1]
}
fileNameMatches := scriptFileRe.FindAllString(p.Launcher, -1)
environmentMatches := environmentParamRe.FindAllString(p.Launcher, -1)
// Launcher has other environment parameter and it's not {{ACS::ScriptFileName}} or {{ACS::ScriptFileName|Ext}}
if len(fileNameMatches) == 1 {
environmentParamExist = true
}
if len(fileNameMatches) > 1 || len(environmentMatches) != len(fileNameMatches) {
taskLogger.Errorf("Invalid match when resolving environment parameter in launcher %s", p.Launcher)
return taskerrors.NewInvalidEnvironmentParameterError(fmt.Sprintf(`Invalid match when resolving environment parameter in launcher %s`, p.Launcher))
}
}
if scriptDir == "" {
var err error
scriptDir, err = pathutil.GetScriptPath()
if err != nil {
useScriptFile = false
if errnoutil.IsNoEnoughSpaceError(err) {
whyNoScriptFile = taskerrors.NewNoEnoughSpaceError(err)
} else {
whyNoScriptFile = taskerrors.NewGetScriptPathError(err)
}
taskLogger.Error("Get script dir path error: ", whyNoScriptFile)
}
}
if useScriptFile {
iVersion := ""
commandName := ""
if p.InvokeVersion > 0 {
iVersion = fmt.Sprintf(".iv%d", p.InvokeVersion)
}
if len(p.CommandName) > 0 {
commandName = p.CommandName + "-"
}
p.scriptFilePath = filepath.Join(scriptDir, fmt.Sprintf("%s%s%s%s", commandName, p.TaskId, iVersion, scriptFileExtension))
// In english environment PowerShell scripts need to be saved in utf8-bom format,
// otherwise no-ASCII characters will not be recognized
commandContent = p.CommandContent
if p.CommandType == "RunPowerShellScript" && !langutil.NeedTransformEncoding() {
commandContent = string(append([]byte{0xEF, 0xBB, 0xBF}, []byte(commandContent)...))
}
if err := scriptmanager.SaveScriptFile(p.scriptFilePath, commandContent); err != nil {
// NOTE: Only non-repeated tasks need to check whether command script
// file exists.
taskLogger.Error("Save script file error: ", err)
if errors.Is(err, scriptmanager.ErrScriptFileExists) {
if p.Repeat == models.RunTaskOnce || p.Repeat == models.RunTaskNextRebootOnly {
return taskerrors.NewScriptFileExistedError(p.scriptFilePath, err)
}
} else if errnoutil.IsNoEnoughSpaceError(err) {
useScriptFile = false
whyNoScriptFile = taskerrors.NewNoEnoughSpaceError(err)
} else {
useScriptFile = false
whyNoScriptFile = taskerrors.NewSaveScriptFileError(err)
}
// If save script into file failed, file may be created but is invalid
if !useScriptFile {
os.Remove(p.scriptFilePath)
}
}
}
if p.Launcher != "" {
if !useScriptFile {
metrics.GetTaskFailedEvent(
"taskid", p.TaskId,
"errormsg", fmt.Sprintf("Can not use script file, so Launcher script cannot run"),
"reason", whyNoScriptFile.Error(),
).ReportEvent()
return whyNoScriptFile
}
launcher := scriptFileRe.ReplaceAllString(p.Launcher, p.scriptFilePath)
launcherParams = launcherParamRe.FindAllString(launcher, -1)
if !environmentParamExist {
launcherParams = append(launcherParams, p.scriptFilePath)
}
}
taskLogger.Infof("Launcher params: %v", launcherParams)
if useScriptFile {
if p.CommandType == "RunShellScript" {
if err := acl.Chmod(p.scriptFilePath, 0755); err != nil {
useScriptFile = false
whyNoScriptFile = taskerrors.NewSetExecutablePermissionError(err)
}
} else {
if p.Username != "" {
if err := acl.Chmod(p.scriptFilePath, 0755); err != nil {
useScriptFile = false
whyNoScriptFile = taskerrors.NewSetWindowsPermissionError(err)
}
}
}
}
p.invokeCommand = p.scriptFilePath
p.invokeCommandArgs = []string{}
if p.CommandType == "RunShellScript" {
p.invokeCommand = "sh"
if useScriptFile {
p.invokeCommandArgs = []string{"-c", p.scriptFilePath}
} else {
p.invokeCommandArgs = []string{"-c", p.CommandContent}
}
if _, err := executil.LookPath(p.invokeCommand); err != nil {
return taskerrors.NewSystemDefaultShellNotFoundError(err)
}
} else if p.CommandType == "RunPowerShellScript" {
p.invokeCommand = "powershell"
if useScriptFile {
p.invokeCommandArgs = []string{"-file", p.scriptFilePath}
} else {
p.invokeCommandArgs = []string{"-command", p.CommandContent}
}
if _, err := executil.LookPath(p.invokeCommand); err != nil {
return taskerrors.NewPowershellNotFoundError(err)
}
tempProcessCmd := process.NewProcessCmd()
if err := tempProcessCmd.SyncRunSimple("powershell.exe", []string{"Set-ExecutionPolicy", "RemoteSigned"}, 10); err != nil {
taskLogger.WithError(err).Warningln("Failed to set powershell execution policy")
}
} else if !useScriptFile {
metrics.GetTaskFailedEvent(
"taskid", p.TaskId,
"errormsg", fmt.Sprintf("Can not use script file, so this type[%s] script cannot run", p.CommandType),
"reason", whyNoScriptFile.Error(),
).ReportEvent()
return whyNoScriptFile
}
// For Launcher: change invokeCommand or invokeCommandArgs
if p.Launcher != "" && len(launcherParams) > 0 {
var cmdPath string
// Check if it's plugin or local launcher
if launcherParams[0][0] == '@' && len(launcherParams[0]) > 1 && launcherParams[0][1] != '@' {
//It's a plugin and install it
pluginName := matchPlugin(p.Launcher)
pluginManager, err := acspluginmanager.NewPluginManager(false)
if err != nil {
return taskerrors.NewPluginLoadFailedError(fmt.Errorf("Broken plugin management: %w", err))
}
pluginInfo, err := acspluginmanager.QueryPluginFromLocal(pluginName, "")
if pluginInfo == nil || err != nil {
err1 := err
pluginInfo, err = acspluginmanager.QueryPluginFromOnline(pluginName, "", "")
// Online query nil
if pluginInfo == nil || err != nil {
taskLogger.Error("Plugin not found online and local: ", launcherParams[0])
return taskerrors.NewPluginLoadFailedError(fmt.Errorf("query from local failed,%s; query from online failed,%s", err1, err))
}
err = pluginManager.InstallPluginFromOnline(pluginInfo, 20)
if err != nil {
taskLogger.Error("Plugin install error: ", err)
return taskerrors.NewPluginLoadFailedError(fmt.Errorf("Plugin install error: %s", err))
}
}
cmdPath = pluginManager.GetPluginCommandPath(pluginInfo)
if _, err := executil.LookPath(cmdPath); err != nil {
taskLogger.Error("LookPath error: ", err)
return taskerrors.NewPluginLoadFailedError(err)
}
} else {
if len(launcherParams[0]) > 1 && launcherParams[0][0] == '@' && launcherParams[0][1] == '@' {
// local launcher start with @
cmdPath = launcherParams[0][1:]
} else {
cmdPath = launcherParams[0]
}
if _, err := executil.LookPath(cmdPath); err != nil {
taskLogger.Error("LookPath error: ", err)
return taskerrors.NewLauncherNotFoundError(err)
}
}
if useScriptFile {
p.invokeCommand = cmdPath
p.invokeCommandArgs = launcherParams[1:]
taskLogger.Infof("InvokeCommand: %s ; InvokeCommandArgs: %s", p.invokeCommand, p.invokeCommandArgs)
} else {
metrics.GetTaskFailedEvent(
"taskid", p.TaskId,
"errormsg", fmt.Sprintf("Can not use script file, so Launcher script cannot run"),
"reason", whyNoScriptFile.Error(),
).ReportEvent()
return whyNoScriptFile
}
}
return nil
}