[ Cmii ] [ Octopus ] - reformat agent-go - 1

This commit is contained in:
zeaslity
2024-03-29 11:39:14 +08:00
parent aa4412f042
commit 1be48aaac2
52 changed files with 683 additions and 557 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
package a_executor
import (
"testing"
"wdd.io/agent-common/assert"
"wdd.io/agent-go/a_agent"
)
var agentOP = &AgentOsOperator{
InstallCommandPrefix: []string{
"yum", "install", "-y",
},
RemoveCommandPrefix: []string{"yum", "remove", "-y"},
CanAccessInternet: true,
IsOsTypeUbuntu: false,
IsOsTypeCentOS: true,
IsOsTypeEuler: true,
IsAgentInnerWall: true,
AgentArch: "amd64",
AgentOSReleaseCode: "focal",
AgentServerInfo: &a_agent.AgentServerInfo{
ServerName: "",
ServerIPPbV4: "",
ServerIPInV4: "10.250.0.100",
ServerIPPbV6: "",
ServerIPInV6: "",
Location: "",
Provider: "",
ManagePort: "",
CPUCore: "",
CPUBrand: "",
OSInfo: "",
OSKernelInfo: "",
TCPControl: "",
Virtualization: "",
IoSpeed: "",
MemoryTotal: "",
DiskTotal: "",
DiskUsage: "",
Comment: "",
MachineID: "",
AgentVersion: "",
TopicName: "",
},
OssOfflinePrefix: "http://10.250.0.100:9000/octopus/",
}
func TestBaseFunc(t *testing.T) {
//command := "DISABLE_SELINUX"
//command := "installDocker"
//command := "installDockerCompose"
command := "installHarbor"
funcArgs := []string{
"10.250.0.147",
"",
"",
"",
}
//agentOP.Exec("shutdownFirewall")
//agentOP.Exec("modifyHostname")
//agentOP.Exec("disableSwap")
//agentOP.Exec("enableSwap")
//agentOP.Exec("removeDocker")
//agentOP.Exec("installDocker", "20")
//agentOP.Exec("removeDockerCompose")
//agentOP.Exec("installDockerCompose")
//agentOP.Exec("installHelm")
//agentOP.Exec("installHarbor")
//agentOP.Exec("chronyToMaster", "192.168.0.8")
//agentOP.Exec("installZSH")
//agentOP.Exec("ok")
exec, strings := agentOP.Exec(command, funcArgs...)
assert.Equal(t, exec, true, "exec should be true!")
t.Logf("[%s] exec result are %s", command, strings)
}

View File

@@ -0,0 +1,683 @@
package a_executor
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"os/exec"
"strings"
)
var RemoveForcePrefix = []string{"rm", "-rf"}
// BasicCommandExists 判定命令是否存在
func BasicCommandExists(commandName string) bool {
cmd := exec.Command("command", "-v", commandName)
err := cmd.Run()
if err != nil {
log.DebugF("指令 %s 不存在 => %s", commandName, err.Error())
return false
} else {
return true
}
}
// BasicCommandExistsBatch 判定批量命令是否存在
func BasicCommandExistsBatch(commandNameList []string) (bool, string) {
for _, commandName := range commandNameList {
if !BasicCommandExists(commandName) {
return false, commandName
}
}
return true, ""
}
// BasicCommandExistByPath 根据路径判定 命令是否存在
func BasicCommandExistByPath(commandName string) bool {
if BasicFileExistInFolder(commandName, "/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "bin", "/snap/bin") {
return true
}
return false
}
// BasicFileExistInFolder 查询fileName是否存在于目录folderList中如果存在返回true不存在返回false
func BasicFileExistInFolder(fileName string, folderList ...string) bool {
for _, folder := range folderList {
if BasicFolderExists(folder) {
// is folder
ok, _ := PipelineCommandExecutor([][]string{
{
"ls",
folder,
},
{
"grep",
"-c",
fileName,
},
})
if ok {
return true
}
} else {
// it's a file
ok, _ := PipelineCommandExecutor([][]string{
{
"echo",
folder,
},
{
"grep",
"-c",
fileName,
},
})
if ok {
return true
}
}
}
return false
}
func BasicGrepItemInFile(item string, fileName string) bool {
if !BasicFileExistAndNotNull(fileName) {
log.ErrorF("[BasicGrepItemInFile] - fileName [ %s ] not exits !", fileName)
return false
}
ok, _ := PipelineCommandExecutor([][]string{
{
"cat",
fileName,
},
{
"grep",
"-q",
item,
},
})
if ok {
return true
}
return false
}
func BasicFindContentInFile(content string, fileName string) bool {
// Open the file
file, err := os.Open(fileName)
if err != nil {
log.ErrorF("[BasicFindContentInFile] - file not exits ")
return false
}
defer file.Close()
// Create a scanner to read the file line by line
scanner := bufio.NewScanner(file)
// Set the split function for the scanner
scanner.Split(bufio.ScanLines)
// Iterate over the lines of the file
for scanner.Scan() {
// Get the current line
line := scanner.Text()
// Check if the current line contains the search term
if strings.Contains(line, content) {
return true
}
}
// Check for any errors that occurred during scanning
if err := scanner.Err(); err != nil {
log.ErrorF("[BasicFindContentInFile] - scanner error ! %s", err.Error())
}
return false
}
func BasicFindAndDeleteContendLineInFile(content string, fileName string) bool {
// Open the file
file, err := os.Open(fileName)
if err != nil {
log.ErrorF("[FindDeleteContendLineInFile] - file not exits ")
return false
}
defer file.Close()
// Create a scanner to read the file line by line
scanner := bufio.NewScanner(file)
// Set the split function for the scanner
scanner.Split(bufio.ScanLines)
// 创建一个新的文件内容变量
var newContent string
// Iterate over the lines of the file
for scanner.Scan() {
// Get the current line
line := scanner.Text()
// Check if the current line contains the search term
if !strings.Contains(line, content) {
newContent += line + "\n"
}
}
// Check for any errors that occurred during scanning
if err := scanner.Err(); err != nil {
log.ErrorF("[BasicFindContentInFile] - scanner error ! %s", err.Error())
return false
}
// 将修改后的内容写回文件
err = os.WriteFile(fileName, []byte(newContent), os.ModePerm)
if err != nil {
log.ErrorF("[FindDeleteContendLineInFile] - write file %s error with contend %s", fileName, newContent)
return false
}
return true
}
func BasicFindContentInCommandOutput(hardCodeCommand string, content string) (bool, []string) {
ok, resultLog := HardCodeCommandExecutor(hardCodeCommand)
if !ok {
log.ErrorF("[BasicFindContentInCommandOutput] - command error ! => %v", resultLog)
return false, nil
}
return BasicFindContentInList(content, resultLog), resultLog
}
func BasicFindContentInList(content string, list []string) bool {
for _, item := range list {
if strings.Contains(item, content) {
return true
}
}
return false
}
func BasicDockerImageExistByFullName(imageFullName string) bool {
var imageVersion string
split := strings.Split(imageFullName, ":")
imageVersion = split[len(split)-1]
imageFullName = strings.TrimSuffix(imageFullName, ":"+imageVersion)
//log.DebugF("[BasicDockerImageExistByFullName] -- %s %s", imageFullName, imageVersion)
return BasicDockerImageExists(imageFullName, imageVersion)
}
func BasicDockerImageExists(imageName, imageVersion string) bool {
if !BasicCommandExistByPath("docker") {
return false
}
ok, _ := PipelineCommandExecutor([][]string{
{
"docker",
"image",
"ls",
},
{
"grep",
imageName,
},
{
"grep",
"-q",
imageVersion,
},
})
if !ok {
return false
}
return true
}
func BasicInstallSoftwares(installPrefix []string, isStrict bool, softwares ...string) (bool, []string) {
var installLog []string
for _, software := range softwares {
log.DebugF("[BasicInstallSoftwares] - going to install [ %s ]", software)
if !PureResultSingleExecute(append(installPrefix, software)) {
failedInstall := fmt.Sprintf("[BasicInstallSoftwares] - software of [ %s ] install failed !", software)
installLog = append(installLog, failedInstall)
if isStrict {
return false, installLog
}
}
successInstall := fmt.Sprintf("[BasicInstallSoftwares] - software of [ %s ] install success !", software)
installLog = append(installLog, successInstall)
}
return true, installLog
}
// BasicReplace 基础替换命令
func BasicReplace(filename string, origin string, replace string) bool {
// 暂不添加
//if !BasicFileExistAndNotNull(filename) {
// log.DebugF("文件替换")
//}
cmd := exec.Command("sed", "-i", "s/"+origin+"/"+replace+"/g", filename)
err := cmd.Run()
if err != nil {
log.DebugF("替换文件 %s ,从 [%s] => [%s] 错误!", filename, origin, replace)
return false
} else {
return true
}
}
func BasicRemoveFolderComplete(folderName string) bool {
if !BasicFileExists(folderName) || !BasicFolderExists(folderName) {
log.DebugF("[BasicRemoveFolderComplete] - file or folder of [%s] not exists !", folderName)
return true
}
cmd := exec.Command("rm", "-rf", folderName)
err := cmd.Run()
if err != nil {
log.DebugF("删除 %s 失败!", folderName)
return false
} else {
return true
}
}
// BasicFileExists 检测文件是否存在
func BasicFileExists(filename string) bool {
cmd := exec.Command("test", "-f", filename)
err := cmd.Run()
if err != nil {
log.DebugF("文件 %s 不存在!", filename)
return false
} else {
return true
}
}
// BasicFileExistAndNotNull 文件不为空返回true 文件为空返回false
func BasicFileExistAndNotNull(filename string) bool {
cmd := exec.Command("test", "-s", filename)
err := cmd.Run()
if err != nil {
log.DebugF("文件 %s 为空!", filename)
return false
} else {
return true
}
}
func BasicFolderExists(folderName string) bool {
cmd := exec.Command("test", "-d", folderName)
err := cmd.Run()
if err != nil {
log.DebugF("目录 %s 不存在!", folderName)
return false
} else {
return true
}
}
func BasicCreateFolder(folderName string) bool {
// 检查目录是否存在
if _, err := os.Stat(folderName); os.IsNotExist(err) {
// 目录不存在,创建目录
err := os.MkdirAll(folderName, os.ModePerm)
if err != nil {
// 如果创建目录失败返回false
return false
}
}
// 目录存在或者成功创建返回true
return true
}
func BasicRemoveFileOrFolder(folderName string) bool {
// Check if the folder exists
if _, err := os.Stat(folderName); os.IsNotExist(err) {
return true // folder does not exist
}
// Folder exists, remove it
err := os.Remove(folderName)
if err != nil {
// Handle error
return false
}
return true // folder was removed
}
// BasicPrettyPrint 打印执行结果
func BasicPrettyPrint(resultOk bool, resultLog []string) {
fmt.Printf("resultOK is => %#v\n", resultOk)
if resultLog == nil || len(resultLog) == 0 {
return
}
for _, s := range resultLog {
fmt.Println(s)
fmt.Println()
}
}
// BasicSystemdShutdown 使用Systemd关闭服务,会判定服务器是否存在
func BasicSystemdShutdown(serviceName string) (ok bool, resultLog []string) {
if !strings.HasSuffix(serviceName, ".service") {
serviceName = serviceName + ".service"
}
// 检查是否存在
existService := BasicFileExistInFolder(serviceName, "/lib/systemd/system/",
"/etc/systemd/system")
if !existService {
return true, []string{
serviceName,
"该服务不存在!",
}
}
// 关闭
var shutDownCommand = [][]string{
{
"systemctl",
"stop",
serviceName,
},
{
"sleep",
"1",
},
{
"systemctl",
"disable",
serviceName,
},
}
resultOK := PureResultSingleExecuteBatch(shutDownCommand)
return resultOK, resultLog
}
// BasicSystemdUp 使用Systemd启动服务,会判定服务器是否存在
func BasicSystemdUp(serviceName string) (ok bool, resultLog []string) {
if !strings.HasSuffix(serviceName, ".service") {
serviceName = serviceName + ".service"
}
// 检查是否存在
existService := BasicFileExistInFolder(serviceName, "/lib/systemd/system/",
"/etc/systemd/system")
if !existService {
return true, []string{
serviceName,
"该服务不存在!",
}
}
// 关闭
var shutDownCommand = [][]string{
{
"systemctl",
"start",
serviceName,
},
{
"sleep",
"1",
},
{
"systemctl",
"enable",
serviceName,
},
}
resultOK := PureResultSingleExecuteBatch(shutDownCommand)
return resultOK, resultLog
}
// BasicTransPipelineCommand 转换手写的管道命令为可执行的形式
func BasicTransPipelineCommand(pipelineString string) (pipelineCommand [][]string) {
var pipelineCommandC [][]string
split := strings.Split(pipelineString, "|")
if len(split) == 0 {
log.WarnF("输入的pipelineString有误 %s", pipelineString)
return pipelineCommandC
}
for _, s := range split {
// 去除掉无效的空行
singleCommand := strings.Split(s, " ")
var realSingleCommand []string
for i := range singleCommand {
if singleCommand[i] != "" {
realSingleCommand = append(realSingleCommand, singleCommand[i])
}
}
pipelineCommandC = append(pipelineCommandC, realSingleCommand)
}
return pipelineCommandC
}
func BasicConvertBufferToSlice(outputBuffer bytes.Buffer) (resultLog []string) {
s := outputBuffer.String()
split := strings.Split(s, "\n")
return split
}
// BasicDownloadFile 从目标地址下载文件到目的地,并检测文件是否下载成功
func BasicDownloadFile(downloadUrl, desFile string) (downloadOk bool, resultLog []string) {
// wget or curl download
var ok bool
if BasicCommandExistByPath("wget") {
ok, resultLog = AllCommandExecutor([]string{
"wget",
"--no-check-certificate",
"--timeout=5",
downloadUrl,
"-qO",
desFile,
})
} else if BasicCommandExistByPath("curl") {
ok, resultLog = AllCommandExecutor([]string{
"curl",
"-o",
desFile,
"--insecure",
"--max-time",
"5",
downloadUrl,
})
} else {
sprintf := fmt.Sprintf("[BasicDownloadFile] - neither wget or curl exists ! can't download file [ %s ] from [ %s ]", desFile, downloadUrl)
log.Error(sprintf)
return false, []string{
sprintf,
}
}
errLog := fmt.Sprintf("[BasicDownloadFile] - download file [ %s ] from [ %s ] failed !", desFile, downloadUrl)
if !ok {
log.Error(errLog)
return false, []string{
errLog,
}
}
// check file exists
existAndNotNull := BasicFileExistAndNotNull(desFile)
if !existAndNotNull {
return false, []string{
errLog,
"[BasicDownloadFile] - file not exist !",
}
}
return true, nil
}
func BasicDownloadFileWithProxy(downloadUrl, proxyUrl, desFile string) (downloadOk bool, resultLog []string) {
var ok bool
if !BasicCommandExistByPath("curl") {
return false, []string{
"curl not exits!",
}
}
ok, resultLog = AllCommandExecutor([]string{
"curl",
"-x",
proxyUrl,
"-o",
desFile,
"--insecure",
"--max-time",
"5",
downloadUrl,
})
errLog := fmt.Sprintf("[BasicDownloadFileWithProxy] - download file [ %s ] with proxy [ %s ] from [ %s ] failed !", desFile, proxyUrl, downloadUrl)
if !ok {
log.Error(errLog)
return false, []string{
errLog,
}
}
// check file exists
existAndNotNull := BasicFileExistAndNotNull(desFile)
if !existAndNotNull {
return false, []string{
errLog,
"[BasicDownloadFile] - file not exist !",
}
}
return true, nil
}
// BasicAppendSourceToFile 将源文件的内容添加到目标文件使用golang标准库完成跨平台、安全性更强
func BasicAppendSourceToFile(sourceFile, targetFile string) bool {
// 打开源文件
source, err := os.Open(sourceFile)
if err != nil {
log.ErrorF("[BasicAppendSourceToFile] - error open source file => %s, error is %s", sourceFile, err.Error())
return false
}
defer source.Close()
// 打开目标文件,如果不存在则创建,如果存在则在末尾追加
target, err := os.OpenFile(targetFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.ErrorF("[BasicAppendSourceToFile] - error open target file => %s, error is %s", sourceFile, err.Error())
return false
}
defer target.Close()
// 将源文件内容复制到目标文件
_, err = io.Copy(target, source)
if err != nil {
log.ErrorF("[BasicAppendSourceToFile] - Error appending to target file: %s", err.Error())
return false
}
return true
}
// BasicAppendContentToFile 向目标文件中追加写入一些内容
func BasicAppendContentToFile(content string, targetFile string) bool {
// 打开文件用于追加。如果文件不存在,将会创建一个新文件。
file, err := os.OpenFile(targetFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.ErrorF("[BasicAppendContentToFile] - Error opening file: %s , error is %s", targetFile, err.Error())
return false
}
defer file.Close() // 确保文件最终被关闭
// 写入内容到文件
if _, err := file.WriteString(content); err != nil {
log.ErrorF("[BasicAppendContentToFile] - Error writing to file: %s , error is %s", targetFile, err.Error())
return false
}
return true
}
// BasicAppendNullToFile 清空一个文件
func BasicAppendNullToFile(targetFile string) bool {
// 使用os.O_TRUNC清空文件内容
file, err := os.OpenFile(targetFile, os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
log.ErrorF("[BasicAppendNullToFile] - Error opening file: %s, error is %s", targetFile, err.Error())
return false
}
defer file.Close() // 确保在函数退出前关闭文件
return true
}
func BasicWordSpaceCompletion(source string, totalLength int) string {
sourceLength := len(source)
if sourceLength >= totalLength {
return source
}
for i := 0; i < totalLength-sourceLength; i++ {
source += " "
}
return source
}

