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
+49 -2
View File
@@ -20,9 +20,14 @@ import (
"yms-daemon/internal/transaction"
)
// containerTestImage 容器更新测试使用的镜像引用,指向 harbor 仓库的一个具体 tag。
const containerTestImage = "harbor.ymswell.asia/ymswell/glory-ymswell:20260813-184902-a37bf50d-v1.1.8.1"
// containerTestDigest 容器更新测试使用的镜像仓库摘要,用于与 containerTestImage 组合成不可变引用。
const containerTestDigest = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
// TestContainerUpdaterPullsSwitchesAndStopsPreviousSlot 验证在已存在运行中的 backend-8080 容器时,
// 更新镜像会切换到非活跃槽位 backend-8081、提交部署并停止旧容器 backend-8080。
func TestContainerUpdaterPullsSwitchesAndStopsPreviousSlot(t *testing.T) {
ctx := context.Background()
updater, store, engine, gateway := newContainerUpdaterFixture(t, map[string]containerengine.Container{
@@ -48,6 +53,9 @@ func TestContainerUpdaterPullsSwitchesAndStopsPreviousSlot(t *testing.T) {
assertCommittedContainerDeployment(t, store, record.ID, 8081, "backend-8081", "container-id-backend-8081")
}
// TestContainerUpdaterFirstInstallCreatesInactiveSlotBeforeSwitch 验证全新安装场景:
// 目标容器 backend-8081 必须先在非路由槽位创建并通过健康检查,之后才切换网关;
// 整个过程不得进入旧容器排水流程,也不得停止任何旧容器。
func TestContainerUpdaterFirstInstallCreatesInactiveSlotBeforeSwitch(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
@@ -92,6 +100,8 @@ func TestContainerUpdaterFirstInstallCreatesInactiveSlotBeforeSwitch(t *testing.
assertCommittedContainerDeployment(t, store, record.ID, 8081, "backend-8081", "container-id-backend-8081")
}
// TestContainerUpdaterRejectsMissingActiveWithPresentInactive 验证当活跃容器 backend-8080 缺失
// 而非活跃容器 backend-8081 仍在运行时,更新会被拒绝并返回明确的错误信息。
func TestContainerUpdaterRejectsMissingActiveWithPresentInactive(t *testing.T) {
updater, _, _, _ := newContainerUpdaterFixture(t, map[string]containerengine.Container{
"backend-8081": {
@@ -105,6 +115,9 @@ func TestContainerUpdaterRejectsMissingActiveWithPresentInactive(t *testing.T) {
}
}
// TestContainerUpdaterRecoversFailedRollbackThenRetriesSameImage 验证更新失败进入回滚后的恢复链路:
// 首次更新因网关切换失败而进入回滚并停止目标容器,再次调用恢复同一回滚直至完成,
// 最后重试同一镜像能够成功切换并提交。
func TestContainerUpdaterRecoversFailedRollbackThenRetriesSameImage(t *testing.T) {
ctx := context.Background()
updater, _, engine, gateway := newContainerUpdaterFixture(t, map[string]containerengine.Container{}, 0)
@@ -140,6 +153,8 @@ func TestContainerUpdaterRecoversFailedRollbackThenRetriesSameImage(t *testing.T
}
}
// TestContainerUpdaterRejectsMissingCommittedContainer 验证当已提交部署记录中的活跃容器 backend-8080 丢失时,
// 更新会将其识别为部署漂移并拒绝继续。
func TestContainerUpdaterRejectsMissingCommittedContainer(t *testing.T) {
ctx := context.Background()
updater, store, _, _ := newContainerUpdaterFixture(t, map[string]containerengine.Container{}, 0)
@@ -175,6 +190,8 @@ func TestContainerUpdaterRejectsMissingCommittedContainer(t *testing.T) {
}
}
// newContainerUpdaterFixture 构造容器更新器的测试夹具,返回更新器、事务存储、假容器引擎和内存网关。
// containers 指定引擎初始存在的容器;drain 指定旧容器停止前的排水等待时长。
func newContainerUpdaterFixture(
t *testing.T,
containers map[string]containerengine.Container,
@@ -242,6 +259,8 @@ func newContainerUpdaterFixture(
return updater, store, engine, gateway
}
// assertCreatedContainerSpec 校验传入容器引擎的创建规格,确认目标容器名称、镜像引用、运行时约束、
// 环境变量和挂载点均符合容器更新契约。
func assertCreatedContainerSpec(t *testing.T, request containerengine.ContainerSpec) {
t.Helper()
if request.Name != "backend-8081" || request.ImageReference != "harbor.ymswell.asia/ymswell/glory-ymswell@"+containerTestDigest {
@@ -258,6 +277,8 @@ func assertCreatedContainerSpec(t *testing.T, request containerengine.ContainerS
}
}
// assertCommittedContainerDeployment 校验事务存储中已提交的后端容器部署记录,
// 确认其活跃端口、容器名称、容器 ID、镜像摘要和事务 ID 均与预期一致。
func assertCommittedContainerDeployment(
t *testing.T,
store *transaction.Store,
@@ -276,6 +297,7 @@ func assertCommittedContainerDeployment(
}
}
// serverConfiguration8080 测试用的 host Nginx 配置片段,其活跃后端端口为 8080。
const serverConfiguration8080 = `http {
upstream yms-server {
# yms-update managed upstream begin
@@ -286,12 +308,17 @@ const serverConfiguration8080 = `http {
}
`
// containerUpdateRoundTripFunc 将普通函数适配为 http.RoundTripper
// 使测试可以用自定义逻辑响应后端的健康检查请求。
type containerUpdateRoundTripFunc func(*http.Request) (*http.Response, error)
// RoundTrip 实现 http.RoundTripper 接口,直接委托给底层函数处理请求。
func (function containerUpdateRoundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) {
return function(request)
}
// containerUpdateEngine 容器引擎的测试替身,在内存中模拟镜像与容器的生命周期,
// 并记录停止、创建和健康检查等交互,供断言验证更新行为。
type containerUpdateEngine struct {
image containerengine.Image
containers map[string]containerengine.Container
@@ -300,12 +327,21 @@ type containerUpdateEngine struct {
healthChecks int
}
func (e *containerUpdateEngine) Ping(context.Context) error { return nil }
func (e *containerUpdateEngine) PullImage(context.Context, string) error { return nil }
// Ping 返回 nil,模拟引擎连通性检查始终成功。
func (e *containerUpdateEngine) Ping(context.Context) error { return nil }
// PullImage 返回 nil,模拟镜像拉取始终成功。
func (e *containerUpdateEngine) PullImage(context.Context, string) error { return nil }
// LoadImage 返回 nil,模拟从流加载镜像始终成功。
func (e *containerUpdateEngine) LoadImage(context.Context, io.Reader) error { return nil }
// InspectImage 返回夹具预设的镜像信息,用于冻结镜像的平台与仓库摘要。
func (e *containerUpdateEngine) InspectImage(context.Context, string) (containerengine.Image, error) {
return e.image, nil
}
// CreateContainer 依据规格在内存中登记一个新容器,记录最后一次创建规格并返回该容器。
func (e *containerUpdateEngine) CreateContainer(_ context.Context, spec containerengine.ContainerSpec) (containerengine.Container, error) {
e.lastCreateSpec = spec
record := containerengine.Container{
@@ -326,6 +362,8 @@ func (e *containerUpdateEngine) CreateContainer(_ context.Context, spec containe
e.containers[spec.Name] = record
return record, nil
}
// StartContainer 将指定容器标记为运行中;若容器不存在则返回 containerengine.ErrNotFound。
func (e *containerUpdateEngine) StartContainer(_ context.Context, name string) error {
record, found := e.containers[name]
if !found {
@@ -337,9 +375,12 @@ func (e *containerUpdateEngine) StartContainer(_ context.Context, name string) e
return nil
}
// ContainerLogs 返回空的日志流,模拟容器日志读取。
func (e *containerUpdateEngine) ContainerLogs(context.Context, string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), nil
}
// StopContainer 记录被停止的容器名称并将该容器标记为已退出;若容器不存在则返回 containerengine.ErrNotFound。
func (e *containerUpdateEngine) StopContainer(_ context.Context, name string) error {
e.stopped = append(e.stopped, name)
record, found := e.containers[name]
@@ -351,6 +392,8 @@ func (e *containerUpdateEngine) StopContainer(_ context.Context, name string) er
e.containers[name] = record
return nil
}
// InspectContainer 返回内存中的指定容器;若容器不存在则返回 containerengine.ErrNotFound。
func (e *containerUpdateEngine) InspectContainer(_ context.Context, name string) (containerengine.Container, error) {
record, found := e.containers[name]
if !found {
@@ -358,6 +401,8 @@ func (e *containerUpdateEngine) InspectContainer(_ context.Context, name string)
}
return record, nil
}
// RemoveContainer 从内存中删除指定容器;若容器不存在则返回 containerengine.ErrNotFound。
func (e *containerUpdateEngine) RemoveContainer(_ context.Context, name string, _ bool) error {
if _, found := e.containers[name]; !found {
return containerengine.ErrNotFound
@@ -365,6 +410,8 @@ func (e *containerUpdateEngine) RemoveContainer(_ context.Context, name string,
delete(e.containers, name)
return nil
}
// Close 返回 nil,模拟引擎关闭无副作用。
func (e *containerUpdateEngine) Close() error { return nil }
var _ containerengine.Engine = (*containerUpdateEngine)(nil)