修复证书部署重试自冲突并补充真实装配回归
This commit is contained in:
parent
f2574e9508
commit
302111c8af
@ -199,6 +199,10 @@ func NewWithConfigPath(ctx context.Context, cfg config.Config, configPath string
|
||||
result, err := trackedWorkflow.DeployCurrent(ctx, domainID)
|
||||
return result.Certificate, result.Deployment, err
|
||||
},
|
||||
DeployCertificateTask: func(ctx context.Context, domainID string) (certificate.IssueResult, deployment.Report, error) {
|
||||
result, err := workflow.DeployCurrent(ctx, domainID)
|
||||
return result.Certificate, result.Deployment, err
|
||||
},
|
||||
RestoreCertificateVersion: func(ctx context.Context, domainID, version string) (certificate.IssueResult, error) {
|
||||
result, err := trackedWorkflow.Restore(ctx, domainID, version)
|
||||
return result.Certificate, err
|
||||
|
||||
132
backend/internal/app/certificate_retry_api_test.go
Normal file
132
backend/internal/app/certificate_retry_api_test.go
Normal file
@ -0,0 +1,132 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.pcux.cn/xuans/Scert/backend/internal/certificate"
|
||||
"git.pcux.cn/xuans/Scert/backend/internal/deployment"
|
||||
)
|
||||
|
||||
func TestApplicationDeployRetryUsesExistingTask(t *testing.T) {
|
||||
_, application := newCertificateTaskFixture(t)
|
||||
saved := workflowSavedCertificate(t)
|
||||
var calls atomic.Int32
|
||||
started := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
defer close(release)
|
||||
transactions := make(chan deployment.TransactionContext, 2)
|
||||
application.Workflow.CurrentCertificate = func(ctx context.Context, id string) (certificate.IssueResult, error) {
|
||||
transaction, _ := deployment.TransactionContextFromContext(ctx)
|
||||
transactions <- transaction
|
||||
if calls.Add(1) == 1 {
|
||||
return certificate.IssueResult{}, errors.New("首次部署故障")
|
||||
}
|
||||
close(started)
|
||||
select {
|
||||
case <-release:
|
||||
case <-ctx.Done():
|
||||
return certificate.IssueResult{}, ctx.Err()
|
||||
}
|
||||
return certificate.IssueResult{DomainID: id, Domains: []string{"example.com"}, Saved: saved}, nil
|
||||
}
|
||||
application.Workflow.Servers = workflowServerLister{servers: []deployment.Server{{ID: "server-1"}, {ID: "server-2"}}}
|
||||
application.Workflow.Deployment = &workflowDeployer{report: deployment.Report{
|
||||
Total: 2, Success: 2, Status: "success",
|
||||
Results: []deployment.Result{{ServerID: "server-1", Status: "success"}, {ServerID: "server-2", Status: "success"}},
|
||||
}}
|
||||
application.Workflow.Notifier = nil
|
||||
application.Workflow.RemoteVerifierForServer = func(context.Context, deployment.Server, string, []string) (certificate.ValidationResult, error) {
|
||||
return certificate.ValidationResult{FingerprintSHA256: saved.FingerprintSHA256}, nil
|
||||
}
|
||||
var cookies []*http.Cookie
|
||||
request := func(path, body string) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
for _, cookie := range cookies {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
response := httptest.NewRecorder()
|
||||
application.HTTP.Handler().ServeHTTP(response, req)
|
||||
return response
|
||||
}
|
||||
login := request("/api/v1/auth/login", `{"identifier":"admin","password":"Test1234!"}`)
|
||||
if login.Code != http.StatusOK {
|
||||
t.Fatalf("真实应用登录失败:%d %s", login.Code, login.Body.String())
|
||||
}
|
||||
cookies = login.Result().Cookies()
|
||||
initial := request("/api/v1/domains/task-domain/deploy", "")
|
||||
if initial.Code != http.StatusBadGateway || calls.Load() != 1 {
|
||||
t.Fatalf("首次故障未经过真实部署接线:%d %s", initial.Code, initial.Body.String())
|
||||
}
|
||||
tasks, err := application.Tasks.ListTasks(t.Context(), "task-domain", 10)
|
||||
if err != nil || len(tasks) != 1 || tasks[0].Status != "failed" {
|
||||
t.Fatalf("首次部署未生成唯一失败任务:%+v %v", tasks, err)
|
||||
}
|
||||
task := tasks[0]
|
||||
retryPath := "/api/v1/tasks/" + task.ID + "/retry"
|
||||
done := make(chan *httptest.ResponseRecorder, 1)
|
||||
go func() { done <- request(retryPath, "") }()
|
||||
select {
|
||||
case <-started:
|
||||
case response := <-done:
|
||||
t.Fatalf("重试未进入原始部署工作流:%d %s", response.Code, response.Body.String())
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("重试工作流启动超时")
|
||||
}
|
||||
concurrent := request(retryPath, "")
|
||||
if concurrent.Code != http.StatusConflict || calls.Load() != 2 {
|
||||
t.Fatalf("并发重试未被阻止:%d %s", concurrent.Code, concurrent.Body.String())
|
||||
}
|
||||
release <- struct{}{}
|
||||
select {
|
||||
case response := <-done:
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("真实应用部署重试失败:%d %s", response.Code, response.Body.String())
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("重试工作流收尾超时")
|
||||
}
|
||||
tasks, err = application.Tasks.ListTasks(t.Context(), "task-domain", 10)
|
||||
if err != nil || len(tasks) != 1 || tasks[0].ID != task.ID || tasks[0].Status != "success" {
|
||||
t.Fatalf("重试重复创建任务或未成功:%+v %v", tasks, err)
|
||||
}
|
||||
attempts, err := application.Tasks.ListAttempts(t.Context(), task.ID)
|
||||
if err != nil || len(attempts) != 2 {
|
||||
t.Fatalf("尝试数量错误:%+v %v", attempts, err)
|
||||
}
|
||||
for i, status := range []string{"failed", "success"} {
|
||||
attempt := attempts[i]
|
||||
transaction := <-transactions
|
||||
if attempt.Status != status || attempt.AttemptNumber != i+1 || attempt.FinishedAt == nil ||
|
||||
transaction.TaskID != task.ID || transaction.AttemptID != attempt.ID || transaction.AttemptNumber != i+1 {
|
||||
t.Fatalf("重试丢失原任务事务关联:%+v %+v", attempt, transaction)
|
||||
}
|
||||
}
|
||||
repeated := request(retryPath, "")
|
||||
results, err := application.Tasks.ListTaskDeploymentResults(t.Context(), task.ID)
|
||||
if err != nil || len(results) != 2 {
|
||||
t.Fatalf("双服务器结果未保存:%+v %v", results, err)
|
||||
}
|
||||
for _, result := range results {
|
||||
if result.AttemptID != attempts[1].ID || result.AttemptNumber != 2 || result.Status != "success" {
|
||||
t.Fatalf("服务器结果未关联重试尝试:%+v", result)
|
||||
}
|
||||
}
|
||||
var auditCount int
|
||||
err = application.Database.DB.QueryRowContext(t.Context(),
|
||||
"SELECT COUNT(*) FROM audit_logs WHERE task_id = ? AND action = ? AND operator_id = ? AND status IN ('running', 'success')",
|
||||
task.ID, "certificate_deploy_retry", task.OperatorID).Scan(&auditCount)
|
||||
if err != nil || auditCount != 2 {
|
||||
t.Fatalf("重试审计与操作者关联不完整:%d %v", auditCount, err)
|
||||
}
|
||||
if repeated.Code != http.StatusConflict || calls.Load() != 2 {
|
||||
t.Fatalf("成功任务仍被重复执行:%d %s", repeated.Code, repeated.Body.String())
|
||||
}
|
||||
}
|
||||
@ -40,29 +40,31 @@ type readinessSecretReader interface {
|
||||
}
|
||||
|
||||
type Dependencies struct {
|
||||
Config config.Config
|
||||
ConfigPath string
|
||||
FrontendDir string
|
||||
Logger *logging.Logger
|
||||
Repository repository.DomainRepository
|
||||
Servers repository.ServerRepository
|
||||
Users repository.UserRepository
|
||||
ProjectMembers repository.ProjectMemberRepository
|
||||
ProjectDirectory repository.ProjectDirectoryRepository
|
||||
ProjectLifecycle repository.ProjectLifecycleRepository
|
||||
ProjectResources repository.ProjectResourceReader
|
||||
ProjectBindings repository.ProjectBindingRepository
|
||||
ProjectDomains repository.ProjectDomainRepository
|
||||
SSHKeys *sshkey.Service
|
||||
DeploymentFactory deployment.ClientFactory
|
||||
SetupDNS setupDNSVerifier
|
||||
Secrets setupSecretWriter
|
||||
Firewall firewall.Manager
|
||||
IssueCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
IssueSetupCertificate func(context.Context, certificate.SetupCertificateRequest) (certificate.SavedResource, error)
|
||||
RollbackSetupCertificate func(context.Context, certificate.SavedResource) error
|
||||
ImportCertificate func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error)
|
||||
DeployCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
Config config.Config
|
||||
ConfigPath string
|
||||
FrontendDir string
|
||||
Logger *logging.Logger
|
||||
Repository repository.DomainRepository
|
||||
Servers repository.ServerRepository
|
||||
Users repository.UserRepository
|
||||
ProjectMembers repository.ProjectMemberRepository
|
||||
ProjectDirectory repository.ProjectDirectoryRepository
|
||||
ProjectLifecycle repository.ProjectLifecycleRepository
|
||||
ProjectResources repository.ProjectResourceReader
|
||||
ProjectBindings repository.ProjectBindingRepository
|
||||
ProjectDomains repository.ProjectDomainRepository
|
||||
SSHKeys *sshkey.Service
|
||||
DeploymentFactory deployment.ClientFactory
|
||||
SetupDNS setupDNSVerifier
|
||||
Secrets setupSecretWriter
|
||||
Firewall firewall.Manager
|
||||
IssueCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
IssueSetupCertificate func(context.Context, certificate.SetupCertificateRequest) (certificate.SavedResource, error)
|
||||
RollbackSetupCertificate func(context.Context, certificate.SavedResource) error
|
||||
ImportCertificate func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error)
|
||||
DeployCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
// DeployCertificateTask 执行已有任务,不得重复创建任务或尝试记录。
|
||||
DeployCertificateTask func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
RestoreCertificateVersion func(context.Context, string, string) (certificate.IssueResult, error)
|
||||
ValidateCertificate func(context.Context, string) (certificate.IssueResult, certificate.ValidationResult, error)
|
||||
|
||||
@ -101,6 +103,7 @@ type Server struct {
|
||||
rollbackSetupCertificate func(context.Context, certificate.SavedResource) error
|
||||
importCertificate func(context.Context, certificate.ImportRequest) (certificate.IssueResult, deployment.Report, error)
|
||||
deployCertificate func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
deployCertificateTask func(context.Context, string) (certificate.IssueResult, deployment.Report, error)
|
||||
restoreVersion func(context.Context, string, string) (certificate.IssueResult, error)
|
||||
validateCertificateWorkflow func(context.Context, string) (certificate.IssueResult, certificate.ValidationResult, error)
|
||||
|
||||
@ -165,6 +168,7 @@ func New(deps Dependencies) *Server {
|
||||
rollbackSetupCertificate: deps.RollbackSetupCertificate,
|
||||
importCertificate: deps.ImportCertificate,
|
||||
deployCertificate: deps.DeployCertificate,
|
||||
deployCertificateTask: deps.DeployCertificateTask,
|
||||
restoreVersion: deps.RestoreCertificateVersion,
|
||||
validateCertificateWorkflow: deps.ValidateCertificate,
|
||||
runRenewal: deps.RunRenewal,
|
||||
|
||||
@ -141,7 +141,7 @@ func (s *Server) executeCertificateIssueTask(c *gin.Context, task repository.Tas
|
||||
}
|
||||
|
||||
func (s *Server) executeCertificateDeployTask(c *gin.Context, task repository.Task, attempt repository.TaskAttempt, auditAction string) {
|
||||
s.executeCertificateTask(c, task, attempt, auditAction, s.deployCertificate, "certificate_deploy", "正在部署证书", "证书部署完成")
|
||||
s.executeCertificateTask(c, task, attempt, auditAction, s.deployCertificateTask, "certificate_deploy", "正在部署证书", "证书部署完成")
|
||||
}
|
||||
|
||||
func (s *Server) executeCertificateTask(c *gin.Context, task repository.Task, attempt repository.TaskAttempt, auditAction string, operation func(context.Context, string) (certificate.IssueResult, deployment.Report, error), stage, runningMessage, successMessage string) {
|
||||
@ -423,7 +423,7 @@ func (s *Server) retryTask(c *gin.Context) {
|
||||
}
|
||||
var runTask func(*gin.Context, repository.Task, repository.TaskAttempt, string)
|
||||
if item.TaskType == "certificate_deploy" {
|
||||
if s.deployCertificate == nil {
|
||||
if s.deployCertificateTask == nil {
|
||||
s.writeError(c, http.StatusNotImplemented, "证书部署接口尚未配置", errors.New("证书部署工作流未配置"))
|
||||
return
|
||||
}
|
||||
|
||||
@ -749,7 +749,7 @@ func TestTaskAPIRetriesFailedCertificateDeploymentWithoutDNSCredential(t *testin
|
||||
return certificate.IssueResult{}, deployment.Report{}, nil
|
||||
})
|
||||
defer closeDB()
|
||||
server.deployCertificate = func(_ context.Context, id string) (certificate.IssueResult, deployment.Report, error) {
|
||||
server.deployCertificateTask = func(_ context.Context, id string) (certificate.IssueResult, deployment.Report, error) {
|
||||
calls++
|
||||
return certificate.IssueResult{DomainID: id}, deployment.Report{Status: "success"}, nil
|
||||
}
|
||||
@ -792,12 +792,42 @@ func TestTaskAPIRetriesFailedCertificateDeploymentWithoutDNSCredential(t *testin
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskAPIDeployRetryRequiresUntrackedExecutor(t *testing.T) {
|
||||
server, tasks, _, closeDB := newTaskAPITestServer(t, nil)
|
||||
defer closeDB()
|
||||
server.deployCertificate = func(context.Context, string) (certificate.IssueResult, deployment.Report, error) {
|
||||
t.Fatal("已有任务不得回退到创建新任务的部署入口")
|
||||
return certificate.IssueResult{}, deployment.Report{}, nil
|
||||
}
|
||||
item := repository.Task{
|
||||
HistoryScope: repository.HistoryScope{ScopeType: repository.ScopeProject, ProjectID: domain.DefaultProjectID},
|
||||
ID: "deploy-missing-executor", DomainID: "domain-1", TaskType: "certificate_deploy",
|
||||
Status: "failed", Stage: "certificate_deploy",
|
||||
}
|
||||
if err := tasks.CreateTask(t.Context(), item); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response := httptest.NewRecorder()
|
||||
serveAdminHTTP(server, response, httptest.NewRequest(http.MethodPost, "/api/v1/tasks/"+item.ID+"/retry", nil))
|
||||
if response.Code != http.StatusNotImplemented {
|
||||
t.Fatalf("缺少已有任务执行器时应拒绝重试:%d %s", response.Code, response.Body.String())
|
||||
}
|
||||
stored, err := tasks.GetTask(t.Context(), item.ID)
|
||||
if err != nil || stored.Status != "failed" {
|
||||
t.Fatalf("执行器未配置时不应改变任务状态:%+v %v", stored, err)
|
||||
}
|
||||
attempts, err := tasks.ListAttempts(t.Context(), item.ID)
|
||||
if err != nil || len(attempts) != 0 {
|
||||
t.Fatalf("执行器未配置时不应增加尝试:%+v %v", attempts, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskAPIRetriesPartialCertificateDeployment(t *testing.T) {
|
||||
server, taskRepo, _, closeDB := newTaskAPITestServer(t, func(context.Context, string) (certificate.IssueResult, deployment.Report, error) {
|
||||
return certificate.IssueResult{}, deployment.Report{}, nil
|
||||
})
|
||||
defer closeDB()
|
||||
server.deployCertificate = func(_ context.Context, id string) (certificate.IssueResult, deployment.Report, error) {
|
||||
server.deployCertificateTask = func(_ context.Context, id string) (certificate.IssueResult, deployment.Report, error) {
|
||||
return certificate.IssueResult{DomainID: id}, deployment.Report{Status: "success"}, nil
|
||||
}
|
||||
finished := time.Now().UTC()
|
||||
|
||||
@ -1217,3 +1217,19 @@ git diff --check
|
||||
- [x] 已完成,等待复审:两台服务器交叉探测对方公网 `8443` 均超时;结合实际监听地址确认该内部端口未对外监听,未据此声称其他已有服务端口全部关闭。
|
||||
- [!] 边界说明:第一台内部管理服务沿用原有 IP 自签证书,使用测试域名和系统 CA 直连会失败;通过密码 SSH 核查证书及 IP SAN 后,以该证书作为显式信任文件访问回环上游返回 200,未关闭 TLS 校验。这仅是该机内部健康核验,不是第一台管理入口的公网受信 TLS 验收。
|
||||
- [!] 边界说明:本轮保留数据库,没有执行清库首次流程;未额外制作数据备份,官方安装器自身保留升级前发布文件以支持失败回退。完整生产验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
### 部署重试阻塞修复进度(2026-10-01 16:51,北京时间)
|
||||
|
||||
本节为本轮当前状态说明,前文保留为历史记录。P0-03 的局部代码复审状态不代表整个项目的生产验收状态;本轮上线范围仅 Linux,不将 Windows 列为阻塞。
|
||||
|
||||
- [✓] 复审通过:双机服务保持回环监听 127.0.0.1:8443,由 Nginx 80/443 对外提供服务;本轮未新增公网业务端口。
|
||||
- [x] 已完成,等待复审:通过正式部署接口制造第一台 Nginx 校验命令失败,两台部署报告整体失败,恢复报告为 success;随后已恢复正常校验命令,双机 nginx -t 通过。该验证使用同一证书重部署,不代表不同新旧证书切换验收。
|
||||
- [x] 已完成,等待复审:定位部署重试自冲突。HTTP 重试已创建尝试并标记原任务运行,但应用装配再次调用创建任务的工作流,将自身判为同类活动任务。新增真实应用登录与路由装配回归,修复前稳定返回 502。
|
||||
- [x] 已完成,等待复审:拆分普通部署与已有任务执行回调,普通部署继续创建任务,重试只执行原始证书部署流程,保留权限、审计、尝试记录与事务关联。
|
||||
- [x] 已完成,等待复审:定向回归验证唯一父任务、失败后成功的两次尝试、原任务事务关联、并发重试 409、成功后重复重试 409;后端 go test ./... 与 go vet ./... 通过。
|
||||
- [~] 进行中:竞态测试、干净提交构建、双机升级和真实失败任务重试复核。当前尚未取得修复后的实机成功证据。
|
||||
- [!] 暂缓或存在阻塞:历史恢复事务、Smail SMTP、数据库生产矩阵、备份恢复与最终清库首次部署门禁仍未闭环,不宣称生产验收通过,不给缺乏依据的完成百分比。
|
||||
|
||||
现场失败任务标识:683ef23a-c912-4f46-b2a6-3ec11908808e。本轮保留其数据用于复现与修复验证,不属于清库首次流程;没有直接改库伪造状态,也没有创建辅助验收脚本。
|
||||
|
||||
本轮补充回归:应用装配测试同时核对双服务器结果归属于第二次尝试,以及开始和成功审计的操作者关联;缺少已有任务执行器时返回 501,不回退到创建任务入口,不改变原任务状态。后端全量测试与 vet 再次通过;app/httpapi/repository 竞态测试通过,补充用例的定向竞态测试通过;前端 241 项通过。实机复测尚待新包部署。
|
||||
|
||||
@ -1529,3 +1529,19 @@ P0-05 从 `[!]` 调整为 `[~]`,并未标记完成;P0-04 剩余门禁和全
|
||||
- [x] 已完成,等待复审:两台服务器交叉探测对方公网 `8443` 均超时;结合实际监听地址确认该内部端口未对外监听,未据此声称其他已有服务端口全部关闭。
|
||||
- [!] 边界说明:第一台内部管理服务沿用原有 IP 自签证书,使用测试域名和系统 CA 直连会失败;通过密码 SSH 核查证书及 IP SAN 后,以该证书作为显式信任文件访问回环上游返回 200,未关闭 TLS 校验。这仅是该机内部健康核验,不是第一台管理入口的公网受信 TLS 验收。
|
||||
- [!] 边界说明:本轮保留数据库,没有执行清库首次流程;未额外制作数据备份,官方安装器自身保留升级前发布文件以支持失败回退。完整生产验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
### 部署重试阻塞修复进度(2026-10-01 16:51,北京时间)
|
||||
|
||||
本节为本轮当前状态说明,前文保留为历史记录。P0-03 的局部代码复审状态不代表整个项目的生产验收状态;本轮上线范围仅 Linux,不将 Windows 列为阻塞。
|
||||
|
||||
- [✓] 复审通过:双机服务保持回环监听 127.0.0.1:8443,由 Nginx 80/443 对外提供服务;本轮未新增公网业务端口。
|
||||
- [x] 已完成,等待复审:通过正式部署接口制造第一台 Nginx 校验命令失败,两台部署报告整体失败,恢复报告为 success;随后已恢复正常校验命令,双机 nginx -t 通过。该验证使用同一证书重部署,不代表不同新旧证书切换验收。
|
||||
- [x] 已完成,等待复审:定位部署重试自冲突。HTTP 重试已创建尝试并标记原任务运行,但应用装配再次调用创建任务的工作流,将自身判为同类活动任务。新增真实应用登录与路由装配回归,修复前稳定返回 502。
|
||||
- [x] 已完成,等待复审:拆分普通部署与已有任务执行回调,普通部署继续创建任务,重试只执行原始证书部署流程,保留权限、审计、尝试记录与事务关联。
|
||||
- [x] 已完成,等待复审:定向回归验证唯一父任务、失败后成功的两次尝试、原任务事务关联、并发重试 409、成功后重复重试 409;后端 go test ./... 与 go vet ./... 通过。
|
||||
- [~] 进行中:竞态测试、干净提交构建、双机升级和真实失败任务重试复核。当前尚未取得修复后的实机成功证据。
|
||||
- [!] 暂缓或存在阻塞:历史恢复事务、Smail SMTP、数据库生产矩阵、备份恢复与最终清库首次部署门禁仍未闭环,不宣称生产验收通过,不给缺乏依据的完成百分比。
|
||||
|
||||
现场失败任务标识:683ef23a-c912-4f46-b2a6-3ec11908808e。本轮保留其数据用于复现与修复验证,不属于清库首次流程;没有直接改库伪造状态,也没有创建辅助验收脚本。
|
||||
|
||||
本轮补充回归:应用装配测试同时核对双服务器结果归属于第二次尝试,以及开始和成功审计的操作者关联;缺少已有任务执行器时返回 501,不回退到创建任务入口,不改变原任务状态。后端全量测试与 vet 再次通过;app/httpapi/repository 竞态测试通过,补充用例的定向竞态测试通过;前端 241 项通过。实机复测尚待新包部署。
|
||||
|
||||
@ -2159,3 +2159,19 @@ P0-11 生产验收门禁
|
||||
- [x] 已完成,等待复审:两台服务器交叉探测对方公网 `8443` 均超时;结合实际监听地址确认该内部端口未对外监听,未据此声称其他已有服务端口全部关闭。
|
||||
- [!] 边界说明:第一台内部管理服务沿用原有 IP 自签证书,使用测试域名和系统 CA 直连会失败;通过密码 SSH 核查证书及 IP SAN 后,以该证书作为显式信任文件访问回环上游返回 200,未关闭 TLS 校验。这仅是该机内部健康核验,不是第一台管理入口的公网受信 TLS 验收。
|
||||
- [!] 边界说明:本轮保留数据库,没有执行清库首次流程;未额外制作数据备份,官方安装器自身保留升级前发布文件以支持失败回退。完整生产验收仍为 **P0-03:完成,待复审**。
|
||||
|
||||
### 部署重试阻塞修复进度(2026-10-01 16:51,北京时间)
|
||||
|
||||
本节为本轮当前状态说明,前文保留为历史记录。P0-03 的局部代码复审状态不代表整个项目的生产验收状态;本轮上线范围仅 Linux,不将 Windows 列为阻塞。
|
||||
|
||||
- [✓] 复审通过:双机服务保持回环监听 127.0.0.1:8443,由 Nginx 80/443 对外提供服务;本轮未新增公网业务端口。
|
||||
- [x] 已完成,等待复审:通过正式部署接口制造第一台 Nginx 校验命令失败,两台部署报告整体失败,恢复报告为 success;随后已恢复正常校验命令,双机 nginx -t 通过。该验证使用同一证书重部署,不代表不同新旧证书切换验收。
|
||||
- [x] 已完成,等待复审:定位部署重试自冲突。HTTP 重试已创建尝试并标记原任务运行,但应用装配再次调用创建任务的工作流,将自身判为同类活动任务。新增真实应用登录与路由装配回归,修复前稳定返回 502。
|
||||
- [x] 已完成,等待复审:拆分普通部署与已有任务执行回调,普通部署继续创建任务,重试只执行原始证书部署流程,保留权限、审计、尝试记录与事务关联。
|
||||
- [x] 已完成,等待复审:定向回归验证唯一父任务、失败后成功的两次尝试、原任务事务关联、并发重试 409、成功后重复重试 409;后端 go test ./... 与 go vet ./... 通过。
|
||||
- [~] 进行中:竞态测试、干净提交构建、双机升级和真实失败任务重试复核。当前尚未取得修复后的实机成功证据。
|
||||
- [!] 暂缓或存在阻塞:历史恢复事务、Smail SMTP、数据库生产矩阵、备份恢复与最终清库首次部署门禁仍未闭环,不宣称生产验收通过,不给缺乏依据的完成百分比。
|
||||
|
||||
现场失败任务标识:683ef23a-c912-4f46-b2a6-3ec11908808e。本轮保留其数据用于复现与修复验证,不属于清库首次流程;没有直接改库伪造状态,也没有创建辅助验收脚本。
|
||||
|
||||
本轮补充回归:应用装配测试同时核对双服务器结果归属于第二次尝试,以及开始和成功审计的操作者关联;缺少已有任务执行器时返回 501,不回退到创建任务入口,不改变原任务状态。后端全量测试与 vet 再次通过;app/httpapi/repository 竞态测试通过,补充用例的定向竞态测试通过;前端 241 项通过。实机复测尚待新包部署。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user