View File

@@ -0,0 +1,108 @@
package a_executor
import (
"strconv"
"testing"
"wdd.io/agent-common/assert"
)
var emptyFilePath = "/home/wdd/IdeaProjects/ProjectOctopus/agent-go/executor/script/123"
var noExistFilePath = "/home/wdd/IdeaProjects/ProjectOctopus/agent-go/executor/script/456"
func TestBasicFileExistAndNotNull(t *testing.T) {
resultOK := BasicFileExistAndNotNull(emptyFilePath)
assert.Equal(t, resultOK, false, "判定为空文件返回false")
}
func TestBasicReplaceFileNotExists(t *testing.T) {
replace := BasicReplace(noExistFilePath, "123", "123")
t.Logf("replace no exists file result are => %#v", replace)
}
func TestBasicFileExists(t *testing.T) {
exists := BasicFileExists(emptyFilePath)
assert.Equal(t, exists, true, "文件存在但是为空应该返回true")
}
func TestBasicFileExistFalse(t *testing.T) {
exists := BasicFileExists(noExistFilePath)
assert.Equal(t, exists, false, "文件不存在应该返回false")
}
func TestBasicSystemdShutdown(t *testing.T) {
ok, resultLog := BasicSystemdShutdown("docker.service")
//ok, resultLog := BasicSystemdShutdown("docker")
//ok, resultLog := BasicSystemdShutdown("fwd")
t.Logf("result ok is %v resultLog is %v", ok, resultLog)
}
func TestBasicSystemdUp(t *testing.T) {
//ok, resultLog := BasicSystemdUp("docker.service")
//ok, resultLog := BasicSystemdUp("docker")
ok, resultLog := BasicSystemdUp("fwd")
t.Logf("result ok is %v resultLog is %v", ok, resultLog)
}
func TestBasicCommandExists(t *testing.T) {
exists := BasicCommandExists("docker-compose")
t.Logf("command exists is => %s", strconv.FormatBool(exists))
}
func TestBasicDockerImageExistByFullName(t *testing.T) {
// Test cases
testCases := []struct {
name string
imageFullName string
expectedResult bool
}{
{
name: "Image exists with full name and version",
imageFullName: "harbor.cdcyy.com.cn/cmii/busybox:0326",
expectedResult: true,
},
{
name: "Image does not exist with full name and version",
imageFullName: "nonexistentImage:latest",
expectedResult: false,
},
{
name: "Image exists with full name but no version",
imageFullName: "harbor.cdcyy.com.cn/cmii/busybox",
expectedResult: true,
},
{
name: "Image does not exist with full name but no version",
imageFullName: "nonexistentImage",
expectedResult: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := BasicDockerImageExistByFullName(tc.imageFullName)
if result != tc.expectedResult {
t.Errorf("Expected %t for imageFullName %s, but got %t", tc.expectedResult, tc.imageFullName, result)
}
})
}
}

View File

