Files
LoganandClaude 76ec9387f0 feat(rdp): 新增 RDP 远程桌面/SMB/账单同步能力 + 清理编译产物
主要变更:
- RDP 远程桌面:新增 backend/pkg/rdp(RDP 引擎 + credssp/display/rle/trace)、
  backend/controller/rdp*(连接/凭证/文件/会话/实时)、frontend RDP 组件与 API
- Windows SMB 文件传输:backend/pkg/winsmb
- 云账单同步:backend/pkg/billingsync + billing_sync/aliyun_billing,前端账单详情
- Agent CLI Kubernetes 工具:kubernetes_action/get、pod_execute/logs
- 服务器导入助手:server_import_helpers、ImportCredentialSettings
- 国际化与组件重构(cloud/deploy/monitor/server/system 等多模块)
- 清理编译产物二进制:移除 backend/server、backend/agent/bin/aiops-agent-*
- .gitignore:新增 backend/agent/ 与 .cache/(避免 334MB go-build 缓存误入库)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-04 01:50:36 +08:00

137 lines
4.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// pod_execute.go - 在目标 Pod 容器内执行 shell 命令 // Logan.Li
package tools
import (
"context"
"fmt"
"strings"
"time"
k8sclient "ezops/pkg/kubernetesclient"
)
// PodExecuteTool 在 Pod 容器内执行命令(K8s exec WebSocket 协议) // Logan.Li
type PodExecuteTool struct {
client *k8sclient.Client
namespace string
podName string
container string
timeout time.Duration
}
// NewPodExecuteTool 创建 Pod 执行工具 // Logan.Li
func NewPodExecuteTool(client *k8sclient.Client, namespace, podName, container string, timeout time.Duration) *PodExecuteTool {
if timeout <= 0 {
timeout = 60 * time.Second
}
return &PodExecuteTool{
client: client, namespace: namespace, podName: podName,
container: container, timeout: timeout,
}
}
// Name 工具名 // Logan.Li
func (t *PodExecuteTool) Name() string { return "pod_execute" }
// Description 给 LLM 的工具说明 // Logan.Li
func (t *PodExecuteTool) Description() string {
return "在已选定的 Kubernetes Pod 容器内执行 shell 命令并返回 stdout+stderr。" +
"用于容器内排障(查看进程/配置/连通性)、应用调试。" +
"注意:容器内可能没有 systemctl 或宿主机路径;多容器 Pod 可通过 container 参数指定。" +
"高危命令需用户确认。"
}
// Parameters JSON Schema // Logan.Li
func (t *PodExecuteTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"command": map[string]interface{}{
"type": "string",
"description": "要执行的 shell 命令,例如 ps aux、cat /etc/nginx/nginx.conf、curl -s localhost:8080/health",
},
"container": map[string]interface{}{
"type": "string",
"description": "可选,多容器 Pod 时指定容器名;默认使用会话绑定的容器",
},
"timeout": map[string]interface{}{
"type": "integer",
"description": "命令超时秒数(默认 60)",
},
},
"required": []string{"command"},
}
}
// RiskLevel 静态风险 // Logan.Li
func (t *PodExecuteTool) RiskLevel() RiskLevel { return RiskHigh }
// RiskLevelForArgs 依据命令动态评估风险 // Logan.Li
func (t *PodExecuteTool) RiskLevelForArgs(args map[string]interface{}) RiskLevel {
return AssessBashRisk(StringParam(args, "command"))
}
// Execute 在 Pod 内执行命令 // Logan.Li
func (t *PodExecuteTool) Execute(ctx context.Context, args map[string]interface{}) (*Result, error) {
command := strings.TrimSpace(StringParam(args, "command"))
if command == "" {
return &Result{Success: false, Error: "参数 command 不能为空"}, nil
}
if t.podName == "" {
return &Result{Success: false, Error: "未绑定目标 Pod,请在前端选择 Pod 或在命令前先 kubernetes_get 确认"}, nil
}
container := strings.TrimSpace(StringParam(args, "container"))
if container == "" {
container = t.container
}
timeout := t.timeout
if sec := IntParam(args, "timeout"); sec > 0 {
timeout = time.Duration(sec) * time.Second
}
execCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
// 用 sh -c 保持 shell 语义,并嵌入 exit_code 与 ssh_execute 对齐 // Logan.Li
wrapped := command + "; echo [exit_code=$?]"
done := make(chan struct {
res *k8sclient.ExecResult
err error
}, 1)
go func() {
r, err := t.client.ExecInPod(t.namespace, t.podName, container, []string{"sh", "-c", wrapped})
done <- struct {
res *k8sclient.ExecResult
err error
}{r, err}
}()
var result *k8sclient.ExecResult
var err error
select {
case <-execCtx.Done():
return &Result{Success: false, Error: "命令执行超时"}, nil
case out := <-done:
result, err = out.res, out.err
}
if err != nil {
return &Result{Success: false, Error: fmt.Sprintf("Pod exec 失败: %v", err)}, nil
}
output := strings.TrimSpace(result.Stdout)
if result.Stderr != "" {
if output != "" {
output += "\n"
}
output += strings.TrimSpace(result.Stderr)
}
success := strings.Contains(output, "[exit_code=0]")
if !success && !strings.Contains(output, "[exit_code=") {
// 无 exit_code 标记时,无 stderr 视为成功 // Logan.Li
success = strings.TrimSpace(result.Stderr) == ""
}
return &Result{Success: success, Output: output}, nil
}