版本封存
This commit is contained in:
47
cmii-uav-watchdog-agent/cmd/env_info.go
Normal file
47
cmii-uav-watchdog-agent/cmd/env_info.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"cmii-uav-watchdog-common/models"
|
||||
)
|
||||
|
||||
var PodEnv = models.EnvInfo{}
|
||||
|
||||
func init() {
|
||||
PodEnv = GetEnvInfo()
|
||||
}
|
||||
|
||||
// GetEnvInfo 获取环境信息
|
||||
// 单例模式, 如果已经初始化过, 则直接返回
|
||||
func GetEnvInfo() models.EnvInfo {
|
||||
var once sync.Once
|
||||
|
||||
if PodEnv.K8S_NAMESPACE != "" {
|
||||
return PodEnv
|
||||
}
|
||||
|
||||
once.Do(func() {
|
||||
PodEnv = models.EnvInfo{
|
||||
K8S_NAMESPACE: os.Getenv("K8S_NAMESPACE"),
|
||||
APPLICATION_NAME: os.Getenv("APPLICATION_NAME"),
|
||||
CUST_JAVA_OPTS: os.Getenv("CUST_JAVA_OPTS"),
|
||||
BIZ_CONFIG_GROUP: os.Getenv("BIZ_CONFIG_GROUP"),
|
||||
SYS_CONFIG_GROUP: os.Getenv("SYS_CONFIG_GROUP"),
|
||||
IMAGE_NAME: os.Getenv("IMAGE_NAME"),
|
||||
JAVA_VERSION: os.Getenv("JAVA_VERSION"),
|
||||
GIT_COMMIT: os.Getenv("GIT_COMMIT"),
|
||||
GIT_BRANCH: os.Getenv("GIT_BRANCH"),
|
||||
NODE_NAME: os.Getenv("NODE_NAME"),
|
||||
NODE_IP: os.Getenv("NODE_IP"),
|
||||
POD_NAME: os.Getenv("POD_NAME"),
|
||||
LIMIT_CPU: os.Getenv("LIMIT_CPU"),
|
||||
LIMIT_MEMORY: os.Getenv("LIMIT_MEMORY"),
|
||||
REQUEST_CPU: os.Getenv("REQUEST_CPU"),
|
||||
REQUEST_MEMORY: os.Getenv("REQUEST_MEMORY"),
|
||||
}
|
||||
})
|
||||
|
||||
return PodEnv
|
||||
}
|
||||
@@ -1 +1,70 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
// import (
|
||||
// "cmii-uav-watchdog-agent/host_info"
|
||||
// "cmii-uav-watchdog-common/wdd_log"
|
||||
// "net/http"
|
||||
// "os"
|
||||
// "os/signal"
|
||||
// "syscall"
|
||||
|
||||
// "github.com/gin-gonic/gin"
|
||||
// )
|
||||
|
||||
// func StartHostInfoGin() {
|
||||
// // 创建一个默认的 Gin 路由
|
||||
// var r = gin.Default() // 定义一个 GET 路由
|
||||
// r.GET("/ping", func(c *gin.Context) {
|
||||
// c.JSON(http.StatusOK, gin.H{"message": "pong"})
|
||||
// })
|
||||
|
||||
// // 定义一个 POST 路由
|
||||
// r.POST("/echo", func(c *gin.Context) {
|
||||
// var json map[string]interface{}
|
||||
// if err := c.ShouldBindJSON(&json); err == nil {
|
||||
// c.JSON(http.StatusOK, json)
|
||||
// } else {
|
||||
// c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
// }
|
||||
// })
|
||||
|
||||
// r.GET("/cpu", func(c *gin.Context) {
|
||||
// cpuInfo := host_info.GetCPUInfo() // 直接返回 CPU 信息
|
||||
// c.JSON(http.StatusOK, cpuInfo)
|
||||
// })
|
||||
|
||||
// r.GET("/memory", func(c *gin.Context) {
|
||||
// memInfo := host_info.GetMemoryInfo() // 直接返回内存信息
|
||||
// c.JSON(http.StatusOK, memInfo)
|
||||
// })
|
||||
|
||||
// r.GET("/disk", func(c *gin.Context) {
|
||||
// diskInfo := host_info.GetDiskInfo() // 直接返回磁盘信息
|
||||
// c.JSON(http.StatusOK, diskInfo)
|
||||
// })
|
||||
|
||||
// r.GET("/motherboard", func(c *gin.Context) {
|
||||
// mbInfo := host_info.GetMotherboardInfo() // 直接返回主板信息
|
||||
// c.JSON(http.StatusOK, mbInfo)
|
||||
// })
|
||||
|
||||
// r.GET("/network", func(c *gin.Context) {
|
||||
// networkInterfaces := host_info.GetNetworkInterfaces()
|
||||
// c.JSON(http.StatusOK, networkInterfaces)
|
||||
// })
|
||||
// r.GET("/all", func(c *gin.Context) {
|
||||
// allInfo := host_info.GetAllInfo()
|
||||
// c.JSON(http.StatusOK, allInfo)
|
||||
// })
|
||||
// //r.GET("/phy", func(c *gin.Context) {
|
||||
// // allInfo, _ := services.GetPVForLV()
|
||||
// // c.JSON(http.StatusOK, allInfo)
|
||||
// //})
|
||||
// // 启动服务,监听在 8080 端口
|
||||
// r.Run(":8098")
|
||||
// // 等待终止信号
|
||||
// sigs := make(chan os.Signal, 1)
|
||||
// signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
// <-sigs
|
||||
// wdd_log.Info("正在关闭服务...")
|
||||
// }
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 是否是Debug模式
|
||||
var DebugMode = false
|
||||
|
||||
var (
|
||||
businessProgramType = flag.String("business-program-type", "", "Type of business program (java or python)")
|
||||
businessProgramPath = flag.String("business-program-path", "", "Path to the business program file")
|
||||
@@ -19,45 +23,221 @@ var (
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
// java 启动参数
|
||||
var (
|
||||
podName = "unknown"
|
||||
imageName = "unknown"
|
||||
imageVersion = "unknown"
|
||||
workpath = "/cmii"
|
||||
custJavaOpts = "-Xms200m -Xmx1500m -Djava.awt.headless=true -Dlog4j2.formatMsgNoLookups=true "
|
||||
envJvmTimezone = "Asia/Shanghai"
|
||||
k8sNamespace = "default"
|
||||
applicationName = "app"
|
||||
nacosRegistry = "helm-nacos:8848"
|
||||
nacosDiscoveryIp = "127.0.0.1"
|
||||
nacosDiscoveryPort = "8080"
|
||||
nacosUsername = "nacos"
|
||||
nacosPassword = "nacos"
|
||||
bizConfigGroup = "wdd-biz"
|
||||
sysConfigGroup = "wdd-sys"
|
||||
)
|
||||
|
||||
// 初始化
|
||||
func init() {
|
||||
// 获取环境变量 特殊设置才能开启DEBUG
|
||||
debugMode := os.Getenv("CMII_DEBUG_MODE")
|
||||
if debugMode == "WDD_DEBUG" {
|
||||
DebugMode = true
|
||||
}
|
||||
wdd_log.Info("DebugMode 是否开启: %v", DebugMode)
|
||||
}
|
||||
|
||||
// 初始化配置并打印配置信息
|
||||
func initConfig() {
|
||||
// 获取环境变量
|
||||
imageName = getEnvOrDefault("IMAGE_NAME", imageName)
|
||||
if imageName != "unknown" {
|
||||
parts := strings.Split(imageName, ":")
|
||||
if len(parts) > 1 {
|
||||
imageVersion = parts[len(parts)-1]
|
||||
}
|
||||
}
|
||||
workpath = getEnvOrDefault("WORKPATH", workpath)
|
||||
custJavaOpts = getEnvOrDefault("CUST_JAVA_OPTS", custJavaOpts)
|
||||
envJvmTimezone = getEnvOrDefault("ENV_JVM_TIMEZONE", envJvmTimezone)
|
||||
k8sNamespace = getEnvOrDefault("K8S_NAMESPACE", k8sNamespace)
|
||||
applicationName = getEnvOrDefault("APPLICATION_NAME", applicationName)
|
||||
nacosRegistry = getEnvOrDefault("NACOS_REGISTRY", nacosRegistry)
|
||||
nacosDiscoveryIp = getEnvOrDefault("NACOS_DISCOVERY_IP", nacosDiscoveryIp)
|
||||
nacosDiscoveryPort = getEnvOrDefault("NACOS_DISCOVERY_PORT", nacosDiscoveryPort)
|
||||
nacosUsername = getEnvOrDefault("NACOS_USERNAME", nacosUsername)
|
||||
nacosPassword = getEnvOrDefault("NACOS_PASSWORD", nacosPassword)
|
||||
|
||||
// 设置默认配置组
|
||||
defaultConfigGroup := "default"
|
||||
if imageVersion != "unknown" {
|
||||
parts := strings.Split(imageVersion, "-")
|
||||
if len(parts) > 0 {
|
||||
defaultConfigGroup = parts[0]
|
||||
}
|
||||
}
|
||||
|
||||
bizConfigGroup = getEnvOrDefault("BIZ_CONFIG_GROUP", bizConfigGroup)
|
||||
if bizConfigGroup == "" {
|
||||
wdd_log.Info("[CONTAINER] As BIZ_CONFIG_GROUP is null, it set default value [%s]", defaultConfigGroup)
|
||||
bizConfigGroup = defaultConfigGroup
|
||||
os.Setenv("BIZ_CONFIG_GROUP", bizConfigGroup)
|
||||
}
|
||||
|
||||
sysConfigGroup = getEnvOrDefault("SYS_CONFIG_GROUP", sysConfigGroup)
|
||||
if sysConfigGroup == "" {
|
||||
wdd_log.Info("[CONTAINER] As SYS_CONFIG_GROUP is null, it set default value [%s]", defaultConfigGroup)
|
||||
sysConfigGroup = defaultConfigGroup
|
||||
os.Setenv("SYS_CONFIG_GROUP", sysConfigGroup)
|
||||
}
|
||||
|
||||
// 打印配置信息
|
||||
wdd_log.Info("[CONTAINER] %s image is running ...", imageName)
|
||||
wdd_log.Info("[CONTAINER] IMAGE_VERSION is %s", imageVersion)
|
||||
wdd_log.Info("[CONTAINER] WORKPATH is %s", workpath)
|
||||
wdd_log.Info("[CONTAINER] CUST_JAVA_OPTS is %s", custJavaOpts)
|
||||
wdd_log.Info("[CONTAINER] JVM_TIMEZONE is %s", envJvmTimezone)
|
||||
wdd_log.Info("[CONTAINER] K8S_NAMESPACE is %s", k8sNamespace)
|
||||
wdd_log.Info("[CONTAINER] APPLICATION_NAME is %s", applicationName)
|
||||
wdd_log.Info("[CONTAINER] NACOS_REGISTRY is %s", nacosRegistry)
|
||||
wdd_log.Info("[CONTAINER] NACOS_DISCOVERY_IP is %s", nacosDiscoveryIp)
|
||||
wdd_log.Info("[CONTAINER] NACOS_DISCOVERY_PORT is %s", nacosDiscoveryPort)
|
||||
wdd_log.Info("[CONTAINER] NACOS_USERNAME is %s", nacosUsername)
|
||||
wdd_log.Info("[CONTAINER] NACOS_PASSWORD is %s", nacosPassword)
|
||||
wdd_log.Info("[CONTAINER] BIZ_CONFIG_GROUP is %s", bizConfigGroup)
|
||||
wdd_log.Info("[CONTAINER] SYS_CONFIG_GROUP is %s", sysConfigGroup)
|
||||
wdd_log.Info("[CONTAINER] starting...")
|
||||
}
|
||||
|
||||
// 获取环境变量,如果为空则返回默认值
|
||||
func getEnvOrDefault(key, defaultValue string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func startBusinessProcess(programType, programPath string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
switch programType {
|
||||
case "java":
|
||||
cmd = exec.Command("java", "-jar", programPath)
|
||||
// 初始化配置
|
||||
initConfig()
|
||||
|
||||
// 构建命令参数列表
|
||||
args := []string{}
|
||||
|
||||
// 添加Java选项
|
||||
if custJavaOpts != "" {
|
||||
// 分割CUST_JAVA_OPTS中的多个参数
|
||||
for _, opt := range splitArgs(custJavaOpts) {
|
||||
if opt != "" {
|
||||
args = append(args, opt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加主JAR文件
|
||||
args = append(args, "-jar", programPath)
|
||||
|
||||
// 添加其他参数
|
||||
args = append(args, []string{
|
||||
"--user.timezone=" + envJvmTimezone,
|
||||
"-Dfile.encoding=UTF-8",
|
||||
"--spring.main.allow-bean-definition-overriding=true",
|
||||
"--spring.application.name=" + applicationName,
|
||||
"--spring.cloud.nacos.username=" + nacosUsername,
|
||||
"--spring.cloud.nacos.password=" + nacosPassword,
|
||||
"--spring.cloud.nacos.config.server-addr=" + nacosRegistry,
|
||||
"--spring.cloud.nacos.config.extension-configs[0].data-id=" + applicationName + ".yml",
|
||||
"--spring.cloud.nacos.config.extension-configs[0].group=" + bizConfigGroup,
|
||||
"--spring.cloud.nacos.config.extension-configs[0].refresh=true",
|
||||
"--spring.cloud.nacos.config.shared-configs[0].data-id=cmii-backend-system.yml",
|
||||
"--spring.cloud.nacos.config.shared-configs[0].group=" + sysConfigGroup,
|
||||
"--spring.cloud.nacos.config.shared-configs[0].refresh=true",
|
||||
"--spring.cloud.nacos.discovery.server-addr=" + nacosRegistry,
|
||||
"--spring.cloud.nacos.discovery.ip=" + nacosDiscoveryIp,
|
||||
"--spring.cloud.nacos.discovery.port=" + nacosDiscoveryPort,
|
||||
}...)
|
||||
|
||||
wdd_log.Info("[CONTAINER] java args: %v", args)
|
||||
|
||||
cmd = exec.Command("java", args...)
|
||||
case "python":
|
||||
cmd = exec.Command("python", programPath)
|
||||
default:
|
||||
log.Fatalf("Unsupported business program type: %s", programType)
|
||||
wdd_log.Error("不支持的业务程序类型: %s", programType)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// 分割命令行参数
|
||||
func splitArgs(s string) []string {
|
||||
var args []string
|
||||
var inQuote bool
|
||||
var current string
|
||||
var quoteChar rune
|
||||
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case (r == '"' || r == '\'') && !inQuote:
|
||||
inQuote = true
|
||||
quoteChar = r
|
||||
case r == quoteChar && inQuote:
|
||||
inQuote = false
|
||||
quoteChar = 0
|
||||
case r == ' ' && !inQuote:
|
||||
if current != "" {
|
||||
args = append(args, current)
|
||||
current = ""
|
||||
}
|
||||
default:
|
||||
current += string(r)
|
||||
}
|
||||
}
|
||||
|
||||
if current != "" {
|
||||
args = append(args, current)
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 解析命令行参数
|
||||
flag.Parse()
|
||||
if *businessProgramType == "" || *businessProgramPath == "" {
|
||||
log.Fatal("Missing required flags: -business-program-type and -business-program-path must be specified")
|
||||
wdd_log.Error("缺少必要的参数: -business-program-type 和 -business-program-path 必须指定")
|
||||
}
|
||||
|
||||
// 信号处理
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// StartHeartbeatDetection(signalChan)
|
||||
|
||||
go func() {
|
||||
for sig := range signalChan {
|
||||
log.Printf("Received signal: %v", sig)
|
||||
wdd_log.Info("接收到信号: %v", sig)
|
||||
mu.Lock()
|
||||
stopRequested = true
|
||||
if currentCmd != nil && currentCmd.Process != nil {
|
||||
// 发送 SIGTERM 给业务进程
|
||||
if err := currentCmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||
log.Printf("Failed to send SIGTERM to process: %v", err)
|
||||
wdd_log.Error("向进程发送SIGTERM信号失败: %v", err)
|
||||
}
|
||||
// 等待 10 秒后强制杀死进程
|
||||
time.AfterFunc(10*time.Second, func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if currentCmd != nil && currentCmd.Process != nil {
|
||||
log.Println("Graceful shutdown timeout, sending SIGKILL")
|
||||
wdd_log.Warn("优雅关闭超时,发送SIGKILL信号")
|
||||
currentCmd.Process.Kill()
|
||||
}
|
||||
})
|
||||
@@ -66,12 +246,19 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
// 授权检测
|
||||
go func() {
|
||||
for {
|
||||
StartHeartbeatDetection(signalChan)
|
||||
}
|
||||
}()
|
||||
|
||||
// 主循环
|
||||
for {
|
||||
mu.Lock()
|
||||
if stopRequested {
|
||||
mu.Unlock()
|
||||
log.Println("Shutting down due to stop request")
|
||||
wdd_log.Info("收到停止请求,正在关闭")
|
||||
os.Exit(0)
|
||||
}
|
||||
mu.Unlock()
|
||||
@@ -82,11 +269,16 @@ func main() {
|
||||
|
||||
// 启动业务进程
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Printf("Failed to start business process: %v", err)
|
||||
wdd_log.Error("启动业务进程失败: %v", err)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// 业务进程启动成功
|
||||
if *businessProgramType == "java" {
|
||||
wdd_log.Info("[CONTAINER] 程序启动成功!")
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
currentCmd = cmd
|
||||
mu.Unlock()
|
||||
@@ -98,21 +290,21 @@ func main() {
|
||||
mu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Business process exited with error: %v", err)
|
||||
wdd_log.Error("业务进程异常退出: %v", err)
|
||||
} else {
|
||||
log.Println("Business process exited normally")
|
||||
wdd_log.Info("业务进程正常退出")
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
if stopRequested {
|
||||
mu.Unlock()
|
||||
log.Println("Shutting down due to stop request")
|
||||
wdd_log.Info("收到停止请求,正在关闭")
|
||||
os.Exit(0)
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
// 等待 5 秒后重启
|
||||
log.Println("Restarting business process in 5 seconds...")
|
||||
wdd_log.Info("5秒后重启业务进程...")
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +1,95 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-agent/host_info"
|
||||
"cmii-uav-watchdog-agent/rpc"
|
||||
"cmii-uav-watchdog-agent/totp"
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"fmt"
|
||||
"log"
|
||||
"cmii-uav-watchdog-common/totp_tier_two"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
// 最大重试次数
|
||||
maxRetryCount = 5
|
||||
maxRetryCount = 12
|
||||
|
||||
// 默认心跳检测间隔
|
||||
defaultHeartbeatInterval = 30 * time.Second
|
||||
defaultHeartbeatInterval = 2 * time.Hour
|
||||
|
||||
// 检测失败后的等待间隔
|
||||
failWaitInterval = 5 * time.Second
|
||||
|
||||
// 环境变量名称
|
||||
appNameEnv = "APP_NAME"
|
||||
failWaitInterval = 1 * time.Hour
|
||||
)
|
||||
|
||||
// 启动心跳检测
|
||||
func StartHeartbeatDetection() {
|
||||
log.Println("启动心跳检测任务...")
|
||||
var tierTwoTotpSecret = ""
|
||||
|
||||
// StartHeartbeatDetection 启动心跳检测
|
||||
func StartHeartbeatDetection(signalChan chan os.Signal) {
|
||||
wdd_log.Info("启动心跳检测任务...")
|
||||
|
||||
// variable
|
||||
var err error
|
||||
|
||||
// 如果Debug模式,那么使用环境变量赋值
|
||||
if DebugMode {
|
||||
// 如果从在环境变量 那么使用环境变量赋值
|
||||
heartbeatInterval := os.Getenv("WATCHDOG_AGENT_HEARTBEAT_INTERVAL")
|
||||
if heartbeatInterval != "" {
|
||||
defaultHeartbeatInterval, err = time.ParseDuration(heartbeatInterval)
|
||||
if err != nil {
|
||||
wdd_log.Error("无法解析环境变量: %v", err)
|
||||
}
|
||||
wdd_log.Info("已更新心跳检测间隔 => %s", defaultHeartbeatInterval)
|
||||
}
|
||||
|
||||
failWaitIntervalEnv := os.Getenv("WATCHDOG_AGENT_FAIL_WAIT_INTERVAL")
|
||||
if failWaitIntervalEnv != "" {
|
||||
failWaitInterval, err = time.ParseDuration(failWaitIntervalEnv)
|
||||
if err != nil {
|
||||
wdd_log.Error("无法解析环境变量: %v", err)
|
||||
}
|
||||
wdd_log.Info("已更新心跳检测失败等待间隔 => %s", failWaitInterval)
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
// 创建RPC客户端
|
||||
client := rpc.NewClient(nil)
|
||||
heartbeatURL := os.Getenv("WATCHDOG_HEARTBEAT_URL")
|
||||
client := rpc.NewClient(nil, heartbeatURL)
|
||||
|
||||
// 监听终止信号
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
||||
wdd_log.Info("心跳检测URL: %s", client.GetHeartbeatURL())
|
||||
|
||||
// 失败计数器
|
||||
failCount := 0
|
||||
failCount := 1
|
||||
|
||||
// 心跳检测循环
|
||||
for {
|
||||
select {
|
||||
case <-signalChan:
|
||||
log.Println("收到终止信号,停止心跳检测")
|
||||
wdd_log.Info("收到终止信号,停止心跳检测")
|
||||
return
|
||||
default:
|
||||
// 尝试发送心跳请求
|
||||
authorized, err := sendHeartbeat(client)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("心跳检测失败: %v", err)
|
||||
wdd_log.Error("第 %d 次心跳检测失败: %v", failCount, err)
|
||||
failCount++
|
||||
} else if !authorized {
|
||||
log.Println("未获得授权")
|
||||
wdd_log.Warn("第 %d 次心跳检测未获得授权", failCount)
|
||||
failCount++
|
||||
} else {
|
||||
// 检测成功,重置失败计数
|
||||
failCount = 0
|
||||
log.Println("心跳检测成功,已获得授权")
|
||||
failCount = 1
|
||||
wdd_log.Info("第 %d 次心跳检测成功,已获得授权", failCount)
|
||||
}
|
||||
|
||||
// 检查是否达到最大失败次数
|
||||
if failCount >= maxRetryCount {
|
||||
log.Printf("心跳检测连续失败 %d 次,发送终止信号", failCount)
|
||||
// 发送终止信号给start_up.go
|
||||
process, err := os.FindProcess(os.Getpid())
|
||||
if err == nil {
|
||||
process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
wdd_log.Fatal("心跳检测连续失败 %d 次,发送终止信号!!", failCount)
|
||||
signalChan <- syscall.SIGTERM
|
||||
return
|
||||
}
|
||||
|
||||
@@ -88,8 +107,9 @@ func StartHeartbeatDetection() {
|
||||
|
||||
// 发送心跳请求
|
||||
func sendHeartbeat(client *rpc.Client) (bool, error) {
|
||||
|
||||
// 1. 获取主机信息
|
||||
hostInfoData := services.GetAllInfo()
|
||||
hostInfoData := host_info.GetAllInfo()
|
||||
hostInfo := models.HostInfo{
|
||||
SystemInfo: hostInfoData.SystemInfo,
|
||||
CPUInfo: hostInfoData.CPUInfo,
|
||||
@@ -98,26 +118,21 @@ func sendHeartbeat(client *rpc.Client) (bool, error) {
|
||||
NetInfo: hostInfoData.NetInfo,
|
||||
}
|
||||
|
||||
// 2. 获取应用名称
|
||||
appName := os.Getenv(appNameEnv)
|
||||
if appName == "" {
|
||||
appName = "unknown-app"
|
||||
log.Printf("警告: 环境变量 %s 未设置,使用默认值: %s", appNameEnv, appName)
|
||||
}
|
||||
// 获取环境信息
|
||||
envInfo := GetEnvInfo()
|
||||
|
||||
// 构建心跳请求
|
||||
request := &models.HeartbeatRequest{
|
||||
HostInfo: hostInfo,
|
||||
Timestamp: time.Now().Unix(),
|
||||
AppName: appName,
|
||||
EnvInfo: envInfo,
|
||||
}
|
||||
|
||||
// 3. 如果已有TOTP密钥,则生成TOTP验证码
|
||||
totpSecret := totp.GetTOTPSecret()
|
||||
if totpSecret != "" {
|
||||
totpCode, err := totp.GenerateTOTPCode()
|
||||
if tierTwoTotpSecret != "" {
|
||||
totpCode, err := totp_tier_two.GenerateTierTwoTOTPCode(tierTwoTotpSecret)
|
||||
if err != nil {
|
||||
log.Printf("生成TOTP验证码失败: %v", err)
|
||||
wdd_log.Error("生成TOTP验证码失败: %v", err)
|
||||
} else {
|
||||
request.TOTPCode = totpCode
|
||||
}
|
||||
@@ -126,20 +141,24 @@ func sendHeartbeat(client *rpc.Client) (bool, error) {
|
||||
// 4. 发送心跳请求
|
||||
response, err := client.SendHeartbeatWithRetry(request, 10*time.Second)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("发送心跳请求失败: %w", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if response == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 5. 处理响应
|
||||
if response.SecondTOTPSecret != "" {
|
||||
// 存储TOTP密钥
|
||||
totp.SetTOTPSecret(response.SecondTOTPSecret)
|
||||
log.Println("已更新TOTP密钥")
|
||||
tierTwoTotpSecret = response.SecondTOTPSecret
|
||||
wdd_log.Info("已更新TOTP密钥 => %s", tierTwoTotpSecret)
|
||||
}
|
||||
|
||||
// 6. 如果有TOTP验证码,进行验证
|
||||
if response.TOTPCode != "" && totpSecret != "" {
|
||||
if !totp.ValidateTOTPCode(response.TOTPCode) {
|
||||
log.Println("TOTP验证码验证失败")
|
||||
if response.TOTPCode != "" && tierTwoTotpSecret != "" {
|
||||
if !totp_tier_two.VerifyTierTwoTOTPCode(response.TOTPCode, tierTwoTotpSecret) {
|
||||
wdd_log.Warn("TOTP验证码验证失败")
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,38 +2,10 @@ module cmii-uav-watchdog-agent
|
||||
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
cmii-uav-watchdog-common v0.0.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
)
|
||||
require cmii-uav-watchdog-common v0.0.0
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
require cmii-uav-watchdog-otp v0.0.0 // indirect
|
||||
|
||||
replace cmii-uav-watchdog-common => ../cmii-uav-watchdog-common
|
||||
|
||||
replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -1,163 +1,205 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"fmt"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewCPUInfo 创建一个默认的 CPUInfo
|
||||
func NewCPUInfo() models.CPUInfo {
|
||||
return models.CPUInfo{
|
||||
ModelName: "unknown",
|
||||
Cores: 0,
|
||||
Architecture: "unknown",
|
||||
UUID: "unknown",
|
||||
ModelName: "unknown",
|
||||
Cores: 0,
|
||||
Architecture: "amd64",
|
||||
Flags: []string{},
|
||||
Hypervisor: "unknown",
|
||||
Virtualization: "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
exePath = "/proc/self/exe"
|
||||
elfMagic = 0x7f
|
||||
elfMagicString = "ELF"
|
||||
eMachineOffset = 18
|
||||
)
|
||||
|
||||
// parseLine 解析单行 CPU 信息
|
||||
func parseLine(line string, cpuInfo *models.CPUInfo) {
|
||||
if strings.HasPrefix(line, "model name") {
|
||||
cpuInfo.ModelName = strings.TrimSpace(strings.Split(line, ":")[1])
|
||||
}
|
||||
}
|
||||
|
||||
// getCPUCores 通过 processor 行获取 CPU 核心数
|
||||
func getCPUCores() (int, error) {
|
||||
cores := 0
|
||||
file, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return cores, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "processor") {
|
||||
cores++
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return cores, err
|
||||
}
|
||||
|
||||
return cores, nil
|
||||
}
|
||||
|
||||
// getCPUUUID 获取 CPU UUID
|
||||
func getCPUUUID() (string, error) {
|
||||
data, err := os.ReadFile("/sys/class/dmi/id/product_uuid")
|
||||
if err != nil {
|
||||
return "unknown", err
|
||||
}
|
||||
return strings.TrimSpace(string(data)), nil
|
||||
}
|
||||
|
||||
func getCPUArchitecture() string {
|
||||
cpuarch := "unknown"
|
||||
|
||||
// 打开当前进程的执行文件
|
||||
file, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Error opening %s: %v\n", exePath, err)
|
||||
return cpuarch
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 读取前64个字节以获取 ELF 头部信息
|
||||
header := make([]byte, 64)
|
||||
if _, err := file.Read(header); err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Error reading header: %v\n", err)
|
||||
return cpuarch
|
||||
}
|
||||
|
||||
// 检查 ELF 文件标识
|
||||
if header[0] != elfMagic || string(header[1:4]) != elfMagicString {
|
||||
_, _ = fmt.Fprintln(os.Stderr, "File is not an ELF file")
|
||||
return cpuarch
|
||||
}
|
||||
|
||||
// 获取架构信息
|
||||
arch := header[eMachineOffset] // e_machine 字段的偏移量
|
||||
switch arch {
|
||||
case 0x02: // EM_386
|
||||
cpuarch = "x86(32-bit)"
|
||||
case 0x03: // EM_X86_64
|
||||
cpuarch = "x86_64(64-bit)"
|
||||
case 0x28: // EM_ARM
|
||||
cpuarch = "ARM"
|
||||
case 0x2A: // EM_ARM64
|
||||
cpuarch = "ARM64"
|
||||
case 0x08: // EM_MIPS
|
||||
cpuarch = "MIPS"
|
||||
default:
|
||||
cpuarch = "unknown architecture"
|
||||
}
|
||||
|
||||
return cpuarch
|
||||
}
|
||||
|
||||
// GetCPUInfo 获取 CPU 信息
|
||||
// GetCPUInfo 获取CPU信息
|
||||
// 返回CPU信息结构体
|
||||
func GetCPUInfo() models.CPUInfo {
|
||||
wdd_log.Debug("开始获取CPU信息")
|
||||
|
||||
// 首先尝试从/proc/cpuinfo文件中读取信息
|
||||
cpuInfo, err := getCPUInfoFromProc()
|
||||
if err == nil {
|
||||
wdd_log.Debug("成功从/proc/cpuinfo获取CPU信息")
|
||||
return cpuInfo
|
||||
}
|
||||
|
||||
// 如果从文件获取失败,尝试使用lscpu命令
|
||||
wdd_log.Warn("从/proc/cpuinfo获取CPU信息失败: %s,尝试使用lscpu命令", err.Error())
|
||||
cpuInfo, err = getCPUInfoFromLscpu()
|
||||
if err == nil {
|
||||
wdd_log.Debug("成功从lscpu命令获取CPU信息")
|
||||
return cpuInfo
|
||||
}
|
||||
|
||||
// 如果都失败,使用runtime包获取基本信息
|
||||
wdd_log.Warn("从lscpu获取CPU信息失败: %s,使用runtime包获取基本信息", err.Error())
|
||||
return getFallbackCPUInfo()
|
||||
}
|
||||
|
||||
// getCPUInfoFromProc 从/proc/cpuinfo文件中读取CPU信息
|
||||
// 返回CPU信息结构体和可能的错误
|
||||
func getCPUInfoFromProc() (models.CPUInfo, error) {
|
||||
wdd_log.Debug("尝试从/proc/cpuinfo读取CPU信息")
|
||||
|
||||
// 创建默认的CPU信息结构体
|
||||
cpuInfo := NewCPUInfo()
|
||||
|
||||
// 读取 /proc/cpuinfo
|
||||
// 打开/proc/cpuinfo文件
|
||||
file, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
fmt.Println("Error opening /proc/cpuinfo:", err)
|
||||
return cpuInfo // 返回默认值
|
||||
return cpuInfo, err
|
||||
}
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
fmt.Println("Error closing /proc/cpuinfo:", err)
|
||||
}
|
||||
}()
|
||||
defer file.Close()
|
||||
|
||||
// 使用scanner读取文件
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// 处理的CPU核心数量
|
||||
coreCount := 1
|
||||
var flags []string
|
||||
|
||||
// 逐行读取并解析
|
||||
for scanner.Scan() {
|
||||
parseLine(scanner.Text(), &cpuInfo)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Println("Error reading /proc/cpuinfo:", err)
|
||||
return cpuInfo // 返回默认值
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ":")
|
||||
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
switch key {
|
||||
case "processor":
|
||||
// 计数处理器核心数量
|
||||
coreCount++
|
||||
case "model name":
|
||||
// 获取CPU型号名称
|
||||
cpuInfo.ModelName = value
|
||||
// 提取频率信息(如果包含)
|
||||
if strings.Contains(value, "@") {
|
||||
freqParts := strings.Split(value, "@")
|
||||
if len(freqParts) > 1 {
|
||||
cpuInfo.Frequency = strings.TrimSpace(freqParts[1])
|
||||
}
|
||||
}
|
||||
case "flags":
|
||||
// 获取CPU标志
|
||||
flags = strings.Fields(value)
|
||||
case "cpu cores":
|
||||
// 获取每个物理CPU的核心数
|
||||
cores, err := strconv.Atoi(value)
|
||||
if err == nil && cores > 0 {
|
||||
cpuInfo.Cores = cores
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 CPU 核心数
|
||||
cores, err := getCPUCores()
|
||||
// 如果没有从cpu cores字段获取到核心数,使用处理器计数
|
||||
if cpuInfo.Cores == 0 && coreCount > 0 {
|
||||
cpuInfo.Cores = coreCount
|
||||
}
|
||||
|
||||
// 设置CPU标志
|
||||
cpuInfo.Flags = flags
|
||||
|
||||
// 检测虚拟化相关信息
|
||||
for _, flag := range flags {
|
||||
if flag == "vmx" {
|
||||
cpuInfo.Virtualization = "vmx"
|
||||
} else if flag == "svm" {
|
||||
cpuInfo.Virtualization = "svm"
|
||||
} else if flag == "hypervisor" {
|
||||
cpuInfo.Hypervisor = "present"
|
||||
}
|
||||
}
|
||||
|
||||
// 设置架构
|
||||
cpuInfo.Architecture = runtime.GOARCH
|
||||
|
||||
return cpuInfo, nil
|
||||
}
|
||||
|
||||
// getCPUInfoFromLscpu 使用lscpu命令获取CPU信息
|
||||
// 返回CPU信息结构体和可能的错误
|
||||
func getCPUInfoFromLscpu() (models.CPUInfo, error) {
|
||||
wdd_log.Debug("尝试使用lscpu命令获取CPU信息")
|
||||
|
||||
// 创建默认的CPU信息结构体
|
||||
cpuInfo := NewCPUInfo()
|
||||
|
||||
// 执行lscpu命令
|
||||
cmd := exec.Command("lscpu")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting CPU cores:", err)
|
||||
} else {
|
||||
cpuInfo.Cores = cores
|
||||
return cpuInfo, err
|
||||
}
|
||||
|
||||
// 获取 CPU UUID
|
||||
uuid, err := getCPUUUID()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting CPU UUID:", err)
|
||||
} else {
|
||||
cpuInfo.UUID = uuid
|
||||
// 解析输出
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
switch key {
|
||||
case "Model name":
|
||||
cpuInfo.ModelName = value
|
||||
// 提取频率信息(如果包含)
|
||||
if strings.Contains(value, "@") {
|
||||
freqParts := strings.Split(value, "@")
|
||||
if len(freqParts) > 1 {
|
||||
cpuInfo.Frequency = strings.TrimSpace(freqParts[1])
|
||||
}
|
||||
}
|
||||
case "Architecture":
|
||||
cpuInfo.Architecture = value
|
||||
case "CPU(s)":
|
||||
cores, err := strconv.Atoi(value)
|
||||
if err == nil {
|
||||
cpuInfo.Cores = cores
|
||||
}
|
||||
case "Flags":
|
||||
cpuInfo.Flags = strings.Fields(value)
|
||||
case "Hypervisor vendor":
|
||||
cpuInfo.Hypervisor = value
|
||||
case "Virtualization":
|
||||
cpuInfo.Virtualization = value
|
||||
case "CPU MHz":
|
||||
if cpuInfo.Frequency == "" {
|
||||
cpuInfo.Frequency = value + " MHz"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpuInfo.Architecture = getCPUArchitecture()
|
||||
return cpuInfo, nil
|
||||
}
|
||||
|
||||
// getFallbackCPUInfo 使用runtime包获取基本CPU信息
|
||||
// 返回CPU信息结构体
|
||||
func getFallbackCPUInfo() models.CPUInfo {
|
||||
wdd_log.Debug("使用runtime包获取基本CPU信息")
|
||||
|
||||
cpuInfo := NewCPUInfo()
|
||||
cpuInfo.Cores = runtime.NumCPU()
|
||||
cpuInfo.Architecture = runtime.GOARCH
|
||||
|
||||
return cpuInfo
|
||||
}
|
||||
|
||||
/*
|
||||
CPU模型名称: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
|
||||
核心数量: 4
|
||||
架构信息: x86_64
|
||||
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"fmt"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -68,7 +68,7 @@ func GetDiskInfo() []models.DiskInfo {
|
||||
// Get disk usage information using stat
|
||||
var stat syscall.Statfs_t
|
||||
if err := syscall.Statfs(mountPoint, &stat); err != nil {
|
||||
fmt.Printf("Warning: error getting statfs for %s: %v\n", mountPoint, err)
|
||||
wdd_log.Warn("获取 %s 的statfs信息失败: %v", mountPoint, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ func GetDiskInfo() []models.DiskInfo {
|
||||
// Calculate percentage used
|
||||
usePercent := "0%"
|
||||
if total > 0 {
|
||||
usePercent = fmt.Sprintf("%.1f%%", float64(used)/float64(total)*100)
|
||||
percentage := float64(used) / float64(total) * 100
|
||||
usePercent = strconv.FormatFloat(percentage, 'f', 1, 64) + "%"
|
||||
}
|
||||
|
||||
// Determine the physical device and its size
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import "cmii-uav-watchdog-common/models"
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"fmt"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -19,7 +19,7 @@ var DefaultMotherboardInfo = models.MotherboardInfo{
|
||||
func readFileWithWarning(path string) ([]byte, error) {
|
||||
value, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: unable to read file %s: %v\n", path, err)
|
||||
wdd_log.Warn("无法读取文件 %s: %v", path, err)
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"fmt"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -26,7 +26,7 @@ func GetMemoryInfo() models.MemoryInfo {
|
||||
memInfo := NewMemoryInfo()
|
||||
data, err := os.ReadFile("/proc/meminfo")
|
||||
if err != nil {
|
||||
fmt.Println("Error reading /proc/meminfo:", err)
|
||||
wdd_log.Error("读取 /proc/meminfo 失败: %v", err)
|
||||
return memInfo
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func GetMemoryInfo() models.MemoryInfo {
|
||||
key := fields[0]
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing value for %s: %v\n", key, err)
|
||||
wdd_log.Error("解析 %s 的值失败: %v", key, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"fmt"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
@@ -25,7 +25,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo {
|
||||
// 获取所有网络接口
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting network interfaces:", err)
|
||||
wdd_log.Error("获取网络接口失败: %v", err)
|
||||
return []models.NetworkInterfaceInfo{{Name: "unknown", MACAddress: "00:00:00:00:00:00", IPAddresses: []string{}}}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo {
|
||||
// 获取 MAC 地址
|
||||
macAddress, err := getMACAddress(iface)
|
||||
if err != nil {
|
||||
fmt.Println("Error getting MAC address for", iface.Name, ":", err)
|
||||
wdd_log.Error("获取网卡 %s 的MAC地址失败: %v", iface.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo {
|
||||
var ipAddresses []string
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting addresses for", iface.Name, ":", err)
|
||||
wdd_log.Error("获取网卡 %s 的IP地址失败: %v", iface.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package services
|
||||
package host_info
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-common/models"
|
||||
"log"
|
||||
"cmii-uav-watchdog-common/wdd_log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -10,16 +10,12 @@ import (
|
||||
// NewOSInfo 创建并返回一个 OSInfo 实例,设置默认值
|
||||
func NewOSInfo() models.OSInfo {
|
||||
return models.OSInfo{
|
||||
Name: "unknown",
|
||||
Version: "unknown",
|
||||
ID: "unknown",
|
||||
IDLike: "unknown",
|
||||
VersionID: "unknown",
|
||||
PrettyName: "unknown",
|
||||
HomeURL: "unknown",
|
||||
SupportURL: "unknown",
|
||||
BugReportURL: "unknown",
|
||||
PrivacyURL: "unknown",
|
||||
Name: "unknown",
|
||||
Version: "unknown",
|
||||
ID: "unknown",
|
||||
IDLike: "unknown",
|
||||
VersionID: "unknown",
|
||||
PrettyName: "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +37,14 @@ func GetSystemInfo() models.SystemInfo {
|
||||
if machineID != "" {
|
||||
sysInfo.MachineID = machineID
|
||||
}
|
||||
|
||||
// 获取 machine serial
|
||||
|
||||
serialID := getMachineSerial()
|
||||
if serialID != "" {
|
||||
sysInfo.MachineSerial = serialID
|
||||
}
|
||||
|
||||
// 获取操作系统版本
|
||||
sysInfo.OS = getOSVersion()
|
||||
|
||||
@@ -50,12 +54,26 @@ func GetSystemInfo() models.SystemInfo {
|
||||
sysInfo.KernelVersion = kernelVersion
|
||||
}
|
||||
|
||||
// 获取节点名称
|
||||
sysInfo.NodeName = "unknown"
|
||||
nodeName := os.Getenv("NODE_NAME")
|
||||
if nodeName != "" {
|
||||
sysInfo.NodeName = nodeName
|
||||
}
|
||||
|
||||
// 获取节点IP
|
||||
sysInfo.NodeIP = "unknown"
|
||||
ip := os.Getenv("NODE_IP")
|
||||
if ip != "" {
|
||||
sysInfo.NodeIP = ip
|
||||
}
|
||||
|
||||
return sysInfo
|
||||
}
|
||||
func getOSVersion() models.OSInfo {
|
||||
data, err := os.ReadFile("/etc/os-release")
|
||||
if err != nil {
|
||||
log.Printf("Error reading /etc/os-release: %v", err)
|
||||
wdd_log.Error("读取 /etc/os-release 失败: %v", err)
|
||||
return NewOSInfo() // 返回默认值
|
||||
}
|
||||
|
||||
@@ -88,25 +106,27 @@ func getOSVersion() models.OSInfo {
|
||||
osInfo.VersionID = value
|
||||
case "PRETTY_NAME":
|
||||
osInfo.PrettyName = value
|
||||
case "HOME_URL":
|
||||
osInfo.HomeURL = value
|
||||
case "SUPPORT_URL":
|
||||
osInfo.SupportURL = value
|
||||
case "BUG_REPORT_URL":
|
||||
osInfo.BugReportURL = value
|
||||
case "PRIVACY_URL":
|
||||
osInfo.PrivacyURL = value
|
||||
}
|
||||
}
|
||||
|
||||
return osInfo
|
||||
}
|
||||
|
||||
// getMachineID 从 /etc/machine-id 文件中获取机器 ID
|
||||
// getMachineID 从 /sys/devices/virtual/dmi/id/product_uuid 文件中获取机器 ID
|
||||
func getMachineID() string {
|
||||
data, err := os.ReadFile("/etc/machine-id")
|
||||
data, err := os.ReadFile("/sys/devices/virtual/dmi/id/product_uuid")
|
||||
if err != nil {
|
||||
log.Printf("Error reading /etc/machine-id: %v", err)
|
||||
wdd_log.Error("读取 /sys/devices/virtual/dmi/id/product_uuid 失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data)) // 去除多余空格
|
||||
}
|
||||
|
||||
// getMachineID 从 /sys/devices/virtual/dmi/id/product_uuid 文件中获取机器 ID
|
||||
func getMachineSerial() string {
|
||||
data, err := os.ReadFile("/sys/devices/virtual/dmi/id/product_serial")
|
||||
if err != nil {
|
||||
wdd_log.Error("读取 /sys/devices/virtual/dmi/id/product_serial 失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data)) // 去除多余空格
|
||||
@@ -116,8 +136,8 @@ func getMachineID() string {
|
||||
func getKernelVersion() string {
|
||||
data, err := os.ReadFile("/proc/version")
|
||||
if err != nil {
|
||||
log.Printf("Error reading /proc/version: %v", err)
|
||||
wdd_log.Error("读取 /proc/version 失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmii-uav-watchdog-agent/host_info"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建一个默认的 Gin 路由
|
||||
var r = gin.Default() // 定义一个 GET 路由
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "pong"})
|
||||
})
|
||||
|
||||
// 定义一个 POST 路由
|
||||
r.POST("/echo", func(c *gin.Context) {
|
||||
var json map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&json); err == nil {
|
||||
c.JSON(http.StatusOK, json)
|
||||
} else {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
}
|
||||
})
|
||||
|
||||
r.GET("/cpu", func(c *gin.Context) {
|
||||
cpuInfo := services.GetCPUInfo() // 直接返回 CPU 信息
|
||||
c.JSON(http.StatusOK, cpuInfo)
|
||||
})
|
||||
|
||||
r.GET("/memory", func(c *gin.Context) {
|
||||
memInfo := services.GetMemoryInfo() // 直接返回内存信息
|
||||
c.JSON(http.StatusOK, memInfo)
|
||||
})
|
||||
|
||||
r.GET("/disk", func(c *gin.Context) {
|
||||
diskInfo := services.GetDiskInfo() // 直接返回磁盘信息
|
||||
c.JSON(http.StatusOK, diskInfo)
|
||||
})
|
||||
|
||||
r.GET("/motherboard", func(c *gin.Context) {
|
||||
mbInfo := services.GetMotherboardInfo() // 直接返回主板信息
|
||||
c.JSON(http.StatusOK, mbInfo)
|
||||
})
|
||||
|
||||
r.GET("/network", func(c *gin.Context) {
|
||||
networkInterfaces := services.GetNetworkInterfaces()
|
||||
c.JSON(http.StatusOK, networkInterfaces)
|
||||
})
|
||||
r.GET("/all", func(c *gin.Context) {
|
||||
allInfo := services.GetAllInfo()
|
||||
c.JSON(http.StatusOK, allInfo)
|
||||
})
|
||||
//r.GET("/phy", func(c *gin.Context) {
|
||||
// allInfo, _ := services.GetPVForLV()
|
||||
// c.JSON(http.StatusOK, allInfo)
|
||||
//})
|
||||
// 启动服务,监听在 8080 端口
|
||||
r.Run(":8098")
|
||||
// 等待终止信号
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigs
|
||||
fmt.Println("Shutting down service...")
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cmii-uav-watchdog-common/models"
|
||||
@@ -23,7 +24,8 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHeartbeatURL = "http://cmii-uav-watchdog/heartbeat"
|
||||
DefaultHeartbeatURL = "http://cmii-uav-watchdog:8080/api/heartbeat"
|
||||
// DefaultHeartbeatURL = "http://192.168.35.70:8990/api/heartbeat"
|
||||
)
|
||||
|
||||
// ClientOptions HTTP客户端配置选项
|
||||
@@ -48,8 +50,9 @@ func DefaultClientOptions() *ClientOptions {
|
||||
|
||||
// Client HTTP客户端封装
|
||||
type Client struct {
|
||||
httpClient *http.Client
|
||||
options *ClientOptions
|
||||
httpClient *http.Client
|
||||
options *ClientOptions
|
||||
heartbeatURL string // 心跳请求的URL地址
|
||||
}
|
||||
|
||||
// NewClient 创建一个新的HTTP客户端
|
||||
@@ -58,7 +61,7 @@ type Client struct {
|
||||
//
|
||||
// 返回:
|
||||
// - *Client: HTTP客户端实例
|
||||
func NewClient(options *ClientOptions) *Client {
|
||||
func NewClient(options *ClientOptions, heartbeatURL string) *Client {
|
||||
if options == nil {
|
||||
options = DefaultClientOptions()
|
||||
}
|
||||
@@ -75,10 +78,22 @@ func NewClient(options *ClientOptions) *Client {
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
return &Client{
|
||||
httpClient: httpClient,
|
||||
options: options,
|
||||
// 解析heartbeatURL
|
||||
heartbeatURL = strings.TrimSpace(heartbeatURL)
|
||||
if heartbeatURL == "" {
|
||||
heartbeatURL = DefaultHeartbeatURL
|
||||
}
|
||||
|
||||
return &Client{
|
||||
httpClient: httpClient,
|
||||
options: options,
|
||||
heartbeatURL: heartbeatURL,
|
||||
}
|
||||
}
|
||||
|
||||
// GetHeartbeatURL 获取心跳请求的URL地址
|
||||
func (c *Client) GetHeartbeatURL() string {
|
||||
return c.heartbeatURL
|
||||
}
|
||||
|
||||
// SendHeartbeat 发送心跳请求并处理响应
|
||||
@@ -114,7 +129,7 @@ func (c *Client) SendHeartbeat(ctx context.Context, request *models.HeartbeatReq
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, DefaultHeartbeatURL, bytes.NewBuffer(requestBody))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.heartbeatURL, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
lastError = fmt.Errorf("创建请求失败: %w", err)
|
||||
continue
|
||||
@@ -140,9 +155,37 @@ func (c *Client) SendHeartbeat(ctx context.Context, request *models.HeartbeatReq
|
||||
// 确保响应体被关闭
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查HTTP状态码
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
lastError = fmt.Errorf("%w: 状态码 %d", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 使用switch检查HTTP状态码
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
// 状态码为200,继续处理响应
|
||||
case http.StatusBadRequest:
|
||||
lastError = fmt.Errorf("%w: 状态码 %d,请求无效", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 读取并丢弃响应体,避免连接泄漏
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
continue
|
||||
case http.StatusUnauthorized:
|
||||
lastError = fmt.Errorf("%w: 状态码 %d,未授权", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 读取并丢弃响应体,避免连接泄漏
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
continue
|
||||
case http.StatusForbidden:
|
||||
lastError = fmt.Errorf("%w: 状态码 %d,禁止访问", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 读取并丢弃响应体,避免连接泄漏
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
continue
|
||||
case http.StatusNotFound:
|
||||
lastError = fmt.Errorf("%w: 状态码 %d,未找到资源", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 读取并丢弃响应体,避免连接泄漏
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
continue
|
||||
case http.StatusInternalServerError:
|
||||
lastError = fmt.Errorf("%w: 状态码 %d,服务器内部错误", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 读取并丢弃响应体,避免连接泄漏
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
continue
|
||||
default:
|
||||
lastError = fmt.Errorf("%w: 状态码 %d,未知错误", ErrInvalidStatusCode, resp.StatusCode)
|
||||
// 读取并丢弃响应体,避免连接泄漏
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
continue
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
package totp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TOTPConfig TOTP配置
|
||||
type TOTPConfig struct {
|
||||
Secret string // TOTP密钥
|
||||
Digits int // TOTP验证码长度
|
||||
TimeStep time.Duration // TOTP时间步长
|
||||
Algorithm string // TOTP算法
|
||||
}
|
||||
|
||||
var (
|
||||
defaultConfig = TOTPConfig{
|
||||
Secret: "",
|
||||
Digits: 6,
|
||||
TimeStep: 30 * time.Second,
|
||||
Algorithm: "SHA1",
|
||||
}
|
||||
mu sync.RWMutex
|
||||
)
|
||||
|
||||
// SetTOTPSecret 设置TOTP密钥
|
||||
func SetTOTPSecret(secret string) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
defaultConfig.Secret = secret
|
||||
}
|
||||
|
||||
// GetTOTPSecret 获取TOTP密钥
|
||||
func GetTOTPSecret() string {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
return defaultConfig.Secret
|
||||
}
|
||||
|
||||
// GenerateTOTPCode 生成TOTP验证码
|
||||
func GenerateTOTPCode() (string, error) {
|
||||
mu.RLock()
|
||||
config := defaultConfig
|
||||
mu.RUnlock()
|
||||
|
||||
if config.Secret == "" {
|
||||
return "", fmt.Errorf("TOTP密钥未设置")
|
||||
}
|
||||
|
||||
// 确保密钥是Base32编码的
|
||||
secret := strings.ToUpper(config.Secret)
|
||||
secret = strings.ReplaceAll(secret, " ", "")
|
||||
secretBytes, err := base32.StdEncoding.DecodeString(secret)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解码TOTP密钥失败: %w", err)
|
||||
}
|
||||
|
||||
// 获取当前时间戳并转换为TOTP时间计数器
|
||||
timeCounter := uint64(time.Now().Unix()) / uint64(config.TimeStep.Seconds())
|
||||
|
||||
// 将时间计数器转换为字节数组
|
||||
timeBytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(timeBytes, timeCounter)
|
||||
|
||||
// 使用HMAC-SHA1计算TOTP值
|
||||
h := hmac.New(sha1.New, secretBytes)
|
||||
h.Write(timeBytes)
|
||||
hash := h.Sum(nil)
|
||||
|
||||
// 根据RFC 6238,我们使用哈希的最后一个字节的低4位作为偏移
|
||||
offset := hash[len(hash)-1] & 0x0f
|
||||
|
||||
// 从哈希中提取4字节并转换为整数
|
||||
binary := binary.BigEndian.Uint32(hash[offset : offset+4])
|
||||
|
||||
// 屏蔽最高位并获取指定位数的数字
|
||||
totp := binary & 0x7fffffff % uint32(pow10(config.Digits))
|
||||
|
||||
// 将数字格式化为字符串,填充前导零
|
||||
return fmt.Sprintf("%0*d", config.Digits, totp), nil
|
||||
}
|
||||
|
||||
// ValidateTOTPCode 验证TOTP验证码
|
||||
func ValidateTOTPCode(code string) bool {
|
||||
generatedCode, err := GenerateTOTPCode()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return code == generatedCode
|
||||
}
|
||||
|
||||
// pow10 计算10的n次方
|
||||
func pow10(n int) int {
|
||||
result := 1
|
||||
for i := 0; i < n; i++ {
|
||||
result *= 10
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user