@@ -0,0 +1,213 @@
package a_executor
import (
"bufio"
"bytes"
"fmt"
"os/exec"
"strconv"
"strings"
"wdd.io/agent-common/logger"
)
type ExecutionMessage struct {
NeedResultReplay bool `json:"needResultReplay"`
DurationTask bool `json:"durationTask,default:false"`
ExecutionType string `json:"executionType"`
FuncContent []string `json:"funcContent"`
SingleLineCommand []string `json:"singleLineCommand"`
MultiLineCommand [][]string `json:"multiLineCommand"`
PipeLineCommand [][]string `json:"pipeLineCommand"`
ResultKey string `json:"resultKey"`
}
var log = logger.Log
var AgentOsOperatorCache = &AgentOsOperator{}
func Activate() {
log.Info("Module [ EXECUTOR ] activated !")
//// 转换类型
//executionMsgString := octopusMessage.Content.(string)
//
//// 解析 ExecutionMessage
//var executionMessage *a_executor.ExecutionMessage
//err := json.Unmarshal([]byte(executionMsgString), &executionMessage)
//if err != nil {
// log.Error(fmt.Sprintf("execution message convert to json is wrong! msg is => %s", executionMsgString))
// return
//}
//
//// 执行命令
//ok, resultLog := a_executor.Execute(executionMessage)
//if ok {
// octopusMessage.ResultCode = "200"
//} else {
// octopusMessage.ResultCode = "300"
//}
//
//// 返回结果
//if executionMessage.NeedResultReplay {
// // send back the result log
// octopusMessage.Result = resultLog
//}
//// 返回时间
//octopusMessage.ACTime = utils.ParseDateTimeTime()
//
//// 返回结果
//octopusMessage.SendToOctopusServer()
}
func Execute(em *ExecutionMessage) (bool, []string) {
var resultLog []string
var err error
ok := true
executionContent := em.ExecutionType + " == " + strings.Join(em.FuncContent, " - ")
log.DebugF("em message is => %#v", em)
if strings.HasPrefix(em.ExecutionType, "BASE") {
// base function
if len(em.FuncContent) > 1 {
ok, resultLog = AgentOsOperatorCache.Exec(em.FuncContent[0], em.FuncContent[1:]...)
} else {
ok, resultLog = AgentOsOperatorCache.Exec(em.FuncContent[0])
}
} else if strings.HasPrefix(em.ExecutionType, "APP") {
// app function
if len(em.FuncContent) > 1 {
ok, resultLog = AgentOsOperatorCache.Deploy(em.FuncContent[0], em.FuncContent[1:]...)
} else {
ok, resultLog = AgentOsOperatorCache.Deploy(em.FuncContent[0])
}
} else if strings.HasPrefix(em.ExecutionType, "HARBOR") {
// harbor function
if em.FuncContent == nil || len(em.FuncContent) <= 1 {
ok = false
resultLog = []string{
"[Harbor Execute] - functions args is wrong!",
}
}
// Harbor Execute
ok, resultLog = HarborOperatorCache.Exec(em.FuncContent[0], em.FuncContent[1:]...)
} else if strings.HasPrefix(em.ExecutionType, "IMAGE") {
// image function
if em.FuncContent == nil || len(em.FuncContent) <= 1 {
ok = false
resultLog = []string{
"[Harbor Execute] - functions args is wrong!",
}
}
// Harbor Execute
ok, resultLog = AgentOsOperatorCache.Sync(em.FuncContent[0], em.FuncContent[1:]...)
} else {
// deprecated
// shell command
if em.PipeLineCommand != nil && len(em.PipeLineCommand) != 0 {
// 管道命令
resultLog, err = PipeLineCommandExecutor(em.PipeLineCommand)
executionContent = fmt.Sprintf("%v", em.PipeLineCommand)
} else if em.MultiLineCommand != nil && len(em.MultiLineCommand) != 0 {
// 多行命令
resultLog, err = MultiLineCommandExecutor(em.MultiLineCommand)
executionContent = fmt.Sprintf("%v", em.MultiLineCommand)
} else {
// 单行命令
resultLog, err = FormatAllCommandExecutor(em.SingleLineCommand)
executionContent = fmt.Sprintf("%v", em.SingleLineCommand)
}
}
// 归一化错误和日志
if err != nil {
resultLog = append(resultLog, " 命令执行错误如下 ", err.Error())
}
resultLog = append(resultLog, fmt.Sprintf("命令 %v 执行结果为 %s", executionContent, strconv.FormatBool(ok)))
// debug
log.DebugF("%v", resultLog)
return ok, resultLog
}
func PipeLineCommandExecutor(pipeLineCommand [][]string) ([]string, error) {
var resultSlice []string
var output []byte
var err error
tmp := make([]string, len(pipeLineCommand))
for index, pipe := range pipeLineCommand {
tmp[index] = strings.Join(pipe, " ")
}
pipelineCommandString := strings.Join(tmp, " | ")
resultSlice = append(resultSlice, fmt.Sprintf(" ========= 命令为 ====> %s", pipelineCommandString))
for _, pipeCommand := range pipeLineCommand {
if len(pipeCommand) == 0 {
continue
}
command := exec.Command(pipeCommand[0], pipeCommand[1:]...)
if len(output) > 0 {
command.Stdin = bytes.NewBuffer(output)
}
output, err = command.CombinedOutput()
if err != nil {
log.ErrorF("Pipeline Command Command Error => %v", err.Error())
// 收集错误的信息
resultSlice = append(resultSlice, "↓↓↓ 命令 错误 如下 ↓↓↓", string(output))
return resultSlice, err
}
}
// 正常的输出
resultSlice = append(resultSlice, "↓↓↓ 命令 输出 如下 ↓↓↓", string(output))
return resultSlice, err
}
func MultiLineCommandExecutor(multiLineCommandExecutor [][]string) ([]string, error) {
var res []string
for _, singleLineCommand := range multiLineCommandExecutor {
singleLogs, err := FormatAllCommandExecutor(singleLineCommand)
res = append(res, singleLogs...)
if err != nil {
log.Error(fmt.Sprintf("Execution error ! command is %v, error is %v", singleLineCommand, err))
return res, err
}
}
return res, nil
}
// SingleLineCommandExecutor 执行单行命令
func SingleLineCommandExecutor(singleLineCommand []string) ([]string, error) {
cmd := exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
err := cmd.Run()
scanner := bufio.NewScanner(&out)
var result []string
for scanner.Scan() {
result = append(result, scanner.Text())
}
if err != nil {
return nil, err
}
return result, nil
}

View File

@@ -0,0 +1,270 @@
package a_executor
import (
"bufio"
"bytes"
"fmt"
"io"
"os/exec"
"strings"
)
// AllCommandExecutor 正确或者错误的信息全部收集,共同返回
func AllCommandExecutor(singleLineCommand []string) (resultOk bool, resultLog []string) {
// result
var resultSlice []string
resultOk = true
cmd := exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.DebugF("command %v stdout error => %v", singleLineCommand, err)
resultOk = false
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.DebugF("command %v stderr error => %v", singleLineCommand, err)
resultOk = false
}
if err := cmd.Start(); err != nil {
log.DebugF("command %v runtime error => %v", singleLineCommand, err)
resultOk = false
}
// 收集错误或者
resultSlice = collectOutput(stdout, resultSlice)
resultSlice = collectOutput(stderr, resultSlice)
if err := cmd.Wait(); err != nil {
log.DebugF("command %v result error => %v", singleLineCommand, err)
resultOk = false
}
log.DebugF("all command of %v result are => %v", singleLineCommand, resultSlice)
return resultOk, resultSlice
}
// AllCompleteExecutor 多行命令的执行函数,返回执行正确与否和执行结果
func AllCompleteExecutor(multiCommand [][]string) (resultOk bool, resultLog []string) {
var result []string
resultOk = true
for _, singleLineCommand := range multiCommand {
ok, resultLog := AllCommandExecutor(singleLineCommand)
if !ok {
// 执行出错
resultOk = false
}
result = append(result, resultLog...)
}
return resultOk, result
}
// FormatAllCommandExecutor 收集全部执行结果、错误并且返回
func FormatAllCommandExecutor(singleLineCommand []string) ([]string, error) {
// result
var resultSlice []string
var resultError error
var cmd *exec.Cmd
if len(singleLineCommand) > 1 {
cmd = exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
} else {
cmd = exec.Command(singleLineCommand[0])
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.ErrorF("command %v stdout error => %v", singleLineCommand, err)
resultError = err
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.ErrorF("command %v stderr error => %v", singleLineCommand, err)
resultError = err
}
if err := cmd.Start(); err != nil {
log.ErrorF("command %v runtime error => %v", singleLineCommand, err)
}
resultSlice = append(resultSlice, fmt.Sprintf(" 开始执行命令 ====> %s", singleLineCommand), "↓↓↓ 命令 输出 如下 ↓↓↓")
resultSlice = collectOutput(stdout, resultSlice)
resultSlice = append(resultSlice, "↓↓↓ 命令 错误 如下 ↓↓↓")
resultSlice = collectOutput(stderr, resultSlice)
if err := cmd.Wait(); err != nil {
log.ErrorF("command %v result error => %v", singleLineCommand, err)
resultError = err
}
log.DebugF("real time exec result are %v", resultSlice)
return resultSlice, resultError
}
func PipelineCommandExecutor(pipelineCommand [][]string) (resultOk bool, resultLog []string) {
if len(pipelineCommand) == 0 {
return true, nil
} else if len(pipelineCommand) == 1 {
log.Debug("输入的PipelineCommand长度有误")
return AllCommandExecutor(pipelineCommand[0])
}
var c []string
cmd1 := exec.Command(pipelineCommand[0][0], pipelineCommand[0][1:]...)
var outputBuf1 bytes.Buffer
cmd1.Stdout = &outputBuf1
if err := cmd1.Start(); err != nil {
sprintf := fmt.Sprintf("Error: The first command can not be startup %s", err)
return false, append(c, sprintf)
}
if err := cmd1.Wait(); err != nil {
sprintf := fmt.Sprintf("Error: Couldn't wait for the first command: %s", err)
return false, append(c, sprintf)
}
for i := 1; i < len(pipelineCommand); i++ {
cmd2 := exec.Command(pipelineCommand[i][0], pipelineCommand[i][1:]...)
sprintf := fmt.Sprintf("current command is %s", pipelineCommand[i])
c = append(c, sprintf)
cmd2.Stdin = &outputBuf1
var outputBuf2 bytes.Buffer
cmd2.Stdout = &outputBuf2
if err := cmd2.Start(); err != nil {
sprintf := fmt.Sprintf("Error: The second command can not be startup: %s", err)
return false, append(c, sprintf)
}
if err := cmd2.Wait(); err != nil {
sprintf := fmt.Sprintf("Error: Couldn't wait for the second command: %s", err)
return false, append(c, sprintf)
}
// change
outputBuf1 = outputBuf2
}
s := outputBuf1.String()
split := strings.Split(s, "\n")
return true, split
}
// PureResultSingleExecute 执行单行命令,忽略输出,只对执行成功与否负责
func PureResultSingleExecute(singleCommand []string) (resultOK bool) {
cmd := exec.Command(singleCommand[0], singleCommand[1:]...)
err := cmd.Run()
if err != nil {
log.ErrorF("指令 %s 执行 错误, 错误内容为 %s", singleCommand, err.Error())
return false
} else {
log.DebugF("指令 %s 执行 成功", singleCommand)
return true
}
}
// PureResultSingleExecuteBatch 批量 执行单行命令,忽略输出,只对执行成功与否负责
func PureResultSingleExecuteBatch(singleCommandList [][]string) (resultOK bool) {
result := true
for _, singleCommand := range singleCommandList {
if !PureResultSingleExecute(singleCommand) {
result = false
}
}
return result
}
// ReadTimeCommandExecutor 执行命令,并且实时返回结果
func ReadTimeCommandExecutor(singleLineCommand []string) bool {
cmd := exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.ErrorF("command %v stdout error => %v", singleLineCommand, err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.ErrorF("command %v stderr error => %v", singleLineCommand, err)
}
if err := cmd.Start(); err != nil {
log.ErrorF("command %v runtime error => %v", singleLineCommand, err)
}
go realTimeOutput(stdout)
go realTimeOutput(stderr)
if err := cmd.Wait(); err != nil {
log.ErrorF("command %v result error => %v", singleLineCommand, err)
return false
}
return true
}
func realTimeOutput(r io.Reader) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func collectOutput(r io.Reader, resultSlice []string) []string {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
resultLine := scanner.Text()
resultSlice = append(resultSlice, resultLine)
// debug usage
//fmt.Println(resultLine)
}
return resultSlice
}
// HardCodeCommandExecutor 执行硬编码的shell命令如 echo sda > sdasd ; rpcinfo -p localhost 等
func HardCodeCommandExecutor(hardCodeCommand string) (ok bool, resultLog []string) {
// result
var resultSlice []string
resultOk := true
cmd := exec.Command("sh", "-c", hardCodeCommand)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.DebugF("hard code command %v stdout error => %s", hardCodeCommand, err)
resultOk = false
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.DebugF("hard code command %v stderr error => %s", hardCodeCommand, err)
resultOk = false
}
if err := cmd.Start(); err != nil {
log.DebugF("hard code command %v runtime error => %v", hardCodeCommand, err)
resultOk = false
}
// 收集错误或者
resultSlice = collectOutput(stdout, resultSlice)
resultSlice = collectOutput(stderr, resultSlice)
if err := cmd.Wait(); err != nil {
log.DebugF("hard code command %v result error => %v", hardCodeCommand, err)
resultOk = false
}
//log.DebugF("hard code command of [ %s ] result are => %v", hardCodeCommand, resultSlice)
return resultOk, resultSlice
}

