From 5c39bd759413b228b099796aa2a2e1d79a8b89c4 Mon Sep 17 00:00:00 2001 From: zeaslity Date: Fri, 28 Feb 2025 17:45:12 +0800 Subject: [PATCH] Enhance Docker Installation and Management Commands - Improved Docker installation process for Ubuntu systems - Added support for dynamic Docker version detection - Enhanced Docker local and online installation commands - Implemented more robust Docker removal functionality - Updated Docker installation to use system-specific package sources - Added better error handling and logging for Docker operations - Refined Docker service startup and configuration checks --- agent-wdd/cmd/Base.go | 200 ++++++++++++++++++++++--- agent-wdd/cmd/Zsh.go | 14 +- agent-wdd/cmd/root.go | 12 +- agent-wdd/config/Config.go | 31 ++-- agent-wdd/config/Network.go | 41 +++-- agent-wdd/config/OS.go | 29 +++- agent-wdd/one-build-and-run.ps1 | 4 +- agent-wdd/op/Excutor.go | 4 +- agent-wdd/op/PackageOperator.go | 60 ++++++-- agent-wdd/op/SystemdExcutor.go | 8 + agent-wdd/test/one-click-build-run.ps1 | 27 ++++ agent-wdd/{ => test}/readme_help.txt | 6 +- agent-wdd/test/run_test.sh | 52 +++++++ agent-wdd/test/test-shell.sh | 135 +++++++++++++++++ agent-wdd/utils/DownloadUtils.go | 47 +++++- agent-wdd/utils/PrintUtils.go | 2 +- 16 files changed, 586 insertions(+), 86 deletions(-) create mode 100644 agent-wdd/test/one-click-build-run.ps1 rename agent-wdd/{ => test}/readme_help.txt (83%) create mode 100644 agent-wdd/test/run_test.sh create mode 100644 agent-wdd/test/test-shell.sh diff --git a/agent-wdd/cmd/Base.go b/agent-wdd/cmd/Base.go index 8f056be..b518297 100644 --- a/agent-wdd/cmd/Base.go +++ b/agent-wdd/cmd/Base.go @@ -8,6 +8,7 @@ import ( "agent-wdd/utils" "fmt" "runtime" + "strings" "github.com/spf13/cobra" ) @@ -282,8 +283,14 @@ func addSSHSubcommands(sshCmd *cobra.Command) { port := "22333" log.Info("[ModifySSHPort] modify ssh port to: %s", port) - // 修改ssh端口 - utils.AppendContentToFile(fmt.Sprintf("Port %s", port), "/etc/ssh/sshd_config") + // 文件中已经包含了Port 22333 则pass + sshdPortString := fmt.Sprintf("Port %s", port) + if utils.FindContentInFile(sshdPortString, "/etc/ssh/sshd_config") { + log.Info("[ModifySSHPort] ssh port already modified to: %s", port) + } else { + // 修改ssh端口 + utils.AppendContentToFile(sshdPortString, "/etc/ssh/sshd_config") + } // 重启ssh服务 ok, resultLog := op.SystemdRestart("sshd") @@ -309,7 +316,7 @@ func addSSHSubcommands(sshCmd *cobra.Command) { } // 修改ssh配置 - utils.AppendContentToFile(beans.DefaultSshdConfig, "/etc/ssh/sshd_config") + utils.AppendOverwriteContentToFile(beans.DefaultSshdConfig, "/etc/ssh/sshd_config") // 重启ssh服务 ok, resultLog := op.SystemdRestart("sshd") @@ -330,8 +337,7 @@ func addSSHSubcommands(sshCmd *cobra.Command) { func addDockerSubcommands(cmd *cobra.Command) { onlineCmd := &cobra.Command{ Use: "online [version]", - Short: "网络安装Docker", - Args: cobra.ExactArgs(1), + Short: "网络安装Docker, 如果不指定参数默认安装20.10.15版本", Run: func(cmd *cobra.Command, args []string) { // 检查参数 @@ -339,15 +345,124 @@ func addDockerSubcommands(cmd *cobra.Command) { fmt.Printf("Installing Docker version: %s\n", args[0]) } - // 没有传递参数,使用默认参数 - version := "20.10.15" - log.Info("Installing Docker version: %s", version) - // 安装docker packOperator := op.AgentPackOperator packOperator.PackageInit() - // ubuntu 和 centos的安装命令是不是一样的 - packOperator.Install([]string{"docker-ce", "docker-ce-cli", "containerd.io", "docker-buildx-plugin", "docker-compose-plugin"}) + + configCache := config.ConfigCache + if configCache.Agent.OS.IsUbuntuType { + // 安装apt-transport-https ca-certificates curl gnupg software-properties-common 依赖部分 + packOperator.Install([]string{"apt-transport-https", "ca-certificates", "curl", "gnupg", "software-properties-common"}) + + // 安装docker的 gpg key + dockerGPGFilePath := "/etc/apt/keyrings/docker.gpg" + dockerAPTFilePath := "/etc/apt/sources.list.d/docker.list" + dockerGPGSource := "https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg" + dockerAPTSource := "https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu" + + // 创建docker的 gpg key 文件 + utils.CreateFolder("/etc/apt/keyrings") + + // 删除docker的 gpg key 文件 + utils.RemoveFile(dockerGPGFilePath) + utils.RemoveFile(dockerAPTFilePath) + + // 如果可以访问外网,则使用外网源 + if configCache.Agent.Network.Internet == 9 { + dockerGPGSource = "https://download.docker.com/linux/ubuntu/gpg" + dockerAPTSource = "https://download.docker.com/linux/ubuntu" + } + + ok, l2 := op.PipeLineCommandExecutor([][]string{ + { + "curl", + "-fsSL", + dockerGPGSource, + }, + { + "gpg", + "--dearmor", + "-o", + dockerGPGFilePath, + }, + }) + + if !ok { + log.Error("下载docker gpg文件失败: %s", l2) + return + } + + if !utils.FileExistAndNotNull(dockerGPGFilePath) { + log.Error("添加gpg失败!") + return + } + + op.SingleLineCommandExecutor([]string{ + "chmod", + "a+r", + dockerGPGFilePath, + }, + ) + + if configCache.Agent.OS.OSReleaseCode == "" { + log.Error("获取系统发行版代号失败! 无法安装docker!") + return + } + + dockerAPTSourceCommand := "deb [arch=" + configCache.Agent.OS.Arch + " signed-by=" + dockerGPGFilePath + "] " + dockerAPTSource + " " + configCache.Agent.OS.OSReleaseCode + " stable" + + log.Info("dockerAPTSourceCommand is => %s ", dockerAPTSourceCommand) + + utils.AppendOverwriteContentToFile(dockerAPTSourceCommand, dockerAPTFilePath) + + // 强制初始化APT update + packOperator.PackageInitForce() + + // 20.04 default + specificDockerVersion := "5:20.10.24~3-0~ubuntu-" + configCache.Agent.OS.OSReleaseCode + // apt-cache madison docker-ce | grep 20.10.20 | awk '{print$3}' + + // get by method + ok, log4 := op.HardCodeCommandExecutor("apt-cache madison docker-ce | grep 20.10.20 | awk '{print$3}'") + if ok && log4 != nil && len(log4) > 0 { + specificDockerVersion = strings.TrimSpace(log4[0]) + fmt.Println("get docker version from online => " + specificDockerVersion) + } + + log.Info("需要安装的docker版本为 => %s", specificDockerVersion) + + dockerStaffList := []string{ + "docker-ce=" + specificDockerVersion, + "docker-ce-cli=" + specificDockerVersion, + "containerd.io", + "docker-compose-plugin", + } + + ok = packOperator.Install(dockerStaffList) + if !ok { + log.Error("安装docker存在问题,请检查!") + return + } + + } else { + log.Error("当前系统不是ubuntu系统,暂时无法安装docker!") + return + } + + log.Info("安装docker成功!") + + // 启动docker + op.SystemdUp("docker") + op.SystemdEnable("docker") + + // 检查docker是否启动成功 + ok, resultLog := op.SystemIsRunning("docker") + if !ok { + log.Error("docker启动失败: %s", resultLog) + return + } + + log.Info("docker启动成功! ") }, } @@ -357,13 +472,45 @@ func addDockerSubcommands(cmd *cobra.Command) { Short: "卸载Docker", Run: func(cmd *cobra.Command, args []string) { fmt.Println("Removing Docker...") + + // 检查docker是二进制安装还是通过包安装的 + dockerExists := op.CommandExistsByPath("docker") + if !dockerExists { + log.Info("Docker 未安装,无需卸载!") + return + } + + // 卸载docker + packOperator := op.AgentPackOperator + packOperator.PackageInit() + packOperator.Remove([]string{"docker-ce", "docker-ce-cli", "containerd.io", "docker-buildx-plugin", "docker-compose-plugin"}) + + // 删除docker目录 + utils.RemoveFile("/usr/bin/docker") + utils.RemoveFile("/usr/bin/docker-compose") + utils.RemoveFile("/usr/bin/docker-buildx") + utils.RemoveFile("/usr/bin/docker-compose-plugin") + utils.RemoveFile("/usr/bin/containerd") + + // 删除docker的配置文件 systemd + utils.RemoveFile("/etc/systemd/system/docker.service") + utils.RemoveFile("/etc/systemd/system/docker.socket") + utils.RemoveFile("/etc/systemd/system/containerd.service") + utils.RemoveFile(beans.ContainerdServiceFile) + utils.RemoveFile(beans.DockerSocketFile) + utils.RemoveFile(beans.DockerServiceFile) + + // 删除docker的日志文件 + utils.RemoveFile("/var/log/docker") + + log.Info("Docker 卸载成功!") + }, } localCmd := &cobra.Command{ - Use: "local [path]", - Short: "本地安装Docker", - Args: cobra.ExactArgs(1), + Use: "local", + Short: "本地安装Docker, 默认安装20.10.15版本, 安装目录为 " + dockerLocalInstallPath, Run: func(cmd *cobra.Command, args []string) { log.Info("Installing Docker from local file: %s", dockerLocalInstallPath) @@ -385,11 +532,24 @@ func addDockerSubcommands(cmd *cobra.Command) { } // 设置权限 - chmodCmd := []string{"chmod", "777", "-R", "/usr/bin/docker*"} - ok, resultLog := op.SingleLineCommandExecutor(chmodCmd) - if !ok { - log.Error("Failed to set permissions for Docker binaries: %s", resultLog) - return + dockerBinList := []string{ + "docker-init", + "containerd", + "ctr", + "runc", + "dockerd", + "docker-proxy", + "containerd-shim", + "docker", + "containerd-shim-runc-v2", + } + + for _, bin := range dockerBinList { + ok, resultLog := op.HardCodeCommandExecutor("chmod 777 /usr/bin/" + bin) + if !ok { + log.Error("Failed to set permissions for Docker binaries: %s", resultLog) + return + } } // 配置并启动Docker服务 @@ -408,7 +568,7 @@ func addDockerSubcommands(cmd *cobra.Command) { } log.Info("docker dameon file append success !") - ok, resultLog = op.SystemdDaemonReload() + ok, resultLog := op.SystemdDaemonReload() if !ok { log.Error("daemon reload error ! %s", resultLog) return diff --git a/agent-wdd/cmd/Zsh.go b/agent-wdd/cmd/Zsh.go index 0a7686f..e9f38a1 100644 --- a/agent-wdd/cmd/Zsh.go +++ b/agent-wdd/cmd/Zsh.go @@ -76,13 +76,13 @@ func addZshSubcommands(cmd *cobra.Command) { log.Info("安装zsh完成, 开始安装插件!") pluginsHardCodeUrl := []string{ - "git clone https://gitee.com/wangl-cc/zsh-autosuggestions.git ~/.oh-my-zsh/plugins/zsh-autosuggestions", - "git clone https://gitee.com/xiaoqqya/zsh-syntax-highlighting.git ~/.oh-my-zsh/plugins/zsh-syntax-highlighting", + "git clone https://gitee.com/wangl-cc/zsh-autosuggestions.git /root/.oh-my-zsh/plugins/zsh-autosuggestions", + "git clone https://gitee.com/xiaoqqya/zsh-syntax-highlighting.git /root/.oh-my-zsh/plugins/zsh-syntax-highlighting", } pluginsHardCodeOutsideUrl := []string{ - "git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/plugins/zsh-autosuggestions", - "git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/plugins/zsh-syntax-highlighting", + "git clone https://github.com/zsh-users/zsh-autosuggestions /root/.oh-my-zsh/plugins/zsh-autosuggestions", + "git clone https://github.com/zsh-users/zsh-syntax-highlighting.git /root/.oh-my-zsh/plugins/zsh-syntax-highlighting", } pluginsListUrl := []string{ @@ -122,9 +122,9 @@ func addZshSubcommands(cmd *cobra.Command) { } // 修改ZSH配置 - utils.FindAndReplaceContentInFile("/root/.zshrc", "robbyrussell", "agnoster") - utils.FindAndReplaceContentInFile("/root/.zshrc", "# DISABLE_AUTO_UPDATE=\"true\"", "DISABLE_AUTO_UPDATE=\"true\"") - utils.FindAndReplaceContentInFile("/root/.zshrc", "plugins=(git)", "plugins=(git zsh-autosuggestions zsh-syntax-highlighting command-not-found z themes)") + utils.FindAndReplaceContentInFile("robbyrussell", "agnoster", "/root/.zshrc") + utils.FindAndReplaceContentInFile("# DISABLE_AUTO_UPDATE=\"true\"", "DISABLE_AUTO_UPDATE=\"true\"", "/root/.zshrc") + utils.FindAndReplaceContentInFile("plugins=(git)", "plugins=(git zsh-autosuggestions zsh-syntax-highlighting command-not-found z themes)", "/root/.zshrc") // op.SingleLineCommandExecutor([]string{ diff --git a/agent-wdd/cmd/root.go b/agent-wdd/cmd/root.go index e9962c8..a735674 100644 --- a/agent-wdd/cmd/root.go +++ b/agent-wdd/cmd/root.go @@ -15,13 +15,17 @@ var rootCmd = &cobra.Command{ Short: "wdd应用程序是wdd封装的NB工具", Long: `使用golang的强大特性,加上 WDD 的高超技术,打造一个方便、高效、适用于各种LINUX系统的瑞士军刀工具! 尽情享用! 尽情享用! 尽情享用!`, - Run: func(cmd *cobra.Command, args []string) { - // Do Stuff Here + DisableAutoGenTag: true, + CompletionOptions: cobra.CompletionOptions{ + DisableDefaultCmd: true, + DisableNoDescFlag: true, + DisableDescriptions: true, + HiddenDefaultCmd: true, }, } func init() { - + // 初始化配置 cobra.OnInitialize(config.InitConfig) } @@ -138,7 +142,7 @@ func Execute() { func printAllCommands(cmd *cobra.Command, writer io.Writer) { // 打印命令标题 - fmt.Fprintf(writer, "Available commands:\n") + fmt.Fprintf(writer, "All available commands:\n\n") // 递归打印命令树 var traverse func(*cobra.Command, int) diff --git a/agent-wdd/config/Config.go b/agent-wdd/config/Config.go index e6c361a..7b2a94c 100644 --- a/agent-wdd/config/Config.go +++ b/agent-wdd/config/Config.go @@ -50,15 +50,16 @@ type Agent struct { } type OS struct { - Hostname string `yaml:"hostname"` - OsName string `yaml:"os_name"` - OsFamily string `yaml:"os_family"` - OsVersion string `yaml:"os_version"` - OsType string `yaml:"os_type"` - Kernel string `yaml:"kernel"` - Arch string `yaml:"arch"` - IsUbuntuType bool `yaml:"is_ubuntu_type" comment:"是否是ubuntu类型的操作系统"` - PackInit bool `yaml:"pack_init" comment:"是否初始化,ubuntu需要"` + Hostname string `mapstructure:"hostname" yaml:"hostname" comment:"主机名"` + OsName string `mapstructure:"os_name" yaml:"os_name" comment:"操作系统名称"` + OsFamily string `mapstructure:"os_family" yaml:"os_family" comment:"操作系统家族"` + OsVersion string `mapstructure:"os_version" yaml:"os_version" comment:"操作系统版本"` + OsType string `mapstructure:"os_type" yaml:"os_type" comment:"操作系统类型"` + Kernel string `mapstructure:"kernel" yaml:"kernel" comment:"内核版本"` + Arch string `mapstructure:"arch" yaml:"arch" comment:"架构"` + IsUbuntuType bool `mapstructure:"is_ubuntu_type" yaml:"is_ubuntu_type" comment:"是否是ubuntu类型的操作系统"` + PackInit bool `mapstructure:"pack_init" yaml:"pack_init" comment:"是否初始化,ubuntu需要"` + OSReleaseCode string `mapstructure:"os_release_code" yaml:"os_release_code" comment:"主机操作系统的发行版代号, focal之类的"` } type Network struct { @@ -68,11 +69,12 @@ type Network struct { } type PublicInfo struct { - IPv4 string `yaml:"ipv4"` - IPv6 string `yaml:"ipv6"` - Country string `yaml:"country"` - City string `yaml:"city"` - ASN string `yaml:"asn"` + IPv4 string `yaml:"ipv4"` + IPv6 string `yaml:"ipv6"` + Country string `yaml:"country"` + City string `yaml:"city"` + ASN string `yaml:"asn"` + Timezone string `yaml:"timezone"` } type Interface struct { @@ -122,7 +124,6 @@ func InitConfig() { v.SetConfigType("yaml") if err := v.ReadInConfig(); err != nil { - log.Error("读取配置文件失败: %w", err) panic(err) diff --git a/agent-wdd/config/Network.go b/agent-wdd/config/Network.go index 777bbb5..5e23bd4 100644 --- a/agent-wdd/config/Network.go +++ b/agent-wdd/config/Network.go @@ -46,7 +46,7 @@ func GetInterfaces() []Interface { // 获取所有网卡信息 netInterfaces, err := net.Interfaces() - log.Info("all network interfaces: %v", netInterfaces) + // log.Info("all network interfaces: %v", netInterfaces) if err != nil { log.Error("获取网卡信息失败: %v", err) return interfaces @@ -120,17 +120,35 @@ func judgeCanConnectInternet() int { Timeout: 3 * time.Second, } - _, err := client.Get("https://www.google.com") - if err == nil { - return 9 + results := make(chan int, 2) + + go func() { + _, err := client.Get("https://www.google.com") + if err == nil { + results <- 9 + } else { + results <- 1 + } + }() + + go func() { + _, err := client.Get("https://www.baidu.com") + if err == nil { + results <- 7 + } else { + results <- 1 + } + }() + + maxResult := 1 + for i := 0; i < 2; i++ { + result := <-results + if result > maxResult { + maxResult = result + } } - _, err = client.Get("https://www.baidu.com") - if err == nil { - return 7 - } - - return 1 + return maxResult } // GetPublicInfo 获取服务器的公网信息 @@ -194,13 +212,14 @@ func (p PublicInfo) GetPublicInfo() PublicInfo { } // 打印解析结果 - log.Info("IP信息:\n%+v\n", info) + // log.Info("IP信息:\n%+v\n", info) // 保存信息 p.IPv4 = info.IP p.ASN = info.Org p.Country = info.Country p.City = info.City + p.Timezone = info.Timezone ConfigCache.Agent.Network.Public = p diff --git a/agent-wdd/config/OS.go b/agent-wdd/config/OS.go index 4ae87b9..835033f 100644 --- a/agent-wdd/config/OS.go +++ b/agent-wdd/config/OS.go @@ -26,7 +26,8 @@ func (o *OS) Gather() { o.Hostname = hostname } - o.OsType = "linux" // 固定为linux + o.OsType = "linux" // 固定为linux + o.IsUbuntuType = true // 默认为ubuntu // 解析系统信息 file, err := os.Open("/etc/os-release") @@ -78,12 +79,30 @@ func (o *OS) Gather() { } // 检查包管理的方式 - c := exec.Command("command", "-v", "apt") - _, err = c.Output() - if err == nil { - o.IsUbuntuType = true + if strings.Contains(o.OsFamily, "centos") || strings.Contains(o.OsFamily, "rhel") { + o.IsUbuntuType = false } // 获取系统架构 o.Arch = runtime.GOARCH + + // 获取系统发行版代号 + o.OSReleaseCode = judgeUbuntuReleaseFromOsVersion(o.OsVersion) + +} + +func judgeUbuntuReleaseFromOsVersion(osVersion string) string { + + switch osVersion { + case "16.04": + return "xenial" + case "18.04": + return "bionic" + case "20.04": + return "focal" + case "22.04": + return "jammy" + default: + return "ubuntu-unknown" + } } diff --git a/agent-wdd/one-build-and-run.ps1 b/agent-wdd/one-build-and-run.ps1 index b76f85b..7529c06 100644 --- a/agent-wdd/one-build-and-run.ps1 +++ b/agent-wdd/one-build-and-run.ps1 @@ -4,7 +4,7 @@ try { $ErrorActionPreference = "Stop" Write-Host "1. Building binary exec file..." -ForegroundColor Cyan - & "C:\Users\wdd\go\bin\gox.exe" -osarch="linux/amd64" -output "build/agent-wdd_{{.OS}}_{{.Arch}}" + & "C:\Users\wddsh\go\bin\gox.exe" -osarch="linux/amd64" -output "build/agent-wdd_{{.OS}}_{{.Arch}}" # 执行远程ssh命令 ssh root@192.168.35.71 "rm /root/agent-wdd_linux_amd64" Write-Host "2. Cleaning old binary file..." -ForegroundColor Yellow @@ -16,7 +16,7 @@ try { Write-Host "4. Exec the command ..." -ForegroundColor Blue Write-Host "" Write-Host "" - ssh root@192.168.35.71 "chmod +x agent-wdd_linux_amd64 && ./agent-wdd_linux_amd64 info network" + ssh root@192.168.35.71 "chmod +x agent-wdd_linux_amd64 && ./agent-wdd_linux_amd64 help" Write-Host "" Write-Host "" Write-Host "5. Cheak Info Result ..." -ForegroundColor Blue diff --git a/agent-wdd/op/Excutor.go b/agent-wdd/op/Excutor.go index 6b49e91..9a6b03e 100644 --- a/agent-wdd/op/Excutor.go +++ b/agent-wdd/op/Excutor.go @@ -179,7 +179,7 @@ func HardCodeCommandExecutor(hardCodeCommand string) (ok bool, resultLog []strin cmd := exec.Command(hardCodeCommand) // 执行命令并获取错误信息 - err := cmd.Run() + cmd.Run() // 合并输出结果 stdoutBuf := bytes.Buffer{} @@ -189,7 +189,7 @@ func HardCodeCommandExecutor(hardCodeCommand string) (ok bool, resultLog []strin output := mergeOutput(&stdoutBuf, &stderrBuf) - return err == nil, output + return true, output } func main() { diff --git a/agent-wdd/op/PackageOperator.go b/agent-wdd/op/PackageOperator.go index c6a32cb..e79cfa9 100644 --- a/agent-wdd/op/PackageOperator.go +++ b/agent-wdd/op/PackageOperator.go @@ -3,12 +3,10 @@ package op import ( "agent-wdd/config" "agent-wdd/log" - "agent-wdd/utils" "encoding/json" "fmt" "io" "net/http" - "strings" "time" ) @@ -60,8 +58,11 @@ func (op *PackageOperator) Install(tools []string) bool { ok, result := SingleLineCommandExecutor(append(AgentPackOperator.installPrefix, tool)) if !ok { log.Error("[install] failed! => %s", tool) - utils.BeautifulPrint(result) } + + // 打印安装的过程内容 + beautifulPrintListWithTitle(result, fmt.Sprintf("安装 %s 的过程内容", tool)) + } return true @@ -71,21 +72,29 @@ func (op *PackageOperator) Install(tools []string) bool { func (op *PackageOperator) PackageInit() bool { log.Info("PackageInit !") + // beautifulPrintListWithTitle(config.ConfigCache) + + // package init + os := config.ConfigCache.Agent.OS + + if os.PackInit { + log.Info("PackageInit already done! skip!") + return true + } + // 判定本机的包管理Operator ok := generatePackageOperator() if !ok { return false } - // package init - os := config.ConfigCache.Agent.OS - osFamily := strings.ToLower(os.OsFamily) + os.PackInit = true + os.SaveConfig() - if strings.Contains(osFamily, "ubuntu") || strings.Contains(osFamily, "debian") { + if os.IsUbuntuType { ok, resultLog := SingleLineCommandExecutor(aptPackageOperator.initCommand) if !ok { - log.Error("APT init failed! please check !") - utils.BeautifulPrint(resultLog) + log.Error("APT init failed! please check ! %s", resultLog) } } @@ -93,6 +102,23 @@ func (op *PackageOperator) PackageInit() bool { } +func (op *PackageOperator) PackageInitForce() bool { + + log.Info("PackageInitForce !") + + os := config.ConfigCache.Agent.OS + + if os.IsUbuntuType { + ok, resultLog := SingleLineCommandExecutor(aptPackageOperator.initCommand) + if !ok { + log.Error("APT init failed! please check ! %s", resultLog) + } + log.Info("APT init success! %s", resultLog) + } + + return true +} + func (op *PackageOperator) Remove(tools []string) { // 判定本机的包管理Operator generatePackageOperator() @@ -102,7 +128,7 @@ func (op *PackageOperator) Remove(tools []string) { ok, result := SingleLineCommandExecutor(append(AgentPackOperator.removePrefix, tool)) if !ok { log.Error("[remove] failed! => %s", tool) - utils.BeautifulPrint(result) + beautifulPrintListWithTitle(result, fmt.Sprintf("移除 %s 的过程内容", tool)) } } @@ -112,6 +138,7 @@ func generatePackageOperator() bool { // cache return if AgentPackOperator.initCommand != nil { + log.Info("PackageOperator init success! %v", AgentPackOperator.initCommand) return true } @@ -125,13 +152,14 @@ func generatePackageOperator() bool { os := config.ConfigCache.Agent.OS if os.Hostname == "" { os.Gather() - os.SaveConfig() } if os.IsUbuntuType { + log.Info("Ubuntu type detected! use apt package operator!") AgentPackOperator = aptPackageOperator } else { + log.Info("Other type detected! use yum package operator!") AgentPackOperator = yumPackageOperator } @@ -195,3 +223,13 @@ func CommandExistsByPath(command string) bool { ok, _ := SingleLineCommandExecutor([]string{"find", "/usr/bin", "/usr/local/bin", "-name", command}) return ok } + +func beautifulPrintListWithTitle(contend []string, title string) { + + fmt.Println() + fmt.Println(">>>>>>>> " + title + " <<<<<<<<") + for _, line := range contend { + fmt.Println(line) + } + fmt.Println(">>>>>>>> end <<<<<<<<") +} diff --git a/agent-wdd/op/SystemdExcutor.go b/agent-wdd/op/SystemdExcutor.go index c9535c1..ad7fce3 100644 --- a/agent-wdd/op/SystemdExcutor.go +++ b/agent-wdd/op/SystemdExcutor.go @@ -79,3 +79,11 @@ func SystemdDaemonReload() (bool, []string) { } return true, resultLog } + +func SystemIsRunning(serviceName string) (bool, []string) { + ok, resultLog := SingleLineCommandExecutor([]string{"systemctl", "is-active", serviceName}) + if !ok { + return false, resultLog + } + return true, resultLog +} diff --git a/agent-wdd/test/one-click-build-run.ps1 b/agent-wdd/test/one-click-build-run.ps1 new file mode 100644 index 0000000..005be73 --- /dev/null +++ b/agent-wdd/test/one-click-build-run.ps1 @@ -0,0 +1,27 @@ + + +# mc.exe alias set oracle-osaka-1 https://axzsapxffbbn.compat.objectstorage.ap-osaka-1.oraclecloud.com b0696c316f87b644b4a13bcb020f095cd147be0b GAIaM/064epLzQcXsRbj2gwlFOrVepjCR23wj2tfJ+A= + +# 重新build项目 +Set-Location "C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd" +& "C:\Users\wddsh\go\bin\gox.exe" -osarch="linux/amd64" -output "build/agent-wdd_{{.OS}}_{{.Arch}}" + + +# mc.exe ls oracle-osaka-1/osaka +# 删除上面存在的旧的内容 +mc.exe rm oracle-osaka-1/osaka/agent-wdd_linux_amd64 +mc.exe rm oracle-osaka-1/osaka/test-shell.sh + + +# 上传文件 +mc.exe cp C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\build\agent-wdd_linux_amd64 oracle-osaka-1/osaka/ +mc.exe cp C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\test\test-shell.sh oracle-osaka-1/osaka/ + + +Set-Location "C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\test" + + + + + + diff --git a/agent-wdd/readme_help.txt b/agent-wdd/test/readme_help.txt similarity index 83% rename from agent-wdd/readme_help.txt rename to agent-wdd/test/readme_help.txt index 948e035..3c0c9af 100644 --- a/agent-wdd/readme_help.txt +++ b/agent-wdd/test/readme_help.txt @@ -1,4 +1,5 @@ Available commands: + acme acme相关的内容 base 服务器基础操作 docker Docker相关操作 @@ -19,11 +20,6 @@ base 服务器基础操作 swap 关闭系统的Swap sysconfig 修改系统的sysconfig tools 通用工具安装 利用本机的yum,apt等从网络安装常用的软件 -completion Generate the autocompletion script for the specified shell - bash Generate the autocompletion script for bash - fish Generate the autocompletion script for fish - powershell Generate the autocompletion script for powershell - zsh Generate the autocompletion script for zsh config 配置文件管理 show 显示agent运行配置 download 文件下载,直接下载 [url] [dest_path] diff --git a/agent-wdd/test/run_test.sh b/agent-wdd/test/run_test.sh new file mode 100644 index 0000000..6eeaafe --- /dev/null +++ b/agent-wdd/test/run_test.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +rm -f /usr/local/bin/agent-wdd +rm -f /usr/local/bin/test-shell.sh + +wget https://pan.107421.xyz/d/oracle-osaka-1/agent-wdd_linux_amd64 -O /usr/local/bin/agent-wdd + +chmod +x /usr/local/bin/agent-wdd + + +wget https://pan.107421.xyz/d/oracle-osaka-1/test-shell.sh -O /usr/local/bin/test-shell.sh + +chmod +x /usr/local/bin/test-shell.sh + + + + +bash /usr/local/bin/test-shell.sh + + + + +/usr/local/bin/agent-wdd info network +/usr/local/bin/agent-wdd info cpu +/usr/local/bin/agent-wdd info mem +/usr/local/bin/agent-wdd info swap +/usr/local/bin/agent-wdd info disks + +cat /usr/local/etc/wdd/agent-wdd-config.yaml + + +/usr/local/bin/agent-wdd base docker local + +/usr/local/bin/agent-wdd info os +/usr/local/bin/agent-wdd base docker online + +/usr/local/bin/agent-wdd info os +/usr/local/bin/agent-wdd zsh + +/usr/local/bin/agent-wdd base tools + +/usr/local/bin/agent-wdd base swap + +/usr/local/bin/agent-wdd base firewall + +/usr/local/bin/agent-wdd base selinux + +/usr/local/bin/agent-wdd base sysconfig + + +/usr/local/bin/agent-wdd download https://pan.107421.xyz/d/oracle-osaka-1/docker-amd64-20.10.15.tgz /root/wdd + diff --git a/agent-wdd/test/test-shell.sh b/agent-wdd/test/test-shell.sh new file mode 100644 index 0000000..ccec991 --- /dev/null +++ b/agent-wdd/test/test-shell.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# set -eo pipefail + +# 测试配置 +TEST_REPEATS=2 +AGENT_BIN="/usr/local/bin/agent-wdd" # 修正路径拼写错误 + +# 输出颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' + +# 测试统计 +declare -i TOTAL_TESTS=0 PASSED_TESTS=0 FAILED_TESTS=0 + +# 实用函数 +log() { + echo -e "${YELLOW}[INFO] $* ${NC}" +} + +pass() { + echo -e "${GREEN}PASS: $* ${NC}" + ((PASSED_TESTS++)) + ((TOTAL_TESTS++)) +} + +fail() { + echo -e "${RED}FAIL: $* ${NC}" + ((FAILED_TESTS++)) + ((TOTAL_TESTS++)) +} + +test_command() { + local cmd="$1" + local expected="${2:-0}" + + if ! eval "$cmd"; then + local exit_code=$? + [[ $exit_code -eq "$expected" ]] || { + fail "$cmd (exit $exit_code)" + return 1 + } + fi + pass "$cmd" + return 0 +} + +repeat_test() { + local times=$1 + local cmd="$2" + local expected="${3:-0}" + + for ((i=1; i<=times; i++)); do + test_command "$cmd" "$expected" + done +} + +test_base_docker() { + log "\nTesting Docker commands..." + test_command "$AGENT_BIN base docker remove" 0 + test_command "$AGENT_BIN base docker online" 0 + repeat_test $TEST_REPEATS "$AGENT_BIN base docker local" 0 + + if docker --version >/dev/null 2>&1; then + pass "docker installation" + else + fail "docker installation" + fi +} + +test_base_ssh() { + log "\nTesting SSH commands..." + repeat_test $TEST_REPEATS "$AGENT_BIN base ssh port 2222" 0 + test_command "$AGENT_BIN base ssh config" 0 +} + +test_tools() { + log "\nTesting tools installation..." + local tools=("htop" "tmux" "nano") + for tool in "${tools[@]}"; do + if command -v "$tool" >/dev/null 2>&1; then + pass "$tool already installed" + else + test_command "$AGENT_BIN base tools $tool" 0 + fi + done +} + +test_info_commands() { + log "\nTesting info commands..." + local commands=( + "$AGENT_BIN info all" + "$AGENT_BIN info cpu" + "$AGENT_BIN info disk" + "$AGENT_BIN info mem" + "$AGENT_BIN info network" + "$AGENT_BIN info os" + ) + + for cmd in "${commands[@]}"; do + repeat_test $TEST_REPEATS "$cmd" 0 + done +} + +main() { + # 前置检查 + [[ -x "$AGENT_BIN" ]] || { + log "Error: Agent binary not found or not executable: $AGENT_BIN" + exit 1 + } + + log "Starting tests with $AGENT_BIN..." + + # 基本信息测试 + test_command "$AGENT_BIN version" 0 + test_command "$AGENT_BIN help" 0 + + # 基础模块测试 + test_base_docker + test_base_ssh + test_tools + + # 信息模块测试 + test_info_commands + + log "\nTest Summary:" + echo -e "Total Tests: $TOTAL_TESTS" + echo -e "${GREEN}Passed: $PASSED_TESTS ${NC}" + echo -e "${RED}Failed: $FAILED_TESTS ${NC}" + + exit "$FAILED_TESTS" +} + +main diff --git a/agent-wdd/utils/DownloadUtils.go b/agent-wdd/utils/DownloadUtils.go index 9f5e211..0d0794d 100644 --- a/agent-wdd/utils/DownloadUtils.go +++ b/agent-wdd/utils/DownloadUtils.go @@ -1,10 +1,13 @@ package utils import ( + "agent-wdd/log" "fmt" "io" "net/http" "os" + "os/exec" + "path/filepath" "strings" "time" ) @@ -18,7 +21,19 @@ func DownloadFile(url string, path string) (bool, string) { return DownloadFileWithClient(client, url, path) } +// DownloadFileWithClient 使用http客户端下载文件 func DownloadFileWithClient(client *http.Client, url string, path string) (bool, string) { + + // path如果是一个目录,则需要获取文件名 + // 获取url使用 / 分割最后的一部分 + // 如果path是目录,则需要获取文件名 + + if IsDirOrFile(path) { + fileName := strings.Split(url, "/")[len(strings.Split(url, "/"))-1] + path = filepath.Join(path, fileName) + log.Info("path是目录,自动获取文件名为 => : %s", path) + } + return downloadWithProgress(client, url, path) } @@ -27,14 +42,14 @@ func downloadWithProgress(client *http.Client, url, dest string) (bool, string) // 创建目标文件 file, err := os.Create(dest) if err != nil { - return false, fmt.Sprintf("创建文件失败: %w", err) + return false, fmt.Sprintf("创建文件失败: %s", err.Error()) } defer file.Close() // 发起请求 resp, err := client.Get(url) if err != nil { - return false, fmt.Sprintf("HTTP请求失败: %w", err) + return false, fmt.Sprintf("HTTP请求失败: %s", err.Error()) } defer resp.Body.Close() @@ -46,6 +61,13 @@ func downloadWithProgress(client *http.Client, url, dest string) (bool, string) size := resp.ContentLength var downloaded int64 + // 不支持下载超过10GB的文件 + + if size > 10*1024*1024*1024 || size < 0 { + log.Error("文件大小超过10GB,或者文件大小未知,不支持高级下载方式! 尝试使用wget下载") + return downloadFileByWget(url, dest) + } + // 打印下载信息 fmt.Printf("开始下载: %s 大小: %s\n", url, HumanSizeInt(size)) @@ -60,13 +82,32 @@ func downloadWithProgress(client *http.Client, url, dest string) (bool, string) // 执行拷贝 if _, err := io.Copy(file, progressReader); err != nil { - return false, fmt.Sprintf("文件拷贝失败: %w", err) + return false, fmt.Sprintf("文件拷贝失败: %s", err.Error()) } fmt.Print("\n") // 保持最后进度显示的完整性 return true, fmt.Sprintf("文件下载成功: %s", dest) } +// 使用wget下载文件 +func downloadFileByWget(url, dest string) (bool, string) { + + log.Info("使用wget下载文件: %s", fmt.Sprintf("wget %s -qO %s", url, dest)) + cmd := exec.Command("wget", url, "-qO", dest) + _, err := cmd.CombinedOutput() + if err != nil { + return false, fmt.Sprintf("wget下载失败: %s", err.Error()) + } + + // 检查文件是否存在且不为空 + fileInfo, err := os.Stat(dest) + if err != nil || fileInfo.Size() == 0 { + return false, fmt.Sprintf("wget下载失败 文件不存在或为空: %s", dest) + } + + return true, fmt.Sprintf("wget下载成功: %s", dest) +} + // 进度跟踪Reader type progressReader struct { io.Reader diff --git a/agent-wdd/utils/PrintUtils.go b/agent-wdd/utils/PrintUtils.go index 1bbfd61..b6f845f 100644 --- a/agent-wdd/utils/PrintUtils.go +++ b/agent-wdd/utils/PrintUtils.go @@ -41,7 +41,7 @@ func BeautifulPrintWithTitle(contend any, title string) { func BeautifulPrintListWithTitle(contend []string, title string) { fmt.Println() - fmt.Println(fmt.Sprintf("content tile is => %s", title)) + fmt.Println(">>>>>>>> " + title + " <<<<<<<<") for _, line := range contend { fmt.Println(line) }