feat: 工具配置增强、站点拨测与日志中心多项改进
- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖 - 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败 - Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS - 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率 - 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示 - 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
This commit is contained in:
1 parent
cde7bbe540
commit
7e1c8f583c
84 files changed
+7729
-1507
No files matched your search
@@ -6,6 +6,7 @@ package remoteexec
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
@@ -191,6 +192,112 @@ func UploadFile(cfg SSHConfig, localPath, remotePath string) (*UploadResult, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// DownloadFile 从远程主机下载文件到本地(二进制安全,供流水线回拉产物) // Logan.Li
|
||||
func DownloadFile(cfg SSHConfig, remotePath, localPath string) error {
|
||||
client, err := connect(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建SSH会话失败: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取stdout失败: %v", err)
|
||||
}
|
||||
|
||||
remoteDir := filepath.Dir(remotePath)
|
||||
mkdirCmd := fmt.Sprintf("mkdir -p %s", remoteDir)
|
||||
_, _ = execWithClient(client, cfg, mkdirCmd, 30*time.Second)
|
||||
|
||||
if err := session.Start(fmt.Sprintf("cat %s", shellQuote(remotePath))); err != nil {
|
||||
return fmt.Errorf("启动远程读取失败: %v", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(localPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(localPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, stdout); err != nil {
|
||||
return fmt.Errorf("写入本地文件失败: %v", err)
|
||||
}
|
||||
if err := session.Wait(); err != nil {
|
||||
return fmt.Errorf("远程读取失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StreamRemoteTar 将远程目录打包为 tar.gz 流式写入本地文件 // Logan.Li
|
||||
func StreamRemoteTar(cfg SSHConfig, remoteDir, localTarPath string, timeout time.Duration) error {
|
||||
if timeout <= 0 {
|
||||
timeout = 30 * time.Minute
|
||||
}
|
||||
client, err := connect(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建SSH会话失败: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取stdout失败: %v", err)
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("tar czf - -C %s .", shellQuote(remoteDir))
|
||||
if err := session.Start(cmd); err != nil {
|
||||
return fmt.Errorf("启动远程打包失败: %v", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(localTarPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(localTarPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, copyErr := io.Copy(f, stdout)
|
||||
done <- copyErr
|
||||
}()
|
||||
|
||||
select {
|
||||
case copyErr := <-done:
|
||||
if copyErr != nil {
|
||||
return fmt.Errorf("写入 tar 失败: %v", copyErr)
|
||||
}
|
||||
case <-time.After(timeout):
|
||||
return fmt.Errorf("下载工作区超时")
|
||||
}
|
||||
if err := session.Wait(); err != nil {
|
||||
return fmt.Errorf("远程打包失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// shellQuote 简单 shell 路径转义 // Logan.Li
|
||||
func shellQuote(s string) string {
|
||||
return "'" + strings.ReplaceAll(s, "'", "'\\''") + "'"
|
||||
}
|
||||
|
||||
// BatchUpload 批量上传文件到多台服务器(并发)。
|
||||
func BatchUpload(servers []SSHConfig, localPath, remotePath string, maxConcurrency int) []UploadResult {
|
||||
if maxConcurrency <= 0 {
|
||||
|
||||
Reference in new issue
Block a user