View File

@@ -0,0 +1,224 @@
package a_executor
import (
"bytes"
"fmt"
"os"
"os/exec"
"reflect"
"strings"
"testing"
)
var closeSELinux = []string{
"sed",
"-i",
"s/SELINUX=enforcing/SELINUX=disabled/g",
"/etc/selinux/config",
}
var callShellScript = []string{
"/bin/bash",
"/root/IdeaProjects/ProjectOctopus/agent-go/tmp/simple.sh",
}
var shutdownFirewalld = []string{
// wrong usage of &&
"systemctl", "stop", "firewalld", "&&", "systemctl", "disable", "firewalld",
}
var wgetCommand = []string{
"wget",
"--timeout=10",
"https://oss-s1.107421.xyz/octopus_ssh_banner",
"-O",
"/home/wdd/IdeaProjects/ProjectOctopus/agent-go/executor/script/123",
}
var echoPathCommand = []string{
"echo",
"$PATH",
}
var pipelineCommandFalse = []string{
"ls",
"/etc",
"|",
"grep",
"passwd",
}
var pipelineCommand = [][]string{
{
"cat",
"/home/wdd/IdeaProjects/ProjectOctopus/agent-go/executor/script/123",
},
{
"grep",
"passwd",
},
}
var pipelineCommandMore = "apt-cache madison docker-ce | awk {print$3} | cut -d: -f2"
var pipelineCommandSecond = "systemctl status -q docker.service | grep found."
var pipelineCommandThird = "ls /etc/ | grep passwd | head -2"
var pipelineCommandFourth = "echo dsadsad | tee /home/wdd/IdeaProjects/ProjectOctopus/agent-go/executor/script/123"
var ifconfigCommand = []string{"ifconfig"}
func TestReadTimeOutput(t *testing.T) {
ReadTimeCommandExecutor(ifconfigCommand)
}
func TestAllCommandExecutor(t *testing.T) {
ok, result := AllCommandExecutor(echoPathCommand)
t.Logf("执行结果为 => %#v", ok)
t.Logf("执行日志为 => %#v", result)
}
func TestPureResultSingleExecute(t *testing.T) {
PureResultSingleExecute(closeSELinux)
}
func TestPipelineCommandExecutorSingle(t *testing.T) {
cmd1 := exec.Command("ls", "/etc/")
cmd2 := exec.Command("grep", "passwd")
var outputBuf1 bytes.Buffer
cmd1.Stdout = &outputBuf1
if err := cmd1.Start(); err != nil {
fmt.Printf("Error: The first command can not be startup %s\n", err)
return
}
if err := cmd1.Wait(); err != nil {
fmt.Printf("Error: Couldn't wait for the first command: %s\n", err)
return
}
cmd2.Stdin = &outputBuf1
var outputBuf2 bytes.Buffer
cmd2.Stdout = &outputBuf2
if err := cmd2.Start(); err != nil {
fmt.Printf("Error: The second command can not be startup: %s\n", err)
return
}
if err := cmd2.Wait(); err != nil {
fmt.Printf("Error: Couldn't wait for the second command: %s\n", err)
return
}
s := outputBuf2.String()
split := strings.Split(s, "\n")
BasicPrettyPrint(true, split)
}
func TestPipelineCommandExecutor(t *testing.T) {
//PipelineCommandExecutor(pipelineCommand)
pipelineStringToCommand := BasicTransPipelineCommand(pipelineCommandSecond)
t.Logf("pipelineCommmand are => %#v", pipelineStringToCommand)
ok, resultLog := PipelineCommandExecutor(pipelineStringToCommand)
t.Logf("command execute ok is => %#v", ok)
t.Logf("command result are 下 \n")
BasicPrettyPrint(ok, resultLog)
}
func TestPipelineCommandExecutor2(t *testing.T) {
// Test case 1: Empty pipeline
pipelineCommand := [][]string{}
ok, log := PipelineCommandExecutor(pipelineCommand)
if !ok {
t.Errorf("Expected success, got failure")
}
if len(log) != 0 {
t.Errorf("Expected no log, got %v", log)
}
// Test case 2: Single command in pipeline
pipelineCommand = [][]string{
{"echo", "Hello, world!"},
}
ok, log = PipelineCommandExecutor(pipelineCommand)
if !ok {
t.Errorf("Expected success, got failure")
}
expectedLog := []string{"Hello, world!"}
if !reflect.DeepEqual(log, expectedLog) {
t.Errorf("Expected log %v, got %v", expectedLog, log)
}
// Test case 3: Multiple commands in pipeline
pipelineCommand = [][]string{
{"echo", "Hello"},
{"echo", "world!"},
}
ok, log = PipelineCommandExecutor(pipelineCommand)
if !ok {
t.Errorf("Expected success, got failure")
}
expectedLog = []string{"Hello", "world!"}
if !reflect.DeepEqual(log, expectedLog) {
t.Errorf("Expected log %v, got %v", expectedLog, log)
}
// Add more test cases as needed...
}
func TestSimple(t *testing.T) {
for i := 0; i < 10; i++ {
var output bytes.Buffer
fmt.Printf("output address is => %p \n", &output)
}
fmt.Println()
var output bytes.Buffer
fmt.Printf("out is => %#v\n", output)
command := exec.Command("ls")
command.StdoutPipe()
}
func TestFileBasedPipe(t *testing.T) {
reader, writer, err := os.Pipe()
if err != nil {
fmt.Printf("Error: Couldn't create the named pipe: %s\n", err)
}
go func() {
output := make([]byte, 100)
n, err := reader.Read(output)
if err != nil {
fmt.Printf("Error: Couldn't read data from the named pipe: %s\n", err)
}
fmt.Printf("Read %d byte(s). [file-based pipe]\n", n)
fmt.Printf("Read content are => %s\n", string(output))
}()
input := make([]byte, 26)
for i := 65; i <= 90; i++ {
input[i-65] = byte(i)
}
n, err := writer.Write(input)
if err != nil {
fmt.Printf("Error: Couldn't write data to the named pipe: %s\n", err)
}
fmt.Printf("Written %d byte(s). [file-based pipe]\n", n)
//time.Sleep(200 * time.Millisecond)
}

View File

