Files
yms-daemon/internal/backendstatus/native.go
T

138 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package backendstatus
import (
"context"
"errors"
"fmt"
"os"
"yms-daemon/internal/daemonapi"
"yms-daemon/internal/deploymentconfig"
"yms-daemon/internal/systemd"
)
// 诊断项稳定标识,供测试与 reconcile 识别,不参与展示。
const (
codeNativeActivePortOK = "native_nginx_active_port"
codeNativeActiveUnitOK = "native_active_unit_running"
codeNativeActiveUnitDown = "native_active_unit_not_running"
codeNativeInactiveUnitOK = "native_inactive_unit_stopped"
codeNativeInactiveRunning = "native_inactive_unit_running"
codeNativeSlotJAROK = "native_slot_jar_link"
codeNativeSlotJARDrift = "native_slot_jar_link_drift"
)
// diagnoseNative 对 native 后端做只读诊断。
// native 没有部署单例记录,事实来源是宿主 Nginx 活动端口与 systemd 单元现场状态。
func (d *Diagnoser) diagnoseNative(ctx context.Context) (daemonapi.Diagnosis, error) {
findings, err := d.nativeGather(ctx)
if err != nil {
return daemonapi.Diagnosis{}, err
}
return toDiagnosis("backend", deploymentconfig.BackendTypeNative, findings), nil
}
// nativeGather 生成 native 后端的全部诊断项。
func (d *Diagnoser) nativeGather(ctx context.Context) ([]finding, error) {
before, err := d.gateway.Read()
if err != nil {
return nil, fmt.Errorf("read host Nginx configuration: %w", err)
}
activePort := before.ActivePort
findings := make([]finding, 0, 6)
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelOK,
code: codeNativeActivePortOK,
message: fmt.Sprintf("宿主 Nginx 指向后端端口 %d", activePort),
})
activeSlot, err := d.config.Backend.SlotForPort(activePort)
if err != nil {
return nil, err
}
inactiveSlot, err := d.config.Backend.SlotForPort(otherPort(activePort))
if err != nil {
return nil, err
}
activeUnit, err := d.units.Inspect(ctx, activeSlot.Unit)
if err != nil {
return nil, fmt.Errorf("inspect native active unit %s: %w", activeSlot.Unit, err)
}
if unitRunning(activeUnit) {
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelOK,
code: codeNativeActiveUnitOK,
message: "活动单元 " + activeSlot.Unit + " 正在运行",
})
} else {
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelDrift,
code: codeNativeActiveUnitDown,
message: "活动单元 " + activeSlot.Unit + " 未运行(ActiveState=" + activeUnit.ActiveState + "",
})
}
inactiveUnit, err := d.units.Inspect(ctx, inactiveSlot.Unit)
if err != nil {
return nil, fmt.Errorf("inspect native inactive unit %s: %w", inactiveSlot.Unit, err)
}
if unitRunning(inactiveUnit) {
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelDrift,
code: codeNativeInactiveRunning,
message: "非活动单元 " + inactiveSlot.Unit + " 意外运行,两个槽位同时在线",
})
} else {
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelOK,
code: codeNativeInactiveUnitOK,
message: "非活动单元 " + inactiveSlot.Unit + " 已停止",
})
}
if ok, detail := jarLinkStatus(activeSlot.JAR); ok {
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelOK,
code: codeNativeSlotJAROK,
message: "活动槽位 JAR 链接有效:" + detail,
})
} else {
findings = append(findings, finding{
level: daemonapi.DiagnosisLevelDrift,
code: codeNativeSlotJARDrift,
message: "活动槽位 JAR 链接异常:" + detail,
})
}
return findings, nil
}
// unitRunning 判断 systemd 单元是否处于运行状态。
func unitRunning(unit systemd.Unit) bool {
return unit.ActiveState != "inactive" && unit.ActiveState != "failed"
}
// jarLinkStatus 检查槽位 JAR 路径是否为指向现存文件的符号链接。
// 返回 ok 与人类可读的详情;JAR 缺失、不是符号链接或目标不存在时返回 ok=false。
func jarLinkStatus(path string) (bool, string) {
info, err := os.Lstat(path)
if errors.Is(err, os.ErrNotExist) {
return false, "路径不存在"
}
if err != nil {
return false, "检查失败:" + err.Error()
}
if info.Mode()&os.ModeSymlink == 0 {
return false, "不是符号链接"
}
target, err := os.Readlink(path)
if err != nil {
return false, "读取链接目标失败:" + err.Error()
}
if _, err := os.Stat(path); err != nil {
return false, "链接目标不存在:" + target
}
return true, "指向 " + target
}