refactor: use nginx -s reload instead of systemd

- doc: add comment
This commit is contained in:
2026-08-17 10:10:14 +08:00
parent 79f18fcdea
commit f536987a7e
62 changed files with 2216 additions and 598 deletions
+93 -36
View File
@@ -1,5 +1,6 @@
// Package deploymentconfig loads the daemon's explicit local deployment configuration.
// It never infers deployment type, executable paths, unit names, slot paths, or endpoints.
// Package deploymentconfig 负责加载并严格校验 daemon 显式给出的本地部署配置。
// 该包绝不推断部署类型、可执行文件路径、systemd 单元名称、槽位路径或健康端点,
// 上述部署契约必须全部由配置文件显式提供,缺失或与内置契约不符都会返回错误。
package deploymentconfig
import (
@@ -16,77 +17,118 @@ import (
)
const (
// DefaultPath is the only default location used by the daemon entrypoint.
// DefaultPath daemon 入口唯一使用的默认配置路径。
DefaultPath = "/etc/yms-daemon/yms-daemon.toml"
EnvironmentDev = "dev"
// EnvironmentDev 表示开发环境。
EnvironmentDev = "dev"
// EnvironmentProd 表示生产环境。
EnvironmentProd = "prod"
BackendTypeNative = "native"
// BackendTypeNative 表示原生后端运行时(systemd 单元 + JAR 文件)。
BackendTypeNative = "native"
// BackendTypeContainer 表示容器后端运行时。
BackendTypeContainer = "container"
// BackendPort8080 蓝绿发布中 8080 槽位的端口号。
BackendPort8080 = 8080
// BackendPort8081 蓝绿发布中 8081 槽位的端口号。
BackendPort8081 = 8081
)
const (
nativeReleaseDir = "/home/yms/lib/releases"
nativeActiveJAR = "/home/yms/lib/glory-soft-yms.jar"
nativeUnit8080 = "yms-backend@8080.service"
nativeUnit8081 = "yms-backend@8081.service"
nativeSlotJAR8080 = "/home/yms/lib/glory-soft-yms-8080.jar"
nativeSlotJAR8081 = "/home/yms/lib/glory-soft-yms-8081.jar"
// nativeReleaseDir 原生后端发布包(JAR)的固定存放目录。
nativeReleaseDir = "/home/yms/lib/releases"
// nativeActiveJAR 原生后端当前生效的主 JAR 路径。
nativeActiveJAR = "/home/yms/lib/glory-soft-yms.jar"
// nativeUnit8080 8080 槽位对应的 systemd 服务单元名称。
nativeUnit8080 = "yms-backend@8080.service"
// nativeUnit8081 8081 槽位对应的 systemd 服务单元名称。
nativeUnit8081 = "yms-backend@8081.service"
// nativeSlotJAR8080 8080 槽位对应的 JAR 文件路径。
nativeSlotJAR8080 = "/home/yms/lib/glory-soft-yms-8080.jar"
// nativeSlotJAR8081 8081 槽位对应的 JAR 文件路径。
nativeSlotJAR8081 = "/home/yms/lib/glory-soft-yms-8081.jar"
// nativeHealthURL8080 8080 槽位的健康检查端点地址。
nativeHealthURL8080 = "http://127.0.0.1:8080/yms/actuator/health"
// nativeHealthURL8081 8081 槽位的健康检查端点地址。
nativeHealthURL8081 = "http://127.0.0.1:8081/yms/actuator/health"
containerName8080 = "backend-8080"
containerName8081 = "backend-8081"
// containerName8080 容器后端 8080 槽位的容器名称。
containerName8080 = "backend-8080"
// containerName8081 容器后端 8081 槽位的容器名称。
containerName8081 = "backend-8081"
)
const (
ContainerConfigSource = "/home/yms/conf/yms.yaml"
ContainerConfigTarget = "/app/config/yms.yaml"
ContainerTmpSource = "/home/yms/tmp"
ContainerTmpTarget = "/home/yms/tmp"
ContainerPortEnvironment = "SERVER_PORT"
// ContainerConfigSource 容器后端配置在宿主侧的源路径。
ContainerConfigSource = "/home/yms/conf/yms.yaml"
// ContainerConfigTarget 容器后端配置在容器内的目标路径。
ContainerConfigTarget = "/app/config/yms.yaml"
// ContainerTmpSource 容器后端临时目录在宿主侧的源路径。
ContainerTmpSource = "/home/yms/tmp"
// ContainerTmpTarget 容器后端临时目录在容器内的目标路径。
ContainerTmpTarget = "/home/yms/tmp"
// ContainerPortEnvironment 用于向容器传递后端端口的环境变量名。
ContainerPortEnvironment = "SERVER_PORT"
// ContainerConfigEnvironment 用于向容器传递 Spring 配置位置的环境变量名。
ContainerConfigEnvironment = "SPRING_CONFIG_LOCATION"
ContainerConfigLocation = "file:/app/config/yms.yaml"
// ContainerConfigLocation 容器内 Spring 配置文件的 file: 地址。
ContainerConfigLocation = "file:/app/config/yms.yaml"
)
// Config is the complete local deployment configuration currently understood by the daemon.
// Config daemon 当前能够理解的完整本地部署配置。
// 其结构与 TOML 文件中的 daemon、backend 两张顶层表一一对应,加载时做严格解码与校验。
type Config struct {
Daemon Daemon `toml:"daemon"`
// Daemon 记录与环境相关、与具体运行时无关的机器级配置。
Daemon Daemon `toml:"daemon"`
// Backend 记录显式选定的后端运行时及其蓝绿槽位。
Backend Backend `toml:"backend"`
}
// Daemon contains machine-wide behavior that is independent of a component runtime.
// Daemon 描述与具体组件运行时无关的机器级行为。
type Daemon struct {
// Environment 必须是 EnvironmentDev 或 EnvironmentProd 之一。
Environment string `toml:"environment"`
}
// Backend describes the explicitly selected backend runtime and its blue/green slots.
// Backend 描述显式选定的后端运行时类型及其蓝绿槽位。
type Backend struct {
Type string `toml:"type"`
ReleaseDir string `toml:"release_dir"`
ActiveJAR string `toml:"active_jar"`
SystemctlPath string `toml:"systemctl_path"`
Slot BackendSlots `toml:"slot"`
// Type 必须是 BackendTypeNative 或 BackendTypeContainer 之一。
Type string `toml:"type"`
// ReleaseDir 仅原生后端使用,必须是 nativeReleaseDir 对应的绝对路径。
ReleaseDir string `toml:"release_dir"`
// ActiveJAR 仅原生后端使用,必须是 nativeActiveJAR 对应的绝对路径。
ActiveJAR string `toml:"active_jar"`
// SystemctlPath systemctl 可执行文件的绝对路径。
SystemctlPath string `toml:"systemctl_path"`
// Slot 包含 8080、8081 两个受支持槽位的精确取值。
Slot BackendSlots `toml:"slot"`
}
// BackendSlots lists the only backend ports supported by the current deployment contract.
// BackendSlots 列出当前部署契约唯一支持的两个后端端口槽位。
type BackendSlots struct {
// Port8080 8080 端口的槽位配置。
Port8080 BackendSlot `toml:"8080"`
// Port8081 8081 端口的槽位配置。
Port8081 BackendSlot `toml:"8081"`
}
// BackendSlot contains exact values for one native or container backend slot.
// BackendSlot 包含一个原生或容器后端槽位的精确取值。
// 不同运行时类型下使用的字段不同:原生后端使用 Unit、JAR,容器后端使用 ContainerName。
type BackendSlot struct {
Unit string `toml:"unit"`
JAR string `toml:"jar"`
ContainerName string `toml:"container_name"`
// Unit 原生后端槽位对应的 systemd 服务单元名称。
Unit string `toml:"unit"`
// JAR 原生后端槽位对应的 JAR 文件绝对路径。
JAR string `toml:"jar"`
// ContainerName 容器后端槽位对应的容器名称。
ContainerName string `toml:"container_name"`
// HealthEndpoint 槽位健康检查的 URL 地址。
HealthEndpoint string `toml:"health_endpoint"`
}
// Load opens path, performs strict TOML decoding, and validates the selected backend contract.
// Load 打开 path 指向的部署配置文件,进行严格的 TOML 解码与契约校验,返回解析后的 Config。
// 它要求 path 是绝对路径且为普通文件,解码时禁用未知字段(任何大小写、拼写或层级不一致的
// 键都会被拒绝),解码后调用 Validate 精确校验取值。任一步失败都会返回带上下文的 error。
func Load(path string) (Config, error) {
if err := validateAbsolutePath("deployment configuration", path); err != nil {
return Config{}, err
@@ -124,6 +166,8 @@ func Load(path string) (Config, error) {
return config, nil
}
// validateExactDocumentKeys 对原始 TOML 文档做逐层键名校验,确保 daemon、backend 及
// backend.slot 下每一张表都只包含契约允许的确切字段,任何未知字段(大小写、拼写或层级不符)都会被拒绝。
func validateExactDocumentKeys(document []byte) error {
var root map[string]any
if err := toml.Unmarshal(document, &root); err != nil {
@@ -165,6 +209,8 @@ func validateExactDocumentKeys(document []byte) error {
return nil
}
// exactTable 从 table 中取出名为 key 的子表并返回;若 key 不存在或对应值不是 TOML 表,
// 则返回错误。parent 用于拼出带前缀的完整字段名,以生成可读的错误信息。
func exactTable(table map[string]any, parent string, key string) (map[string]any, error) {
value, found := table[key]
field := key
@@ -181,6 +227,8 @@ func exactTable(table map[string]any, parent string, key string) (map[string]any
return nested, nil
}
// rejectUnknownKeys 检查 table 是否只包含 allowed 中列出的键;存在其他键时,按字典序
// 返回第一个未知键的拒绝错误。parent 用于拼出带前缀的完整字段名。
func rejectUnknownKeys(table map[string]any, parent string, allowed ...string) error {
known := make(map[string]struct{}, len(allowed))
for _, key := range allowed {
@@ -204,7 +252,9 @@ func rejectUnknownKeys(table map[string]any, parent string, allowed ...string) e
return nil
}
// Validate rejects incomplete or altered local deployment identifiers.
// Validate 拒绝不完整或与内置部署契约不一致的本地部署标识符。
// 它依次校验 daemon.environment、backend.systemctl_path 的绝对路径约束,再按 backend.type
// 分支精确校验原生或容器槽位的 unit、jar、container_name、health_endpoint 等字段。
func (c Config) Validate() error {
switch c.Daemon.Environment {
case EnvironmentDev, EnvironmentProd:
@@ -250,7 +300,8 @@ func (c Config) Validate() error {
return nil
}
// Slot returns the exact configuration for one supported backend port.
// SlotForPort 返回 port 对应的后端槽位配置,仅支持 BackendPort8080 或 BackendPort8081
// 其他端口返回带端口号的错误。
func (b Backend) SlotForPort(port int) (BackendSlot, error) {
switch port {
case BackendPort8080:
@@ -262,6 +313,8 @@ func (b Backend) SlotForPort(port int) (BackendSlot, error) {
}
}
// validateNativeSlot 校验原生槽位 slot 的 Unit、JAR、HealthEndpoint 是否分别等于
// unit、jar、endpoint,任一不符即返回带 field 前缀的错误。
func validateNativeSlot(field string, slot BackendSlot, unit string, jar string, endpoint string) error {
if slot.Unit != unit {
return fmt.Errorf("%s.unit must be %q", field, unit)
@@ -275,6 +328,8 @@ func validateNativeSlot(field string, slot BackendSlot, unit string, jar string,
return nil
}
// validateContainerSlot 校验容器槽位 slot 的 ContainerName、HealthEndpoint 是否分别等于
// containerName、endpoint,任一不符即返回带 field 前缀的错误。
func validateContainerSlot(field string, slot BackendSlot, containerName string, endpoint string) error {
if slot.ContainerName != containerName {
return fmt.Errorf("%s.container_name must be %q", field, containerName)
@@ -285,6 +340,8 @@ func validateContainerSlot(field string, slot BackendSlot, containerName string,
return nil
}
// validateAbsolutePath 校验 value 是一个非空、无首尾空白、经过 Clean 且不含 NUL 字节的绝对路径,
// 用于保证配置中的路径标识符不会被意外篡改。field 用于拼出错误信息。
func validateAbsolutePath(field string, value string) error {
if value == "" {
return fmt.Errorf("%s is required", field)