@@ -0,0 +1,420 @@
package a_executor
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
"strconv"
"strings"
"github.com/mittwald/goharbor-client/v5/apiv2"
"github.com/mittwald/goharbor-client/v5/apiv2/model"
)
type HarborOperator struct {
SourceHarborHost string `json:"sourceHarborHost,omitempty"`
TargetHarborHost string `json:"targetHarborHost,omitempty"`
HarborPort string `json:"harborPort,omitempty"`
HarborAdminUser string `json:"harborAdminUser,omitempty"`
HarborAdminPass string `json:"harborAdminPass,omitempty"`
TargetHarborClient *apiv2.RESTClient
SourceHarborClient *apiv2.RESTClient
}
// NewHarborOperator 返回一个带有默认HarborAdminPass的HarborOperator实例
func NewHarborOperator() *HarborOperator {
return &HarborOperator{
HarborPort: "8033",
HarborAdminUser: "admin", // 设置默认值
HarborAdminPass: "V2ryStr@ngPss", // 设置默认值
}
}
// HarborOperatorCache 饿汉式单例
var HarborOperatorCache = NewHarborOperator()
var OctopusReplicationPolicyName = "octopus-sync-replication"
func (hOp *HarborOperator) Exec(baseFuncName string, funcArgs ...string) (bool, []string) {
// 参见 HarborFunctionEnum
resultOk := true
var resultLog []string
switch baseFuncName {
case "CREATE_PROJECT":
resultOk, resultLog = hOp.CreateProjectExec(funcArgs)
break
case "LIST_PROJECT":
resultOk, resultLog = hOp.ListProjectExec(funcArgs)
break
case "SYNC_PROJECT_BETWEEN_HARBOR":
resultOk, resultLog = hOp.SyncProjectExec(funcArgs)
break
case "SYNC_STATUS_HARBOR":
resultOk, resultLog = hOp.SyncStatusExec(funcArgs)
break
}
return resultOk, resultLog
}
func (hOp *HarborOperator) CreateProjectExec(funcArgs []string) (bool, []string) {
if hOp.TargetHarborClient == nil {
ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], "", true)
if !ok {
return false, []string{
"[Harbor Create Project] - Error !",
}
}
hOp.TargetHarborClient = createClient
}
client := hOp.TargetHarborClient
// create project
// 定义你想要创建的仓库(项目)的详细信息
log.Debug("[Harbor Create Project] - create project !")
needToCreateProjectNameList := []string{"cmii", "rancher"}
// 使用客户端创建项目
ctx := context.Background()
for _, projectName := range needToCreateProjectNameList {
log.DebugF("start to create proect => %s", projectName)
projectReq := &model.ProjectReq{
ProjectName: projectName, // 仓库名称
Metadata: &model.ProjectMetadata{
Public: "true", // 是否是公开的
},
}
exists, _ := client.ProjectExists(ctx, projectName)
if !exists {
err := client.NewProject(ctx, projectReq)
if err != nil {
errorLog := fmt.Sprintf("Error creating project %s: %s\n", projectName, err.Error())
return false, []string{errorLog}
}
}
log.DebugF("[Harbor Create Project] - Project %s already exists ! continue ", projectName)
}
successLog := "[Harbor CreateProjectExec] - Project Create Success !"
log.Info(successLog)
return true, []string{successLog}
}
func (hOp *HarborOperator) CheckAndBuildHarborClient(targetHarborHost string, targetHarborPort string, isTarget bool) (bool, *apiv2.RESTClient) {
log.InfoF("[Harbor Client Create] - start to create harbor client %s", targetHarborHost)
parseIP := net.ParseIP(targetHarborHost)
if parseIP == nil {
log.Error(
fmt.Sprintf("[Harbor Client Create] - ip format is wrong! parseIP is => %s ", parseIP),
)
return false, nil
}
if targetHarborPort == "" {
targetHarborPort = hOp.HarborPort
}
if isTarget {
hOp.TargetHarborHost = "http://" + targetHarborHost + ":" + targetHarborPort + "/api/"
log.DebugF("[Harbor Client Create] - harbor host is => %s", hOp.TargetHarborHost)
} else {
hOp.SourceHarborHost = "http://" + targetHarborHost + ":" + targetHarborPort + "/api/"
log.DebugF("[Harbor Client Create] - harbor host is => %s", hOp.SourceHarborHost)
}
// check connection
var client *apiv2.RESTClient
var err error
if isTarget {
client, err = apiv2.NewRESTClientForHost(hOp.TargetHarborHost, hOp.HarborAdminUser, hOp.HarborAdminPass, nil)
} else {
client, err = apiv2.NewRESTClientForHost(hOp.SourceHarborHost, hOp.HarborAdminUser, hOp.HarborAdminPass, nil)
}
if err != nil {
errorLog := fmt.Sprintf("Error creating REST client: %s\n", err.Error())
log.Error(errorLog)
return false, nil
}
return true, client
}
func (hOp *HarborOperator) ListProjectExec(funcArgs []string) (bool, []string) {
if hOp.TargetHarborClient == nil {
ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], "", true)
if !ok {
return false, []string{
"[Harbor Create Project ] - Error !",
}
}
hOp.TargetHarborClient = createClient
}
client := hOp.TargetHarborClient
// 使用客户端列出所有项目
ctx := context.Background()
projects, err := client.ListProjects(ctx, "")
if err != nil {
fmt.Printf("Error listing projects: %v\n", err)
os.Exit(1)
}
// 打印所有项目的信息
for _, project := range projects {
fmt.Printf("Project ID: %d, Name: %s, Public: %v\n", project.ProjectID, project.Name, project.Metadata.Public)
}
marshal, _ := json.Marshal(projects)
return true, []string{
fmt.Sprintf("List Projects of %s ", hOp.TargetHarborHost),
string(marshal),
}
}
func (hOp *HarborOperator) SyncProjectExec(funcArgs []string) (bool, []string) {
if hOp.TargetHarborClient == nil {
ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], "", true)
if !ok {
return false, []string{
"[Harbor Sync Project ] - Error !",
}
}
hOp.TargetHarborClient = createClient
}
targetClient := hOp.TargetHarborClient
if hOp.SourceHarborClient == nil {
realHost := funcArgs[1]
realPort := ""
if strings.Contains(funcArgs[1], ":") {
split := strings.Split(funcArgs[1], ":")
realHost = split[0]
realPort = split[1]
}
ok, createClient := hOp.CheckAndBuildHarborClient(realHost, realPort, false)
if !ok {
return false, []string{
"[Harbor Sync Project ] - Error !",
}
}
hOp.SourceHarborClient = createClient
}
needToSynchronizedProject := funcArgs[2]
log.InfoF("[Harbor Sync Project ] - start to sync harbor project => %s", needToSynchronizedProject)
log.DebugF("[Harbor Sync Project ] - start to check projects all exists!")
ctx := context.Background()
// check both source and target harbor project exists
needToCreateProjectNameList := []string{"rancher", "cmii"}
for _, projectName := range needToCreateProjectNameList {
syncNotExistHarborProjectError := []string{
"[Harbor Sync Project ] - project not exists !",
}
exists, _ := targetClient.ProjectExists(ctx, projectName)
if !exists {
return false, append(syncNotExistHarborProjectError, "targetClient")
}
}
OctopusSourceHarborName := "octopus-source"
// add source registry to destination harbor
log.InfoF("[Harbor Sync Project ] - start to create source harbor endpoints => %s", hOp.SourceHarborHost)
log.Debug("[Harbor Sync Project ] - start to delete exist replication policy !")
policies, _ := targetClient.ListReplicationPolicies(ctx)
if policies != nil {
for _, policy := range policies {
if policy.Name == OctopusReplicationPolicyName {
err := targetClient.DeleteReplicationPolicyByID(ctx, policy.ID)
if err != nil {
log.ErrorF("[Harbor Sync Project ] - delete exists replication policy failed ! => %v ", policy)
}
}
}
}
log.Debug("[Harbor Sync Project ] - start to delete exist exist harbor endpoints !")
exitRegistry, _ := targetClient.GetRegistryByName(ctx, OctopusSourceHarborName)
if exitRegistry != nil {
log.Debug("[Harbor Sync Project ] - source endpoints already exists ! delete it !")
err := targetClient.DeleteRegistryByID(ctx, exitRegistry.ID)
if err != nil {
log.ErrorF("[Harbor Sync Project ] - source endpoints delete failed ! => %v ", exitRegistry)
}
}
// todo cqga failed
octopusSourceRegistry := &model.Registry{
Credential: &model.RegistryCredential{
AccessKey: "admin",
AccessSecret: "V2ryStr@ngPss",
Type: "basic",
},
Insecure: true,
Name: OctopusSourceHarborName, // 源 Harbor 实例的注册表 ID通常为 0
Type: "harbor",
URL: strings.Split(hOp.SourceHarborHost, "/api")[0],
}
err := targetClient.NewRegistry(ctx, octopusSourceRegistry)
if err != nil {
sprintf := fmt.Sprintf("[SyncProjectExec] - source endpoints create failed ! => %s", err.Error())
log.Error(sprintf)
return false, []string{sprintf}
}
// get the real one for it's ID
realOctopusSourceRegistry, err := targetClient.GetRegistryByName(ctx, OctopusSourceHarborName)
if err != nil {
sprintf := fmt.Sprintf("[SyncProjectExec] - get target registry id failed ! => %s", err.Error())
log.Error(sprintf)
return false, []string{sprintf}
}
// 创建复制策略
octopusReplicationPolicy := &model.ReplicationPolicy{
CopyByChunk: nil,
Deletion: false,
Description: "",
DestNamespace: "", // 可以指定目标 Harbor 中的特定项目,如果为空,则使用源项目名称
DestNamespaceReplaceCount: nil,
SrcRegistry: &model.Registry{
ID: realOctopusSourceRegistry.ID,
},
DestRegistry: &model.Registry{
ID: 0,
},
Enabled: true,
Filters: []*model.ReplicationFilter{
{
Type: "name",
Value: needToSynchronizedProject + "/**",
},
},
ID: 0,
Name: OctopusReplicationPolicyName,
Override: true,
ReplicateDeletion: false,
Speed: nil,
Trigger: &model.ReplicationTrigger{
Type: "manual", // 可以是 "manual", "scheduled", 或 "event_based"
// 如果是 "scheduled",还需要设置 Cron 表达式
// TriggerSettings: &model.TriggerSettings{Cron: "0 * * * *"},
},
}
// 在源 Harbor 中创建复制策略
log.InfoF("[Harbor Sync Project ] - Start To Sync Project => %s !", needToSynchronizedProject)
err = targetClient.NewReplicationPolicy(ctx, octopusReplicationPolicy.DestRegistry, octopusReplicationPolicy.SrcRegistry, octopusReplicationPolicy.Deletion, octopusReplicationPolicy.Override, octopusReplicationPolicy.Enabled, octopusReplicationPolicy.Filters, octopusReplicationPolicy.Trigger, octopusReplicationPolicy.DestNamespace, octopusReplicationPolicy.Name, octopusReplicationPolicy.Name)
if err != nil {
syncErrorMessage := fmt.Sprintf("[Harbor Sync Project ] - Sync Project [ %s ] Failed ! Error is => %s\n", needToSynchronizedProject, err.Error())
log.Error(syncErrorMessage)
return false, []string{
syncErrorMessage,
}
}
realOctopusReplicationPolicy, err := targetClient.GetReplicationPolicyByName(ctx, OctopusReplicationPolicyName)
if err != nil {
return false, []string{
"[Harbor Sync Project ] - failed to get the realOctopusReplicationPolicy!",
err.Error(),
}
}
err = targetClient.TriggerReplicationExecution(ctx, &model.StartReplicationExecution{
PolicyID: realOctopusReplicationPolicy.ID,
})
if err != nil {
return false, []string{
"[ Harbor Sync Project ] - failed to start the harbor sync execution !",
err.Error(),
}
}
return true, []string{
fmt.Sprintf("[ Harbor Sync Project ] - sync project [ %s ] started !", needToSynchronizedProject),
}
}
func (hOp *HarborOperator) SyncStatusExec(funcArgs []string) (bool, []string) {
if hOp.TargetHarborClient == nil {
ok, createClient := hOp.CheckAndBuildHarborClient(funcArgs[0], "", true)
if !ok {
return false, []string{
"[ Sync Status ] - Error !",
}
}
hOp.TargetHarborClient = createClient
}
targetClient := hOp.TargetHarborClient
ctx := context.Background()
// check replication policy exists
replicationPolicy, err := targetClient.GetReplicationPolicyByName(ctx, OctopusReplicationPolicyName)
if err != nil {
return false, []string{
"[ Sync Status ] - get replication error !",
err.Error(),
}
}
// list execution status
replicationExecutions, err := targetClient.ListReplicationExecutions(ctx, &replicationPolicy.ID, nil, nil)
if err != nil {
return false, []string{
"[ Sync Status ] - replication has no sync work !",
err.Error(),
}
}
// find the newest one only have one here
for _, execution := range replicationExecutions {
if !strings.HasPrefix(execution.Status, "Succeed") {
bytes, _ := json.Marshal(execution)
log.InfoF("[sync status]- status are => %v", string(bytes))
// report status
return false, []string{
fmt.Sprintf("[sync status] - not complete ! progress is => %s %%",
strconv.FormatFloat(float64(execution.Succeed)/float64(execution.Total)*100, 'f', 2, 64)),
}
}
}
return true, []string{
"[sync status]- sync completed !",
}
}
func (hOp *HarborOperator) Command(baseFuncName string, funcArgs ...string) []string {
return nil
}

View File

