fix: clarify reconciliation and preserve container image tags
This commit is contained in:
@@ -190,6 +190,94 @@ func TestContainerUpdaterRejectsMissingCommittedContainer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestContainerUpdaterRestartsContainerWithLocalImage 验证 container 后端重启会复用当前已提交部署
|
||||
// 记录的镜像:轮转到非活跃槽位、健康检查通过后切流并停止旧容器,且整个过程不重新拉取镜像。
|
||||
func TestContainerUpdaterRestartsContainerWithLocalImage(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
updater, store, engine, gateway := newContainerUpdaterFixture(t, map[string]containerengine.Container{
|
||||
"backend-8080": {
|
||||
ID: "committed-backend-8080", Name: "backend-8080", Running: true,
|
||||
ImageReference: "harbor.ymswell.asia/ymswell/glory-ymswell@" + containerTestDigest,
|
||||
},
|
||||
}, 0)
|
||||
commitContainerDeployment(t, store, transaction.BackendContainerDeployment{
|
||||
ActivePort: 8080, ContainerName: "backend-8080", ImageDigest: containerTestDigest, ContainerID: "committed-backend-8080",
|
||||
})
|
||||
|
||||
record, err := updater.RestartContainer(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("restart container backend: %v", err)
|
||||
}
|
||||
if record.State != transaction.StateCommitted || gateway.snapshot.ActivePort != 8081 {
|
||||
t.Fatalf("unexpected committed container restart: record=%+v gateway=%+v", record, gateway.snapshot)
|
||||
}
|
||||
if engine.containers["backend-8080"].Running {
|
||||
t.Fatal("previous backend container is still running after restart")
|
||||
}
|
||||
if engine.pullCalls != 0 {
|
||||
t.Fatalf("restart must reuse the local image without pulling: pulls=%d", engine.pullCalls)
|
||||
}
|
||||
assertCommittedContainerDeployment(t, store, record.ID, 8081, "backend-8081", "container-id-backend-8081")
|
||||
}
|
||||
|
||||
// TestContainerUpdaterRestartRejectsWithoutDeployment 验证不存在已提交部署记录时,container 后端重启被拒绝。
|
||||
func TestContainerUpdaterRestartRejectsWithoutDeployment(t *testing.T) {
|
||||
updater, _, _, _ := newContainerUpdaterFixture(t, map[string]containerengine.Container{}, 0)
|
||||
|
||||
_, err := updater.RestartContainer(context.Background(), nil)
|
||||
if err == nil || !strings.Contains(err.Error(), "requires a committed deployment") {
|
||||
t.Fatalf("unexpected restart without deployment: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestContainerUpdaterRestartRejectsGatewayDrift 验证宿主 Nginx 活动端口与部署记录不一致时,container 后端重启被拒绝。
|
||||
func TestContainerUpdaterRestartRejectsGatewayDrift(t *testing.T) {
|
||||
updater, store, _, gateway := newContainerUpdaterFixture(t, map[string]containerengine.Container{
|
||||
"backend-8080": {
|
||||
ID: "committed-backend-8080", Name: "backend-8080", Running: true,
|
||||
ImageReference: "harbor.ymswell.asia/ymswell/glory-ymswell@" + containerTestDigest,
|
||||
},
|
||||
}, 0)
|
||||
commitContainerDeployment(t, store, transaction.BackendContainerDeployment{
|
||||
ActivePort: 8080, ContainerName: "backend-8080", ImageDigest: containerTestDigest, ContainerID: "committed-backend-8080",
|
||||
})
|
||||
gateway.snapshot.ActivePort = 8081
|
||||
|
||||
_, err := updater.RestartContainer(context.Background(), nil)
|
||||
if err == nil || !strings.Contains(err.Error(), "requires gateway active port 8080, got 8081") {
|
||||
t.Fatalf("unexpected restart with gateway drift: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// commitContainerDeployment 通过合法的状态机推进写入一条 container 部署记录,供重启测试预置现场。
|
||||
func commitContainerDeployment(t *testing.T, store *transaction.Store, deployment transaction.BackendContainerDeployment) {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
transactionID := "seed-" + deployment.ContainerName
|
||||
_, _, err := store.CreateTransaction(ctx, transaction.CreateRequest{
|
||||
ID: transactionID, IdempotencyKey: "backend:container:seed:" + deployment.ContainerName, Source: sourceLocalCLI,
|
||||
Service: serviceBackend, Request: []byte(`{"inputType":"container-image"}`),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create seed backend transaction: %v", err)
|
||||
}
|
||||
for _, state := range []transaction.State{
|
||||
transaction.StateValidating,
|
||||
transaction.StatePrepared,
|
||||
transaction.StateStarting,
|
||||
transaction.StateSwitching,
|
||||
transaction.StateVerifying,
|
||||
transaction.StateDraining,
|
||||
} {
|
||||
if _, err := store.Transition(ctx, transactionID, state, "seed committed deployment"); err != nil {
|
||||
t.Fatalf("transition seed backend transaction to %s: %v", state, err)
|
||||
}
|
||||
}
|
||||
if _, err := store.CommitBackendContainerDeployment(ctx, transactionID, deployment, "seed committed backend container deployment"); err != nil {
|
||||
t.Fatalf("commit seed backend deployment: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// newContainerUpdaterFixture 构造容器更新器的测试夹具,返回更新器、事务存储、假容器引擎和内存网关。
|
||||
// containers 指定引擎初始存在的容器;drain 指定旧容器停止前的排水等待时长。
|
||||
func newContainerUpdaterFixture(
|
||||
@@ -263,7 +351,7 @@ func newContainerUpdaterFixture(
|
||||
// 环境变量和挂载点均符合容器更新契约。
|
||||
func assertCreatedContainerSpec(t *testing.T, request containerengine.ContainerSpec) {
|
||||
t.Helper()
|
||||
if request.Name != "backend-8081" || request.ImageReference != "harbor.ymswell.asia/ymswell/glory-ymswell@"+containerTestDigest {
|
||||
if request.Name != "backend-8081" || request.ImageReference != containerTestImage {
|
||||
t.Fatalf("unexpected target container identity: %+v", request)
|
||||
}
|
||||
if request.NetworkMode != "host" || request.RestartPolicy.Name != "no" || request.User != "0:0" {
|
||||
@@ -325,13 +413,17 @@ type containerUpdateEngine struct {
|
||||
stopped []string
|
||||
lastCreateSpec containerengine.ContainerSpec
|
||||
healthChecks int
|
||||
pullCalls int
|
||||
}
|
||||
|
||||
// Ping 返回 nil,模拟引擎连通性检查始终成功。
|
||||
func (e *containerUpdateEngine) Ping(context.Context) error { return nil }
|
||||
|
||||
// PullImage 返回 nil,模拟镜像拉取始终成功。
|
||||
func (e *containerUpdateEngine) PullImage(context.Context, string) error { return nil }
|
||||
// PullImage 记录一次拉取调用并返回 nil,模拟镜像拉取始终成功。
|
||||
func (e *containerUpdateEngine) PullImage(context.Context, string) error {
|
||||
e.pullCalls++
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadImage 返回 nil,模拟从流加载镜像始终成功。
|
||||
func (e *containerUpdateEngine) LoadImage(context.Context, io.Reader) error { return nil }
|
||||
@@ -381,7 +473,7 @@ func (e *containerUpdateEngine) ContainerLogs(context.Context, string) (io.ReadC
|
||||
}
|
||||
|
||||
// StopContainer 记录被停止的容器名称并将该容器标记为已退出;若容器不存在则返回 containerengine.ErrNotFound。
|
||||
func (e *containerUpdateEngine) StopContainer(_ context.Context, name string) error {
|
||||
func (e *containerUpdateEngine) StopContainer(_ context.Context, name string, _ int) error {
|
||||
e.stopped = append(e.stopped, name)
|
||||
record, found := e.containers[name]
|
||||
if !found {
|
||||
|
||||
Reference in New Issue
Block a user