From 0828002e59f12f4dcf6e2a366e2c9690b4eb4dd3 Mon Sep 17 00:00:00 2001 From: zhanghan Date: Mon, 13 Oct 2025 17:22:35 +0800 Subject: [PATCH] Add Script Execution Interface --- .../agent/exec-script/controller/script.go | 23 ++++ automation/agent/exec-script/model/script.go | 14 ++ automation/agent/exec-script/router.go | 14 ++ .../agent/exec-script/service/script.go | 128 ++++++++++++++++++ automation/agent/pkg/router/router.go | 4 +- 5 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 automation/agent/exec-script/controller/script.go create mode 100644 automation/agent/exec-script/model/script.go create mode 100644 automation/agent/exec-script/router.go create mode 100644 automation/agent/exec-script/service/script.go diff --git a/automation/agent/exec-script/controller/script.go b/automation/agent/exec-script/controller/script.go new file mode 100644 index 00000000..a08f327f --- /dev/null +++ b/automation/agent/exec-script/controller/script.go @@ -0,0 +1,23 @@ +package controller + +import ( + "ant-agent/exec-script/model" + "ant-agent/exec-script/service" + + "github.com/gin-gonic/gin" + "openeuler.org/PilotGo/PilotGo-plugin-automation/pkg/response" +) + +func ExecScript(c *gin.Context) { + var script model.ScriptsRun + if err := c.ShouldBindJSON(&script); err != nil { + response.Fail(c, nil, "参数错误") + return + } + result, err := service.ExecScript(&script) + if err != nil { + response.Fail(c, nil, err.Error()) + return + } + response.Success(c, result, "脚本执行成功") +} diff --git a/automation/agent/exec-script/model/script.go b/automation/agent/exec-script/model/script.go new file mode 100644 index 00000000..3033b724 --- /dev/null +++ b/automation/agent/exec-script/model/script.go @@ -0,0 +1,14 @@ +package model + +type CmdResult struct { + RetCode int + Stdout string + Stderr string +} + +type ScriptsRun struct { + ScriptType string + ScriptContent string + Params string + TimeOut int +} diff --git a/automation/agent/exec-script/router.go b/automation/agent/exec-script/router.go new file mode 100644 index 00000000..7034821d --- /dev/null +++ b/automation/agent/exec-script/router.go @@ -0,0 +1,14 @@ +package execscript + +import ( + "ant-agent/exec-script/controller" + + "github.com/gin-gonic/gin" +) + +func ExecScriptHandler(router *gin.RouterGroup) { + api := router.Group("/script") + { + api.POST("/exec", controller.ExecScript) + } +} diff --git a/automation/agent/exec-script/service/script.go b/automation/agent/exec-script/service/script.go new file mode 100644 index 00000000..348e06c6 --- /dev/null +++ b/automation/agent/exec-script/service/script.go @@ -0,0 +1,128 @@ +package service + +import ( + "ant-agent/exec-script/model" + "bytes" + "context" + "encoding/base64" + "fmt" + "os" + "os/exec" + "time" + + "gitee.com/openeuler/PilotGo/sdk/logger" +) + +func getScriptInfo(scriptType string) (suffix string, interpreter string, err error) { + switch scriptType { + case "Shell": + return ".sh", "/bin/bash", nil + case "Python": + return ".py", "/usr/bin/python3", nil + case "Perl": + return ".pl", "/usr/bin/perl", nil + case "Ruby": + return ".rb", "/usr/bin/ruby", nil + case "PHP": + return ".php", "/usr/bin/php", nil + default: + err = fmt.Errorf("不支持的脚本类型: %s", scriptType) + } + return +} + +func createTempScriptFile(workDir, suffix, encodedScript string) (string, error) { + decodedScript, err := base64.StdEncoding.DecodeString(encodedScript) + if err != nil { + return "", fmt.Errorf("脚本内容base64解码失败: %s", err.Error()) + } + + if _, err := os.Stat(workDir); os.IsNotExist(err) { + if err := os.MkdirAll(workDir, 0755); err != nil { + return "", fmt.Errorf("创建临时工作目录失败: %s", err.Error()) + } + } + + tmpFile, err := os.CreateTemp(workDir, "script_*"+suffix) + if err != nil { + return "", fmt.Errorf("创建临时脚本文件失败: %s", err.Error()) + } + + if _, err = tmpFile.Write(decodedScript); err != nil { + tmpFile.Close() + os.Remove(tmpFile.Name()) + return "", fmt.Errorf("内容写入到脚本文件失败: %s", err.Error()) + } + tmpFile.Close() + + if err := os.Chmod(tmpFile.Name(), 0755); err != nil { + os.Remove(tmpFile.Name()) + return "", fmt.Errorf("设置脚本可执行权限失败: %s", err.Error()) + } + + return tmpFile.Name(), nil +} + +func runScript(interpreter, scriptPath string, params string, timeoutSec int) (string, string, int, error) { + if timeoutSec <= 0 { + timeoutSec = 30 + } + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSec)*time.Second) + defer cancel() + + args := append([]string{scriptPath}, params) + cmd := exec.CommandContext(ctx, interpreter, args...) + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if ctx.Err() == context.DeadlineExceeded { + return stdout.String(), "脚本执行超时", -1, fmt.Errorf("脚本执行超时") + } + + if err != nil { + retcode := -1 + if cmd.ProcessState != nil { + retcode = cmd.ProcessState.ExitCode() + } + errMsg := stderr.String() + if errMsg == "" { + errMsg = err.Error() + } + return stdout.String(), errMsg, retcode, err + } + + return stdout.String(), stderr.String(), 0, nil +} + +func ExecScript(script *model.ScriptsRun) (interface{}, error) { + var workDir = "/tmp/scripts/" + + suffix, interpreter, err := getScriptInfo(script.ScriptType) + if err != nil { + return "", fmt.Errorf("获取脚本类型失败: %s", err.Error()) + } + + scriptPath, err := createTempScriptFile(workDir, suffix, script.ScriptContent) + if err != nil { + return "", fmt.Errorf("创建临时脚本失败: %s", err.Error()) + } + defer os.Remove(scriptPath) + + logger.Debug("run script timeout: %v", script.TimeOut) + logger.Debug("process run script command: %s %s %v", interpreter, scriptPath, script.Params) + + stdout, stderr, execCode, err := runScript(interpreter, scriptPath, script.Params, script.TimeOut) + result := &model.CmdResult{ + Stdout: stdout, + Stderr: stderr, + RetCode: execCode, + } + if err != nil || execCode != 0 { + return "", fmt.Errorf("脚本执行错误: %s", err.Error()) + } + + return result, nil +} diff --git a/automation/agent/pkg/router/router.go b/automation/agent/pkg/router/router.go index a1e33b59..3445f076 100644 --- a/automation/agent/pkg/router/router.go +++ b/automation/agent/pkg/router/router.go @@ -1,6 +1,7 @@ package router import ( + execscript "ant-agent/exec-script" "ant-agent/pkg/global" "gitee.com/openeuler/PilotGo/sdk/logger" @@ -22,6 +23,7 @@ func initRouters() *gin.Engine { Router.Use(logger.RequestLogger([]string{})) // 注册各自的路由模块 - // api := Router.Group("/plugin/automation") + api := Router.Group("/plugin/automation") + execscript.ExecScriptHandler(api) return Router } -- Gitee