@@ -0,0 +1,298 @@
package a_executor
import (
"strings"
"wdd.io/agent-common/image"
)
var LocalGzipImageFolderPrefix = "/var/lib/docker/image_sync/"
func (op *AgentOsOperator) Sync(baseFuncName string, funcArgs ...string) (bool, []string) {
resultOk := false
var errorLog []string
switch baseFuncName {
case "DOWNLOAD_DOCKER_IMAGE":
resultOk, errorLog = op.downloadDockerImage(funcArgs)
break
case "COMPRESS_IMAGE_TO_GZIP":
resultOk, errorLog = op.compressImageToGzip(funcArgs)
break
case "UPLOAD_GZIP_TO_OSS":
resultOk, errorLog = op.uploadGzipFileToOss(funcArgs)
break
case "DOWNLOAD_GZIP_IMAGE_FILE":
resultOk, errorLog = op.downloadGzipImageFile(funcArgs)
break
case "LOAD_DOCKER_IMAGE_FROM_GZIP":
resultOk, errorLog = op.loadDockerImageFromGzip(funcArgs)
break
case "PUSH_IMAGE_TO_TARGET_HARBOR":
resultOk, errorLog = op.pushImageToTargetHarbor(funcArgs)
break
case "UPDATE_IMAGE_TAG":
resultOk, errorLog = op.updateImageTag(funcArgs)
break
default:
resultOk, errorLog = op.okExec(funcArgs)
}
return resultOk, errorLog
}
func (op *AgentOsOperator) downloadDockerImage(funcArgs []string) (bool, []string) {
// funcArgs are imageFullName gzipFolderPrefix gzipFileName ossUrlPrefix namespace newImageTag
if !BasicCommandExistByPath("docker") {
return false, []string{
"docker not exits !",
}
}
imageFullName := funcArgs[0]
log.InfoF("[downloadDockerImage]- start to pull docker image %s", imageFullName)
// login
if strings.HasPrefix(imageFullName, image.CmiiHarborPrefix) {
HardCodeCommandExecutor("docker login -u rad02_drone -p Drone@1234 harbor.cdcyy.com.cn")
}
if !PureResultSingleExecute([]string{
"docker",
"pull",
imageFullName,
}) {
return false, []string{
"docker pull failed of " + imageFullName,
}
}
if !BasicDockerImageExistByFullName(funcArgs[0]) {
return false, []string{
"image not exits ! unknown error happened!",
}
}
return true, []string{
imageFullName,
}
}
func (op *AgentOsOperator) compressImageToGzip(funcArgs []string) (bool, []string) {
if !BasicCommandExistByPath("docker") {
return false, []string{
"docker not exits !",
}
}
if !BasicCommandExistByPath("gzip") {
return false, []string{
"gzip not exits !",
}
}
gzipFolderPrefix := funcArgs[1]
if !BasicFolderExists(gzipFolderPrefix) {
BasicCreateFolder(gzipFolderPrefix)
}
imageFullName := funcArgs[0]
if !BasicDockerImageExistByFullName(imageFullName) {
return false, []string{
"image not exits !",
}
}
if !strings.HasSuffix(gzipFolderPrefix, "/") {
gzipFolderPrefix += "/"
}
gzipImageFromFullName := image.ImageFullNameToGzipFileName(imageFullName)
dockerSaveCommand := "docker save " + imageFullName + " | gzip > " + gzipFolderPrefix + gzipImageFromFullName
executor, i := HardCodeCommandExecutor(dockerSaveCommand)
if !executor {
return false, i
}
if !BasicFileExistAndNotNull(gzipFolderPrefix + gzipImageFromFullName) {
return false, []string{
"gzip of ile error ",
}
}
return true, []string{
gzipImageFromFullName,
}
}
func (op *AgentOsOperator) uploadGzipFileToOss(funcArgs []string) (bool, []string) {
if !BasicCommandExistByPath("mc") {
return false, []string{
"mc not exits!",
}
}
gzipFolderPrefix := funcArgs[1]
gzipImageFromFullName := funcArgs[2]
ok, resultLog := HardCodeCommandExecutor("mc --insecure alias set demo https://oss.ig-demo.uavcmlc.com cmii B#923fC7mk")
//ok, resultLog = HardCodeCommandExecutor("mc alias list")
PureResultSingleExecute([]string{
"mc",
"rm",
"demo/cmlc-installation/tmp/" + gzipImageFromFullName,
})
ok, resultLog = AllCommandExecutor([]string{
"mc",
"cp",
gzipFolderPrefix + gzipImageFromFullName,
"demo/cmlc-installation/tmp/" + gzipImageFromFullName,
})
if !ok {
return false, resultLog
}
find, _ := BasicFindContentInCommandOutput("mc ls demo/cmlc-installation/tmp/", gzipImageFromFullName)
if !find {
return false, []string{
"demo oss can't find gzip file !",
}
}
return true, []string{
gzipImageFromFullName,
}
}
func (op *AgentOsOperator) downloadGzipImageFile(funcArgs []string) (bool, []string) {
ossUrlPrefix := funcArgs[3]
gzipImageFromFullName := funcArgs[2]
proxyUrl := funcArgs[4]
// create folder
BasicCreateFolder(LocalGzipImageFolderPrefix)
// remove file
desFile := LocalGzipImageFolderPrefix + gzipImageFromFullName
if !BasicRemoveFileOrFolder(desFile) {
return false, []string{
"file already exits ! can't remove it!",
}
}
var download bool
var downloadLog []string
if proxyUrl == "" {
download, downloadLog = BasicDownloadFile(ossUrlPrefix+gzipImageFromFullName, desFile)
} else {
download, downloadLog = BasicDownloadFileWithProxy(ossUrlPrefix+gzipImageFromFullName, proxyUrl, desFile)
}
if !download {
return false, downloadLog
}
return true, []string{
desFile,
}
}
func (op *AgentOsOperator) loadDockerImageFromGzip(funcArgs []string) (bool, []string) {
gzipImageFromFullName := funcArgs[2]
if !BasicFileExistAndNotNull(LocalGzipImageFolderPrefix + gzipImageFromFullName) {
return false, []string{
LocalGzipImageFolderPrefix + gzipImageFromFullName,
"local gzip file not exits!",
}
}
hardCodeCommand := "docker load < " + LocalGzipImageFolderPrefix + gzipImageFromFullName
executor, i := HardCodeCommandExecutor(hardCodeCommand)
if !executor {
return false, i
}
if !BasicDockerImageExistByFullName(funcArgs[0]) {
return false, []string{
"docker load from gzip file error ! image not exits!",
funcArgs[0],
}
}
return true, nil
}
func (op *AgentOsOperator) pushImageToTargetHarbor(funcArgs []string) (bool, []string) {
targetHarborHost := funcArgs[5]
imageFullName := funcArgs[0]
if !strings.Contains(targetHarborHost, "8033") {
targetHarborHost += ":8033"
}
targetImageFullName := image.ImageFullNameToTargetImageFullName(imageFullName, targetHarborHost)
if !PureResultSingleExecute([]string{
"docker",
"tag",
imageFullName,
targetImageFullName,
}) {
return false, []string{
"docker tag error!",
}
}
if strings.HasPrefix(targetImageFullName, image.CmiiHarborPrefix) {
HardCodeCommandExecutor("docker login -u rad02_drone -p Drone@1234 harbor.cdcyy.com.cn")
} else {
HardCodeCommandExecutor("docker login -u admin -p V2ryStr@ngPss " + targetHarborHost)
}
ok, resultLog := AllCommandExecutor([]string{
"docker",
"push",
targetImageFullName,
})
if !ok {
return false, resultLog
}
return true, []string{
targetImageFullName,
}
}
func (op *AgentOsOperator) updateImageTag(funcArgs []string) (bool, []string) {
namespace := funcArgs[6]
targetImageFullName := funcArgs[7]
if !BasicCommandExistByPath("kubectl") {
return false, []string{
"kubectl not exits !",
}
}
imageFullName := funcArgs[0]
if !strings.HasPrefix(imageFullName, image.CmiiHarborPrefix) {
return false, []string{
"cant update this image !",
}
}
appName := image.ImageFullNameToAppName(imageFullName)
updateCommand := "kubectl -n " + namespace + " patch deployment " + appName + "-p \"{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\": " + appName + ",\"image\": " + targetImageFullName + "}]}}}}"
executor, i := HardCodeCommandExecutor(updateCommand)
if !executor {
return false, i
}
return true, nil
}
//func BuildGzipImageFromFullName(imageFullName string) string {
//
//}

View File

@@ -0,0 +1,25 @@
package a_executor
import (
"fmt"
"strconv"
"testing"
"wdd.io/agent-common/utils"
)
func TestAgentOsOperator_Sync(t *testing.T) {
// imageFullName gzipFolderPrefix gzipFileName ossUrlPrefix proxyUrl targetHarborHost namespace targetImageFullName
funcArgs := []string{
"harbor.cdcyy.com.cn/cmii/busybox:0326",
"/var/lib/docker/octopus_image/",
"cmlc=cmii=busybox=0326.tar.gz",
"https://oss.demo.uavcmlc.com:18000/cmlc-installation/tmp/",
"",
"10.250.0.100",
"",
}
sync, strings := agentOP.Sync("PUSH_IMAGE_TO_TARGET_HARBOR", funcArgs...)
fmt.Println("func result are => " + strconv.FormatBool(sync))
utils.BeautifulPrint(strings)
}

View File

@@ -0,0 +1,197 @@
package a_executor
import (
"encoding/json"
"strconv"
"strings"
"wdd.io/agent-go/a_status"
)
func BuildAgentOsOperator(agentInfo *a_status.AgentInfo, ossOfflinePrefix string) *AgentOsOperator {
AgentOsOperatorCache = &AgentOsOperator{
InstallCommandPrefix: []string{
"apt-get", "install", "--allow-downgrades", "-y",
},
RemoveCommandPrefix: []string{"apt", "remove", "-y"},
CanAccessInternet: true,
IsOsTypeUbuntu: true,
IsOsTypeCentOS: false,
IsOsTypeEuler: false,
IsAgentInnerWall: false,
AgentArch: "amd64",
AgentOSReleaseCode: "focal",
AgentServerInfo: nil,
OssOfflinePrefix: ossOfflinePrefix,
}
// os type
detectByAgentStatusInfo(agentInfo, AgentOsOperatorCache)
// internet
detectByInternet(AgentOsOperatorCache)
return AgentOsOperatorCache
}
func detectByAgentStatusInfo(agentInfo *a_status.AgentInfo, os *AgentOsOperator) {
if agentInfo == nil {
log.WarnF("[detectByAgentStatusInfo] - agentInfo from status module is nil, roll back to traditional way!")
// detectByOsType()
}
bytes, _ := json.Marshal(agentInfo)
log.DebugF("[detectByAgentStatusInfo] - agent info is => %s", string(bytes))
if strings.Contains(agentInfo.HostInfo.Platform, "openeuler") || strings.Contains(agentInfo.HostInfo.PlatformFamily, "rehl") {
// centos
os.IsOsTypeUbuntu = false
os.IsOsTypeCentOS = true
if strings.Contains(agentInfo.HostInfo.Platform, "openeuler") {
os.IsOsTypeEuler = true
}
os.InstallCommandPrefix = []string{
"yum", "install", "-y",
}
os.RemoveCommandPrefix = []string{
"yum", "remove", "-y",
}
} else if strings.Contains(agentInfo.HostInfo.PlatformFamily, "debian") {
// ubuntu
os.IsOsTypeUbuntu = true
os.IsOsTypeCentOS = false
os.IsOsTypeEuler = false
os.RemoveCommandPrefix = []string{"apt", "remove", "-y"}
os.InstallCommandPrefix = []string{
"apt-get", "install", "--allow-downgrades", "-y",
}
// os release code
os.AgentOSReleaseCode = judgeUbuntuReleaseFromOsVersion(agentInfo.HostInfo.PlatformVersion)
}
// agent cpu arch
os.AgentArch = judgeAgentCpuArchByKernelArch(agentInfo.HostInfo.KernelArch)
}
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"
}
}
func judgeAgentCpuArchByKernelArch(kernelArch string) string {
switch kernelArch {
case "x86_64":
return "amd64"
case "aarch64":
return "arm64"
case "armv8":
return "arm64"
case "armv6":
return "arm6"
case "armv7":
return "arm7"
case "i32":
return "386"
case "i86":
return "386"
default:
return "cpu-unknown"
}
}
func detectByInternet(os *AgentOsOperator) {
outsideTestUrl := "www.google.com"
innerTestUrl := "www.baidu.com"
testInternetCommand := []string{
"curl",
"-o",
"/dev/null",
"-m",
"5",
"-s",
}
if PureResultSingleExecute(append(testInternetCommand, outsideTestUrl)) {
os.CanAccessInternet = true
os.IsAgentInnerWall = false
} else if PureResultSingleExecute(append(testInternetCommand, innerTestUrl)) {
os.CanAccessInternet = true
os.IsAgentInnerWall = true
} else {
os.CanAccessInternet = false
os.IsAgentInnerWall = true
}
log.InfoF("[Agent Network Status] - Can Access Internet => %s Inner CN => %s", strconv.FormatBool(os.CanAccessInternet), strconv.FormatBool(os.IsAgentInnerWall))
}
func detectByOsType(os *AgentOsOperator, osInfo string) {
ubuntuOsReleaseCode := [][]string{
{
"cat",
"/etc/os-release",
},
{
"grep",
"CODE",
},
{
"head",
"-1",
},
{
"cut",
"-d",
"=",
"-f",
"2",
},
}
if strings.HasPrefix(osInfo, "Ce") {
// centos
os.IsOsTypeUbuntu = false
os.InstallCommandPrefix = []string{
"yum", "install", "-y",
}
os.RemoveCommandPrefix = []string{
"yum", "remove",
}
} else {
// ubuntu
os.IsOsTypeUbuntu = true
os.RemoveCommandPrefix = []string{"apt", "remove", "-y"}
os.InstallCommandPrefix = []string{
"apt-get", "install", "--allow-downgrades", "-y",
}
// os release code
ok, resultLog := PipelineCommandExecutor(ubuntuOsReleaseCode)
if ok {
os.AgentOSReleaseCode = resultLog[0]
} else {
os.AgentOSReleaseCode = "UNKNOWN"
}
}
}

