devops/tool/main.go
2024-12-31 18:12:26 +08:00

410 lines
10 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"gopkg.in/ini.v1"
)
// GOOS=linux GOARCH=amd64 go build -o /data/devops/Goleaf/tool/tool main.go
var help = `
Usage: app.ini [options]
start start the server
stop stop the server
restart restart the server
status get the server status
`
var cfg *ini.File
var FuncMap map[string]func() error
var dirPath string
var app_path string
func main() {
FuncMap = make(map[string]func() error)
// 检查是否提供了命令行参数
if len(os.Args) < 2 {
log.Fatal(help)
}
var err error
// 获取当前文件的绝对路径
execPath, err := os.Executable()
if err != nil {
log.Fatal(err)
}
absPath, err := filepath.Abs(execPath)
if err != nil {
log.Fatal(err)
}
// 获取当前文件的绝对路径的文件夹路径
dirPath = filepath.Dir(absPath)
// 加载 app.ini 文件
cfg, err = ini.Load(dirPath + "/app.ini")
if err != nil {
log.Fatal(err)
}
app_path = cfg.Section("app").Key("app_path").String()
register("start", start)
register("stop", stop)
register("install", install)
register("status", status)
register("restart", restart)
funcName := os.Args[1]
if f, ok := FuncMap[funcName]; ok {
e := f()
if e != nil {
log.Fatal(e)
}
} else {
log.Fatal(help)
}
}
func register(name string, f func() error) {
FuncMap[name] = f
}
func install() error {
fmt.Println("install")
if len(os.Args) < 4 {
log.Fatal("请输入要安装的服务类型和区号 install [center|node] 区号")
}
serverType := os.Args[2]
zone := os.Args[3]
if serverType != "center" && serverType != "node" {
log.Fatal("请输入正确的服务类型 center|node")
}
AppName := cfg.Section("app").Key("app_name").String()
// 生成服务名
serviceName := fmt.Sprintf("%s_%s_%s", AppName, serverType, zone)
// 判断文件夹是否存在
folderPath := fmt.Sprintf("%s/zone/%s", app_path, serviceName)
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
err := os.MkdirAll(folderPath, os.ModePerm)
if err != nil {
log.Fatal(err)
}
fmt.Printf("文件夹 %s 创建成功\n", folderPath)
} else {
fmt.Printf("文件夹 %s 已存在\n", folderPath)
}
// 生成配置文件
configPath := fmt.Sprintf("%s/server.json", folderPath)
file, err := os.Create(configPath)
if err != nil {
log.Fatal(err)
}
defer file.Close()
zoneId, _ := strconv.Atoi(zone)
cf, confs := createConfigFile(zoneId, serverType)
file.WriteString(confs)
// 读取 SQL 文件内容并替换 %database% 字符串
sqlFilePath := fmt.Sprintf("%s/tool/Merge_Pet.sql", app_path)
sqlContent, err := os.ReadFile(sqlFilePath)
if err != nil {
log.Fatal(err)
}
dbName := cf["DbName"].(string)
modifiedSQL := strings.ReplaceAll(string(sqlContent), "%database%", dbName)
// 将修改后的内容写入临时文件
tempSQLFile, err := os.CreateTemp("", "modified_*.sql")
if err != nil {
log.Fatal(err)
}
// defer os.Remove(tempSQLFile.Name())
_, err = tempSQLFile.WriteString(modifiedSQL)
if err != nil {
log.Fatal(err)
}
tempSQLFile.Close()
// 创建数据库
dbUser := cfg.Section("mysql").Key("mysql_user").String()
dbPassword := cfg.Section("mysql").Key("mysql_password").String()
dbHost := cfg.Section("mysql").Key("mysql_host").String()
dbPort := cfg.Section("mysql").Key("mysql_port").String()
fmt.Println("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, "-e", fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", dbName))
createDBCmd := exec.Command("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, "-e", fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", dbName))
createDBCmd.Stdout = os.Stdout
createDBCmd.Stderr = os.Stderr
err = createDBCmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Println("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, dbName, "-e", fmt.Sprintf("source %s", tempSQLFile.Name()))
// 执行修改后的 SQL 文件
cmd := exec.Command("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, dbName, "-e", fmt.Sprintf("source %s", tempSQLFile.Name()))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
cmd = exec.Command("mkdir", cf["LogPath"].(string))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("SQL 文件 %s 执行成功\n", sqlFilePath)
fmt.Printf("配置文件 %s 创建成功\n", configPath)
return nil
}
func createConfigFile(Id int, Type string) (map[string]interface{}, string) {
conf := make(map[string]interface{})
conf["LogLevel"] = cfg.Section("log").Key("log_level").String()
TcpStartAddr, _ := cfg.Section("server").Key("tcp_addr").Int()
conf["TcpAddr"] = fmt.Sprintf(":%d", TcpStartAddr+Id)
WsStartAddr, _ := cfg.Section("server").Key("ws_addr").Int()
conf["WsAddr"] = fmt.Sprintf(":%d", WsStartAddr+Id)
ListenAddr, _ := cfg.Section("server").Key("listen_addr").Int()
conf["MySqlAddr"] = cfg.Section("mysql").Key("mysql_host").String()
conf["MySqlUsr"] = cfg.Section("mysql").Key("mysql_user").String()
conf["MySqlPwd"] = cfg.Section("mysql").Key("mysql_password").String()
conf["MySqlPort"] = cfg.Section("mysql").Key("mysql_port").String()
MaxConnNum, _ := cfg.Section("server").Key("max_conn").Int()
conf["MaxConnNum"] = MaxConnNum
app_name := cfg.Section("app").Key("app_name").String()
conf["LogPath"] = fmt.Sprintf("%s/zone/%s_%s_%d/log/", app_path, app_name, Type, Id)
conf["DbName"] = fmt.Sprintf("%s_%d", app_name, Id)
conf["TELOGDIR"] = cfg.Section("log").Key("te_log_path").String()
conf["GameName"] = app_name
AppId, _ := cfg.Section("app").Key("app_id").Int()
conf["AppID"] = AppId
conf["ServerType"] = Type
conf["ServerID"] = Id
conf["ServerOpenTime"] = "2028-01-01 00:00:00"
conf["ServerName"] = fmt.Sprintf("%s_%d", app_name, Id)
conf["ServerStatus"] = 1
CenterId, _ := cfg.Section("app").Key("center_id").Int()
conf["ServerCenter"] = CenterId
conf["RedisAddr"] = cfg.Section("redis").Key("redis_host").String()
conf["RedisPort"] = cfg.Section("redis").Key("redis_port").String()
conf["GameConfPath"] = app_path + "/config/"
conf["RemoteAddr"] = fmt.Sprintf("%s:%d", cfg.Section("server").Key("remote").String(), ListenAddr+Id) // 服务器地址
conf["ListenAddr"] = fmt.Sprintf(":%d", ListenAddr+Id)
conf["CenterAddr"] = fmt.Sprintf("%s:%s", cfg.Section("cluster").Key("center_host").String(), cfg.Section("cluster").Key("center_port").String()) // 服务器地址
b, _ := json.MarshalIndent(conf, "", " ")
return conf, string(b)
}
func start() error {
fmt.Println("start")
err := status()
if err == nil {
return err
}
NodeType := os.Args[2]
Zone := os.Args[3]
// 示例命令
app_name := cfg.Section("app").Key("app_name").String()
cmdName := app_path + "/main"
cmdArgs := []string{fmt.Sprintf("%s/zone/%s_%s_%s/server.json", app_path, app_name, NodeType, Zone)}
// 创建命令
cmd := exec.Command(cmdName, cmdArgs...)
cmd.SysProcAttr = &syscall.SysProcAttr{}
// 创建管道
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Env = os.Environ()
// 打开输出文件
outfile, err := os.OpenFile(fmt.Sprintf("%s/zone/%s_%s_%s/output.log", app_path, app_name, NodeType, Zone), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal(err)
}
defer outfile.Close()
// 重定向标准输出和标准错误到文件
cmd.Stdout = outfile
cmd.Stderr = outfile
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
// 打印进程号
fmt.Printf("进程 %d 已启动\n", cmd.Process.Pid)
// 发送命令到子进程
go func() {
time.Sleep(2 * time.Second) // 等待子进程启动
_, err := stdin.Write([]byte("your_command\n"))
if err != nil {
log.Fatal(err)
}
stdin.Close()
}()
// 释放与子进程相关的资源
err = cmd.Process.Release()
if err != nil {
log.Fatal(err)
}
return nil
}
func stop() error {
fmt.Println("stop")
err := status()
if err != nil {
return err
}
NodeType := os.Args[2]
Zone := os.Args[3]
// 示例命令
app_name := cfg.Section("app").Key("app_name").String()
processName := fmt.Sprintf("%s/zone/%s_%s_%s/server.json", app_path, app_name, NodeType, Zone)
// 示例进程名称
// 获取进程号
pid, err := getPidByArgs(processName)
if err != nil {
log.Fatal(err)
}
// 查找进程
process, err := os.FindProcess(pid)
if err != nil {
log.Fatal(err)
}
// 关闭进程
err = process.Kill()
if err != nil {
log.Fatal(err)
}
// // 等待子进程退出
// _, err = process.Wait()
// if err != nil {
// log.Fatal(err)
// }
fmt.Printf("进程 %d 已关闭\n", pid)
return nil
}
// func getPidByName(name string) (int, error) {
// cmd := exec.Command("pgrep", name)
// output, err := cmd.Output()
// if err != nil {
// return 0, err
// }
// // 解析输出,获取第一个匹配的进程号
// outputStr := strings.TrimSpace(string(output))
// pidStr := strings.Split(outputStr, "\n")[0]
// pid, err := strconv.Atoi(pidStr)
// if err != nil {
// return 0, err
// }
// return pid, nil
// }
func getPidByArgs(args string) (int, error) {
cmd := exec.Command("pgrep", "-f", args)
output, err := cmd.Output()
if err != nil {
return 0, err
}
// 解析输出,获取第一个匹配的进程号
outputStr := strings.TrimSpace(string(output))
pidStr := strings.Split(outputStr, "\n")[0]
pid, err := strconv.Atoi(pidStr)
if err != nil {
return 0, err
}
return pid, nil
}
func status() error {
NodeType := os.Args[2]
Zone := os.Args[3]
// 示例命令
app_name := cfg.Section("app").Key("app_name").String()
processName := fmt.Sprintf("%s/zone/%s_%s_%s/server.json", app_path, app_name, NodeType, Zone)
// 示例进程名称
// 获取进程号
pid, err := getPidByArgs(processName)
if err != nil {
return fmt.Errorf("进程 %s_%s 未启动", NodeType, Zone)
}
// 查找进程
_, err = os.FindProcess(pid)
if err != nil {
return fmt.Errorf("进程 %s_%s 未启动", NodeType, Zone)
}
fmt.Printf("节点 %s_%s 启动中, 进程号 %d\n", NodeType, Zone, pid)
return nil
}
func restart() error {
fmt.Println("restart")
err := stop()
if err != nil {
fmt.Println(err)
}
for status() == nil {
time.Sleep(1 * time.Second)
}
err = start()
if err != nil {
return err
}
return nil
}
func restartAll() error {
fmt.Println("restartAll")
err := stop()
if err != nil {
fmt.Println(err)
}
for status() == nil {
time.Sleep(1 * time.Second)
}
err = start()
if err != nil {
return err
}
return nil
}