diff --git a/.cursorignore b/.cursorignore index 9eb725e..4ced8da 100644 --- a/.cursorignore +++ b/.cursorignore @@ -1,9 +1,9 @@ # Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) -./agent-deploy/ -./message_pusher/ -./port_forwarding/ -./server/ -./server-go/ -./socks_txthinking/ -./source/ +agent-deploy/ +message_pusher/ +port_forwarding/ +server/ +server-go/ +socks_txthinking/ +source/ diff --git a/agent-wdd/cmd/Base.go b/agent-wdd/cmd/Base.go index 40d710c..7a7ce71 100644 --- a/agent-wdd/cmd/Base.go +++ b/agent-wdd/cmd/Base.go @@ -208,4 +208,116 @@ func addDockerSubcommands(cmd *cobra.Command) { func addDockerComposeSubcommands(cmd *cobra.Command) { + installCmd := &cobra.Command{ + Use: "online [version]", + Short: "安装Docker Compose", + Run: func(cmd *cobra.Command, args []string) { + log.Info("Installing Docker Compose...") + + // 检查参数 + if len(args) > 0 { + fmt.Printf("Installing Docker Compose version: %s\n", args[0]) + } + + // 默认安装最新版本 + version := "latest" + if len(args) > 0 { + version = args[0] + } + + // 如果是最新版本,则通过GitHub API查询 + if version == "latest" { + latestVersion, err := op.GetLatestGithubReleaseVersion("docker", "compose") + if err != nil { + log.Error("获取Docker Compose最新版本失败: %s", err) + } else { + version = latestVersion + log.Info("获取到Docker Compose最新版本: %s", version) + } + } + + log.Info("安装 Docker Compose 版本: %s", version) + + // 检查是否可以连接互联网 + if config.CanConnectInternet() <= 1 { + log.Error("服务器无法连接互联网,无法在线安装 Docker Compose") + return + } + + // 检查 Docker 是否已安装 + dockerExists := op.CommandExistsByPath("docker") + if !dockerExists { + log.Error("Docker 未安装,请先安装 Docker") + return + } + + // 根据架构选择合适的下载链接 + arch := runtime.GOARCH + downloadURL := fmt.Sprintf("https://github.com/docker/compose/releases/download/%s/docker-compose-linux-%s", version, arch) + + log.Info("Downloading Docker Compose from: %s", downloadURL) + // 下载 Docker Compose + ok, ccc := op.DownloadFile(downloadURL, "/usr/local/bin/docker-compose") + if !ok { + log.Error("下载 Docker Compose 失败: %s", ccc) + return + } + + // 设置执行权限 + chmodCmd := []string{"chmod", "+x", "/usr/local/bin/docker-compose"} + ok, resultLog := op.SingleLineCommandExecutor(chmodCmd) + if !ok { + log.Error("设置 Docker Compose 权限失败: %s", resultLog) + return + } + + // 创建软链接 + linkCmd := []string{"ln", "-sf", "/usr/local/bin/docker-compose", "/usr/bin/docker-compose"} + op.SingleLineCommandExecutor(linkCmd) + + // 验证安装 + verifyCmd := []string{"docker-compose", "--version"} + ok, resultLog = op.SingleLineCommandExecutor(verifyCmd) + if !ok { + log.Error("Docker Compose 安装验证失败: %s", resultLog) + return + } + + log.Info("Docker Compose 安装成功: %s", resultLog) + + }, + } + + removeCmd := &cobra.Command{ + Use: "remove", + Short: "卸载Docker Compose", + Run: func(cmd *cobra.Command, args []string) { + log.Info("Removing Docker Compose...") + }, + } + + localCmd := &cobra.Command{ + Use: "local [path]", + Short: "本地安装Docker Compose", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + log.Info("Installing Docker Compose from local file...") + }, + } + + versionCmd := &cobra.Command{ + Use: "version", + Short: "查看Docker Compose版本", + Run: func(cmd *cobra.Command, args []string) { + log.Info("Docker Compose version...") + }, + } + + cmd.AddCommand( + installCmd, + removeCmd, + localCmd, + versionCmd, + ) + } diff --git a/agent-wdd/config/Config.go b/agent-wdd/config/Config.go index 9982f43..510444b 100644 --- a/agent-wdd/config/Config.go +++ b/agent-wdd/config/Config.go @@ -3,9 +3,11 @@ package config import ( "agent-wdd/log" "agent-wdd/utils" + "os" + "runtime" + "github.com/spf13/viper" "gopkg.in/yaml.v3" - "os" ) var WddConfigFilePath = "/usr/local/etc/wdd/agent-wdd-config.yaml" @@ -14,9 +16,12 @@ var ConfigCache *Config func init() { // 根据运行的操作系统不同, 修改 WddConfigFilePath 的位置 + if runtime.GOOS == "windows" { + homedir, _ := os.UserHomeDir() + WddConfigFilePath = homedir + "\\agent-wdd\\agent-wdd-config.yaml" + } } -// 配置结构体定义 type Config struct { TimeStamp string `yaml:"timestamp"` ModifiedTimes int `yaml:"modifiedTimes"` diff --git a/agent-wdd/config/Disk.go b/agent-wdd/config/Disk.go index 2c83c17..e6c8729 100644 --- a/agent-wdd/config/Disk.go +++ b/agent-wdd/config/Disk.go @@ -3,12 +3,10 @@ package config import ( "agent-wdd/log" "bufio" - "fmt" "os" "path/filepath" "strconv" "strings" - "syscall" ) var CommonDiskPath = []string{ @@ -82,32 +80,31 @@ func DiskListGather() { } func (disk *Disk) calculateDiskUsage() { - var stat syscall.Statfs_t - err := syscall.Statfs(disk.Path, &stat) - if err != nil { - log.Error("disk syscall error of %v", err) + // var stat unix.Statfs_t + // err := unix.Statfs(disk.Path, &stat) + // if err != nil { + // log.Error("disk syscall error: %v", err) + // disk.Size = "0B" + // disk.Usage = "0B" + // disk.Percent = "0.00%" + // return + // } - disk.Size = "0B" - disk.Usage = "0B" - disk.Percent = "0.00%" - return - } + // // 计算存储空间大小 + // totalBytes := stat.Blocks * uint64(stat.Bsize) + // availBytes := stat.Bavail * uint64(stat.Bsize) + // usedBytes := totalBytes - availBytes - // 计算存储空间大小 - totalBytes := stat.Blocks * uint64(stat.Bsize) - availBytes := stat.Bavail * uint64(stat.Bsize) - usedBytes := totalBytes - availBytes + // // 格式化输出 + // disk.Size = formatDiskSize(totalBytes) + // disk.Usage = formatDiskSize(usedBytes) - // 格式化输出 - disk.Size = formatDiskSize(totalBytes) - disk.Usage = formatDiskSize(usedBytes) - - if totalBytes == 0 { - disk.Percent = "0.00%" - } else { - percent := float64(usedBytes) / float64(totalBytes) * 100 - disk.Percent = fmt.Sprintf("%.2f%%", percent) - } + // if totalBytes == 0 { + // disk.Percent = "0.00%" + // } else { + // percent := float64(usedBytes) / float64(totalBytes) * 100 + // disk.Percent = fmt.Sprintf("%.2f%%", percent) + // } } func formatDiskSize(bytes uint64) string { diff --git a/agent-wdd/config/Network.go b/agent-wdd/config/Network.go index 9318dc4..a66f992 100644 --- a/agent-wdd/config/Network.go +++ b/agent-wdd/config/Network.go @@ -41,6 +41,23 @@ func (network *Network) Gather() { // CanConnectInternet 判定主机能够上网 func CanConnectInternet() int { + + // 初始化 ConfigCache + if ConfigCache == nil { + ConfigCache = &Config{ + TimeStamp: "", + ModifiedTimes: 0, + Agent: Agent{ + OS: OS{}, + Network: Network{}, + CPU: CPU{}, + Mem: Memory{}, + Swap: Swap{}, + Disks: []Disk{}, + }, + } + } + // 读取 config 文件,判定能否上网 internetCode := ConfigCache.Agent.Network.Internet diff --git a/agent-wdd/op/DownloadExecutor.go b/agent-wdd/op/DownloadExecutor.go new file mode 100644 index 0000000..720342e --- /dev/null +++ b/agent-wdd/op/DownloadExecutor.go @@ -0,0 +1,43 @@ +package op + +import ( + "fmt" + "io" + "net/http" + "os" + "time" +) + +func DownloadFile(url string, path string) (bool, string) { + // 创建HTTP客户端 + client := &http.Client{ + Timeout: 5 * time.Second, + } + + // 发送GET请求 + resp, err := client.Get(url) + if err != nil { + return false, fmt.Sprintf("下载文件失败: %v", err) + } + defer resp.Body.Close() + + // 检查响应状态码 + if resp.StatusCode != http.StatusOK { + return false, fmt.Sprintf("下载文件失败,HTTP状态码: %d", resp.StatusCode) + } + + // 创建目标文件 + out, err := os.Create(path) + if err != nil { + return false, fmt.Sprintf("创建文件失败: %v", err) + } + defer out.Close() + + // 将响应内容写入文件 + _, err = io.Copy(out, resp.Body) + if err != nil { + return false, fmt.Sprintf("写入文件失败: %v", err) + } + + return true, fmt.Sprintf("文件下载成功: %s", path) +} diff --git a/agent-wdd/op/PackageOperator.go b/agent-wdd/op/PackageOperator.go index 74b04da..c6a32cb 100644 --- a/agent-wdd/op/PackageOperator.go +++ b/agent-wdd/op/PackageOperator.go @@ -4,7 +4,12 @@ import ( "agent-wdd/config" "agent-wdd/log" "agent-wdd/utils" + "encoding/json" + "fmt" + "io" + "net/http" "strings" + "time" ) type PackageOperator struct { @@ -36,12 +41,19 @@ var ( "yum", "remove", "-y", }, upgradePrefix: []string{}, + initCommand: []string{ + "yum", "makecache", + }, } ) -func (op *PackageOperator) Install(tools []string) { +func (op *PackageOperator) Install(tools []string) bool { // 判定本机的包管理Operator - generatePackageOperator() + ok := generatePackageOperator() + if !ok { + log.Error("PackageOperator init failed! 无法执行安装操作 %s", tools) + return false + } // install seperately for _, tool := range tools { @@ -51,14 +63,19 @@ func (op *PackageOperator) Install(tools []string) { utils.BeautifulPrint(result) } } + + return true } -// PackageInit 初始化包管理器, 如果本机是ubuntu或者debian, 则使用apt, 否则使用yum -func (op *PackageOperator) PackageInit() { +// PackageInit 初始化包管理器, 同样会判定主机是否可以联网,如果本机是ubuntu或者debian, 则使用apt, 否则使用yum +func (op *PackageOperator) PackageInit() bool { log.Info("PackageInit !") // 判定本机的包管理Operator - generatePackageOperator() + ok := generatePackageOperator() + if !ok { + return false + } // package init os := config.ConfigCache.Agent.OS @@ -72,6 +89,8 @@ func (op *PackageOperator) PackageInit() { } } + return true + } func (op *PackageOperator) Remove(tools []string) { @@ -89,13 +108,20 @@ func (op *PackageOperator) Remove(tools []string) { } -func generatePackageOperator() { +func generatePackageOperator() bool { // cache return if AgentPackOperator.initCommand != nil { - return + return true } - // 检查本机是否存在Os的信息 + + // 检查是否可以连接互联网 + if config.CanConnectInternet() <= 1 { + log.Error("服务器无法连接互联网,无法初始化包管理器") + return false + } + + // 检查本机是否存在OS的信息 os := config.ConfigCache.Agent.OS if os.Hostname == "" { os.Gather() @@ -108,4 +134,64 @@ func generatePackageOperator() { } else { AgentPackOperator = yumPackageOperator } + + return true +} + +// GetLatestGithubReleaseVersion 获取GitHub仓库的最新版本 +func GetLatestGithubReleaseVersion(repoOwner, repoName string) (string, error) { + // 检查是否可以连接互联网 + if config.CanConnectInternet() <= 1 { + return "", fmt.Errorf("服务器无法连接互联网,无法获取GitHub最新版本") + } + + // 设置超时时间为10秒的HTTP客户端 + client := &http.Client{ + Timeout: 10 * time.Second, + } + + // 构建GitHub API URL + apiURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repoOwner, repoName) + + // 发送GET请求 + resp, err := client.Get(apiURL) + if err != nil { + return "", fmt.Errorf("请求GitHub API失败: %v", err) + } + defer resp.Body.Close() + + // 检查响应状态码 + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("GitHub API返回非200状态码: %d", resp.StatusCode) + } + + // 读取响应体 + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("读取响应内容失败: %v", err) + } + + // 解析JSON响应 + var release struct { + TagName string `json:"tag_name"` + } + + if err := json.Unmarshal(body, &release); err != nil { + return "", fmt.Errorf("解析JSON响应失败: %v", err) + } + + return release.TagName, nil +} + +// CommandExists 判定命令是否存在 +func CommandExists(command string) bool { + ok, _ := SingleLineCommandExecutor([]string{"command", "-v", command}) + return ok +} + +func CommandExistsByPath(command string) bool { + + // 查询 /usr/bin /usr/local/bin中是否有可执行文件 + ok, _ := SingleLineCommandExecutor([]string{"find", "/usr/bin", "/usr/local/bin", "-name", command}) + return ok } diff --git a/agent-wdd/op/PackageOperator_test.go b/agent-wdd/op/PackageOperator_test.go new file mode 100644 index 0000000..c14610c --- /dev/null +++ b/agent-wdd/op/PackageOperator_test.go @@ -0,0 +1,21 @@ +package op + +import ( + "testing" +) + +func TestGetLatestGithubReleaseVersion(t *testing.T) { + repoOwner := "docker" + repoName := "compose" + + version, err := GetLatestGithubReleaseVersion(repoOwner, repoName) + if err != nil { + t.Fatalf("获取最新版本失败: %v", err) + } + + if version == "" { + t.Error("获取的版本号为空") + } + + t.Logf("获取到的最新版本号: %s", version) +}