View File

@@ -0,0 +1,365 @@
package a_executor
import (
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
"os"
"strings"
"sync"
"time"
"context"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
var k8sConfigFilePath = "/root/wdd/kube_config_cluster.yml"
var k8sClient = newK8sClientInstance()
func newK8sClientInstance() *kubernetes.Clientset {
once := sync.Once{}
if !BasicFileExistAndNotNull(k8sConfigFilePath) {
log.WarnF("[newK8sClientInstance] - k8s config %s does not exist ! ", k8sConfigFilePath)
return nil
}
var client *kubernetes.Clientset
once.Do(func() {
// 使用kubeconfig文件初始化客户端
config, err := clientcmd.BuildConfigFromFlags("", k8sConfigFilePath)
if err != nil {
log.ErrorF("[newK8sClientInstance] - load from %s error !", k8sConfigFilePath)
}
client, err = kubernetes.NewForConfig(config)
if err != nil {
log.Error("[newK8sClientInstance] - create k8s client error !")
}
})
return client
}
func K8sCheckPodStatusTimeout(specificPod string, supreme string, waitTimeOut int) bool {
if k8sClient == nil {
// this should be the first call of k8s function
k8sClient = newK8sClientInstance()
if k8sClient == nil {
log.ErrorF("k8s client is nil, run k8s function error !")
return false
}
}
// 设置超时时间和时间间隔
timeout := time.After(time.Duration(waitTimeOut) * time.Second)
tick := time.Tick(5 * time.Second)
// 监控Pod状态
for {
select {
case <-timeout:
log.ErrorF("[K8sCheckPodStatusTimeout] - 命名空间: [%s], Pod名称: [%s], 状态: 失败!", supreme, specificPod)
return false
case <-tick:
pod, err := k8sClient.CoreV1().Pods(supreme).Get(context.TODO(), specificPod, metav1.GetOptions{})
if err != nil {
log.ErrorF("[K8sCheckPodStatusTimeout] - 命名空间: [%s], Pod名称: [%s], 获取Pod信息失败 ", supreme, err.Error())
} else {
log.DebugF("[K8sCheckPodStatusTimeout] - 命名空间: [%s], Pod名称: [%s], 状态: [%s]", supreme, pod.Name, pod.Status.Phase)
if pod.Status.Phase == corev1.PodRunning || pod.Status.Phase == corev1.PodSucceeded {
return true
}
}
}
}
}
func K8sCheckDeploymentStatusTimeout(specificDeployment string, supreme string, waitTimeOut int) bool {
if k8sClient == nil {
// this should be the first call of k8s function
k8sClient = newK8sClientInstance()
if k8sClient == nil {
log.ErrorF("k8s client is nil, run k8s function error !")
return false
}
}
// 设置超时时间和时间间隔
timeout := time.After(time.Duration(waitTimeOut) * time.Second)
tick := time.Tick(5 * time.Second)
// 监控Pod状态
for {
select {
case <-timeout:
log.ErrorF("[K8sCheckDeploymentStatusTimeout] - 命名空间: %s, Deployment名称: %s, 状态: 失败!\n", supreme, specificDeployment)
return false
case <-tick:
deployment, err := k8sClient.AppsV1().Deployments(supreme).Get(context.TODO(), specificDeployment, metav1.GetOptions{})
if err != nil {
log.ErrorF("[K8sCheckDeploymentStatusTimeout] - 命名空间: [%s], Deployment 名称: [%s], 获取Deployment信息失败 ", supreme, err.Error())
} else {
log.DebugF("[K8sCheckDeploymentStatusTimeout] - 命名空间: [ %s ], Deployment: [ %s ] 还有Pods未处于Running状态 (Ready: %d, Total: %d)\n", supreme, deployment.Name, deployment.Status.ReadyReplicas, deployment.Status.Replicas)
if deployment.Status.ReadyReplicas == deployment.Status.Replicas {
return true
}
}
}
}
}
func K8sListPVCInNamespace(supreme string) (bool, []string) {
if k8sClient == nil {
// this should be the first call of k8s function
k8sClient = newK8sClientInstance()
if k8sClient == nil {
log.ErrorF("k8s client is nil, run k8s function error !")
return false, []string{
"[K8sListPVCInNamespace] - k8s client not exits !",
}
}
}
pvcs, err := k8sClient.CoreV1().PersistentVolumeClaims(supreme).List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.ErrorF("[K8sListPVCInNamespace] - error list pvc list in namespace %s", supreme)
return false, nil
}
var pvcList []string
for _, pvc := range pvcs.Items {
pvcList = append(pvcList, pvc.Name)
}
log.DebugF("K8sListPVCInNamespace - all pvc in namespace of [ %s ] are %v", supreme, pvcList)
return true, pvcList
}
func K8sCheckPVCStatusTimeOut(specificPvcName string, supreme string, waitTimeOut int) bool {
if k8sClient == nil {
// this should be the first call of k8s function
k8sClient = newK8sClientInstance()
if k8sClient == nil {
log.ErrorF("k8s client is nil, run k8s function error !")
return false
}
}
// 设置超时时间和时间间隔
timeout := time.After(time.Duration(waitTimeOut) * time.Second)
tick := time.Tick(5 * time.Second)
// 监控Pod状态
for {
select {
case <-timeout:
log.ErrorF("[K8sCheckPVCStatusTimeOut] - 命名空间: %s, PVC 名称: %s, 状态: 失败! ", supreme, specificPvcName)
return false
case <-tick:
pvc, err := k8sClient.CoreV1().PersistentVolumeClaims(supreme).Get(context.TODO(), specificPvcName, metav1.GetOptions{})
if err != nil {
log.ErrorF("[K8sCheckPVCStatusTimeOut] - 命名空间: [ %s ], 获取 PVC [%s] 信息失败: %s ", supreme, specificPvcName, err.Error())
}
if pvc.Status.Phase == corev1.ClaimBound {
log.DebugF("[K8sCheckPVCStatusTimeOut] - PVC %s in namespace %s is running", specificPvcName, supreme)
return true
} else {
log.WarnF("[K8sCheckPVCStatusTimeOut] - PVC %s in namespace %s run failed !", specificPvcName, supreme)
return false
}
}
}
}
func KubectlCheckPodStatus(specific string, supreme string) bool {
if !BasicCommandExists("kubectl") {
log.Error("kubectl命令不存在无法查看Pod状态请查看")
}
ok, resultLog := AllCommandExecutor([]string{
"kubectl", "-n", supreme, "get", "pod", specific, "-o", "jsonpath='{.status.phase}'",
})
if !ok {
return false
}
for _, resultLine := range resultLog {
if strings.HasPrefix(resultLine, "Running") {
return true
}
}
return false
}
func KubectlApplyExec(resourcesYamlFile string) (bool, []string) {
// check kubectl
if !BasicCommandExistByPath("kubectl") {
return false, []string{
"[KubectlApplyExec] - kubectl command not exist !",
}
}
// check resourcesYamlFile
if !BasicFileExistAndNotNull(resourcesYamlFile) {
return false, []string{
fmt.Sprintf("[KubectlApplyExec] - wrong resourcesYamlFile %s not exist !", resourcesYamlFile),
}
}
// apply -f
ok, resultLog := AllCommandExecutor([]string{
"/usr/local/bin/kubectl",
"apply",
"-f",
resourcesYamlFile,
})
if !ok {
return false, resultLog
}
return true, append(resultLog,
fmt.Sprintf("[KubectlApplyExec] - %s apply success!", resourcesYamlFile))
}
func KubectlDeleteExec(resourcesYamlFile string) (bool, []string) {
// check kubectl
if !BasicCommandExistByPath("kubectl") {
return false, []string{
"[KubectlDeleteExec] - kubectl command not exist !",
}
}
// check resourcesYamlFile
if !BasicFileExistAndNotNull(resourcesYamlFile) {
return false, []string{
fmt.Sprintf("[KubectlDeleteExec] - wrong resourcesYamlFile %s not exist !", resourcesYamlFile),
}
}
// apply -f
ok, resultLog := AllCommandExecutor([]string{
"/usr/local/bin/kubectl",
"delete",
"-f",
resourcesYamlFile,
})
if !ok {
return false, resultLog
}
return true, append(resultLog,
fmt.Sprintf("[KubectlDeleteExec] - %s delete success!", resourcesYamlFile))
}
func K8sCreateNamespace(namespaceName string) bool {
if k8sClient == nil {
// this should be the first call of k8s function
k8sClient = newK8sClientInstance()
if k8sClient == nil {
log.ErrorF("k8s client is nil, run k8s function error !")
return false
}
}
namespace, err := k8sClient.CoreV1().Namespaces().Get(context.TODO(), namespaceName, metav1.GetOptions{})
if err == nil {
log.InfoF("[K8sCreateNamespace] - namespace of [%s] already exists!", namespaceName)
return true
}
// create namespace
// 创建命名空间对象
namespace = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
}
// 使用客户端创建命名空间
namespace, err = k8sClient.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
if err != nil {
log.ErrorF("Error getting namespace: %s ", err.Error())
return false
}
// check namespace exists
// 尝试获取名为 "xxg" 的命名空间
namespace, err = k8sClient.CoreV1().Namespaces().Get(context.TODO(), namespaceName, metav1.GetOptions{})
// 如果返回错误,需要判断是因为命名空间不存在还是其他错误
if err != nil {
if errors.IsNotFound(err) {
log.ErrorF("Namespace %s cant be got !", namespaceName)
return false
} else {
log.ErrorF("Error retrieving namespace: %s\n", err.Error())
}
return false
}
log.DebugF("Namespace %s create successful !", namespaceName)
return true
}
func K8sGetDashBoardAuthKey() bool {
if k8sClient == nil {
// this should be the first call of k8s function
k8sClient = newK8sClientInstance()
if k8sClient == nil {
log.ErrorF("k8s client is nil, run k8s function error !")
return false
}
}
// 获取 kube-system 命名空间的 secrets 列表
secrets, err := k8sClient.CoreV1().Secrets("kube-system").List(context.TODO(), metav1.ListOptions{})
if err != nil {
fmt.Printf("Error retrieving secrets from kube-system namespace: %s\n", err.Error())
os.Exit(1)
}
// 过滤出名为 admin-user 的 secret
var adminUserSecretName string
for _, secret := range secrets.Items {
if strings.Contains(secret.Name, "admin-user") {
adminUserSecretName = secret.Name
break
}
}
if adminUserSecretName == "" {
fmt.Println("No admin-user secret found")
os.Exit(1)
}
// 获取并打印特定的 secret 描述信息
secret, err := k8sClient.CoreV1().Secrets("kube-system").Get(context.TODO(), adminUserSecretName, metav1.GetOptions{})
if err != nil {
fmt.Printf("Error retrieving secret %s: %s\n", adminUserSecretName, err.Error())
os.Exit(1)
}
// 打印 secret 的详细信息,根据需要格式化输出
fmt.Printf("Name: %s\nNamespace: %s\nData:\n", secret.Name, secret.Namespace)
for key, value := range secret.Data {
fmt.Printf("%s: %s\n", key, value)
}
return false
}

View File

@@ -0,0 +1,183 @@
package a_executor
//import (
// _ "github.com/go-sql-driver/mysql"
//)
//func MysqlSqlFileLoad(jackeyLoveIp string, jackeyLoveFileList []string) (bool, []string) {
//
// dsn := "root:QzfXQhd3bQ@tcp(" + jackeyLoveIp + ":33306)/"
// db, err := sql.Open("mysql", dsn)
// if err != nil {
// errConnect := fmt.Sprintf("[MysqlSqlFileLoad]- mysql connection error ! please check ! => %s error is %s ", dsn, err.Error())
// log.Error(errConnect)
// return false, []string{
// errConnect,
// }
// }
// defer db.Close()
//
// // 确保数据库连接是有效的
// if err := db.Ping(); err != nil {
// errConnect := "[MysqlSqlFileLoad]- mysql ping error ! please check !"
// log.Error(errConnect)
// return false, []string{
// errConnect,
// }
// }
//
// for _, jackeyLoveFile := range jackeyLoveFileList {
//
// // 打开 SQL 文件
// jackeyLove, err := os.Open(jackeyLoveFile)
// if err != nil {
// log.ErrorF("[MysqlSqlFileLoad] - failed to load jackeyLoveFile => %s", jackeyLove.Name())
// }
// defer jackeyLove.Close()
//
// // 逐行读取 SQL 文件并执行
// query := ""
// delimiterSwitched := false
// scanner := bufio.NewScanner(jackeyLove)
// for scanner.Scan() {
// sqlStatement := scanner.Text()
//
// if strings.HasPrefix(strings.TrimSpace(sqlStatement), "DELIMITER") {
// delimiterSwitched = true
// continue
// }
// if delimiterSwitched {
// if strings.TrimSpace(sqlStatement) == "" || strings.TrimSpace(sqlStatement) == ";" {
// delimiterSwitched = false
// // 替换自定义分隔符为 ;
// sqlStatement = strings.ReplaceAll(sqlStatement, ";;", ";")
// } else {
// // 忽略自定义分隔符行
// continue
// }
// }
//
// // 这里可以添加逻辑来忽略空行或注释行
// if sqlStatement == "" || sqlStatement[:2] == "--" || sqlStatement[:2] == "/*" {
// continue
// }
//
// query += sqlStatement
//
// if strings.HasSuffix(strings.TrimSpace(sqlStatement), ";") {
//
// fmt.Printf("%s -> %s", jackeyLoveFile, query)
//
// _, err := db.Exec(query)
// if err != nil {
// executeError := fmt.Sprintf("[MysqlSqlFileLoad] - jackeyLoveFile %s 执行出错: %s, 错误信息: %s", jackeyLove.Name(), sqlStatement, err.Error())
// log.Error(executeError)
// return false, []string{
// executeError,
// }
// }
// query = ""
// } else {
// query += " " // 添加空格以便连接多行语句
// }
// }
//
// // 检查扫描过程中是否有错误
// if err := scanner.Err(); err != nil {
// executeError := fmt.Sprintf("[MysqlSqlFileLoad] - jackeyLoveFile %s 文件加载错误! 错误信息: %s", jackeyLove.Name(), err.Error())
// log.Error(executeError)
// return false, []string{
// executeError,
// }
// }
// log.DebugF("[MysqlSqlFileLoad] - jackeyLoveFile %s execute success !", jackeyLove.Name())
// }
//
// return true, append(jackeyLoveFileList, "[MysqlSqlFileLoad] all file loaded !")
//}
//func MySqlParseSQLFile(sqlFilePath string) []string {
//
// var result []string
//
// // 打开 SQL 文件
// jackeyLove, err := os.Open(sqlFilePath)
// if err != nil {
// log.ErrorF("[MySqlParseSQLFile] - failed to load jackeyLoveFile => %s", jackeyLove.Name())
// }
// defer jackeyLove.Close()
//
// // 逐行读取 SQL 文件并执行
// query := ""
// delimiterSwitched := false
// scanner := bufio.NewScanner(jackeyLove)
// for scanner.Scan() {
// sqlStatement := scanner.Text()
//
// if strings.HasPrefix(strings.TrimSpace(sqlStatement), "DELIMITER") {
// delimiterSwitched = true
// continue
// }
// if delimiterSwitched {
// if strings.TrimSpace(sqlStatement) == "" || strings.TrimSpace(sqlStatement) == ";" {
// delimiterSwitched = false
// // 替换自定义分隔符为 ;
// sqlStatement = strings.ReplaceAll(sqlStatement, ";;", ";")
// } else {
// // 忽略自定义分隔符行
// continue
// }
// }
//
// // 这里可以添加逻辑来忽略空行或注释行
// if sqlStatement == "" || sqlStatement[:2] == "--" || sqlStatement[:2] == "/*" {
// continue
// }
//
// query += sqlStatement
//
// if strings.HasSuffix(strings.TrimSpace(sqlStatement), ";") {
//
// result = append(result, query)
//
// query = ""
// } else {
// query += "" // 添加空格以便连接多行语句
// }
// }
//
// // 检查扫描过程中是否有错误
// if err := scanner.Err(); err != nil {
// executeError := fmt.Sprintf("[MysqlSqlFileLoad] - jackeyLoveFile %s 文件加载错误! 错误信息: %s", jackeyLove.Name(), err.Error())
// log.Error(executeError)
//
//
// }
//
// return result
//}
//
//func MySqlConnection(jackeyLoveIp string, jackeyLovePort string) (bool, []string) {
//
// dsn := "root:QzfXQhd3bQ@tcp(" + jackeyLoveIp + ":33306)/"
// db, err := sql.Open("mysql", dsn)
// if err != nil {
// errConnect := fmt.Sprintf("[MysqlSqlFileLoad]- mysql connection error ! please check ! => %s error is %s ", dsn, err.Error())
// log.Error(errConnect)
// return false, []string{
// errConnect,
// }
// }
// defer db.Close()
//
// // 确保数据库连接是有效的
// if err := db.Ping(); err != nil {
// errConnect := "[MysqlSqlFileLoad]- mysql ping error ! please check !"
// log.Error(errConnect)
// return false, []string{
// errConnect,
// }
// }
//
// return true, nil
//}

View File

@@ -0,0 +1,18 @@
package a_executor
//func TestMySqlConnection(t *testing.T) {
//
// connection, strings := MySqlConnection("10.250.0.126", "33306")
// if !connection {
// t.Error(strings)
// }
//
// println("success!")
//}
//
//func TestMySqlParseSQLFile(t *testing.T) {
//
// parseSQLFile := MySqlParseSQLFile("C:\\Users\\wdd\\Documents\\4.1.6-部署\\init_5.1.0\\1_all_tables_demo_5.1.0.sql")
//
// BasicPrettyPrint(true, parseSQLFile)
//}

View File

@@ -0,0 +1,53 @@
package a_executor
//import (
// "fmt"
// "os/exec"
// "strings"
// "syscall"
// "unsafe"
//)
//
//var (
// user32DLL = syscall.NewLazyDLL("user32.dll")
//
// messageBox = user32DLL.NewProc("MessageBoxW")
//)
//
//func FindPublicIpAddress() {
//
// cmd := exec.Command("curl", "ifconfig.io")
// output, err := cmd.Output()
// if err != nil {
// fmt.Println("无法获取公网IP地址", err)
// return
// }
//
// ip := strings.TrimSpace(string(output))
// fmt.Println("公网IP地址:", ip)
//
// fmt.Println("")
//}
//
//func CallAgent() {
// var (
// hWnd uintptr
// text = "Hello, World!"
// caption = "Golang Win32API"
// flags = uint(0x00000001) // MB_OK
// )
//
// ret, _, err := messageBox.Call(
// hWnd,
// uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
// uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
// uintptr(flags),
// )
// if ret == 0 {
// fmt.Println("MessageBox failed:", err)
// return
// }
//
// fmt.Println("MessageBox returned:", ret)
//
//}

View File

@@ -0,0 +1,8 @@
package a_executor
//import "testing"
//
//func TestCallAgent(t *testing.T) {
//
// FindPublicIpAddress()
//}

View File

@@ -0,0 +1,25 @@
______ __ ______ __
/ \ | \ / \ | \
| $$$$$$\ _______ _| $$_ ______ ______ __ __ _______ | $$$$$$\ ______ ______ _______ _| $$_
| $$ | $$/ | $$ \ / \ / \| \ | \/ \ | $$__| $$/ \ / \| | $$ \
| $$ | $| $$$$$$$\$$$$$$ | $$$$$$| $$$$$$| $$ | $| $$$$$$$ | $$ $| $$$$$$| $$$$$$| $$$$$$$\$$$$$$
| $$ | $| $$ | $$ __| $$ | $| $$ | $| $$ | $$\$$ \ | $$$$$$$| $$ | $| $$ $| $$ | $$| $$ __
| $$__/ $| $$_____ | $$| | $$__/ $| $$__/ $| $$__/ $$_\$$$$$$\ | $$ | $| $$__| $| $$$$$$$| $$ | $$| $$| \
\$$ $$\$$ \ \$$ $$\$$ $| $$ $$\$$ $| $$ | $$ | $$\$$ $$\$$ | $$ | $$ \$$ $$
\$$$$$$ \$$$$$$$ \$$$$ \$$$$$$| $$$$$$$ \$$$$$$ \$$$$$$$ \$$ \$$_\$$$$$$$ \$$$$$$$\$$ \$$ \$$$$
| $$ | \__| $$
| $$ \$$ $$
\$$ \$$$$$$
__ __
| \ | \
____| $$ ______ ____| $$ ______ __ __ __ ______ _______ ______
______ ______ ______ / $$| \ / $$| \ | \ | \ | \| \| \ / \
| | | \ | $$$$$$$ \$$$$$$| $$$$$$$ \$$$$$$\ | $$ | $$ | $$ \$$$$$$| $$$$$$$| $$$$$$\
\$$$$$$\$$$$$$\$$$$$$ | $$ | $$/ $| $$ | $$/ $$ | $$ | $$ | $$/ $| $$ | $| $$ | $$
| $$__| $| $$$$$$| $$__| $| $$$$$$$ | $$_/ $$_/ $| $$$$$$| $$ | $| $$__| $$
\$$ $$\$$ $$\$$ $$\$$ $$ \$$ $$ $$\$$ $| $$ | $$\$$ $$
\$$$$$$$ \$$$$$$$ \$$$$$$$ \$$$$$$$ \$$$$$\$$$$ \$$$$$$$\$$ \$$_\$$$$$$$
| \__| $$
\$$ $$
\$$$$$$

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
#!/bin/bash
rm -rf go1.21.6.linux-amd64.tar.gz
wget http://10.250.0.100:9000/octopus/go1.21.6.linux-amd64.tar.gz -O go1.21.6.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.6.linux-amd64.tar.gz
sed -i "$ a export PATH=\$PATH:/usr/local/go/bin" /root/.bashrc
source /root/.bashrc
go version
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

View File

@@ -0,0 +1,2 @@
systemctl stop firewalld
systemctl disable firewalld