split container update preparation
This commit is contained in:
@@ -192,5 +192,6 @@
|
||||
- daemon server 将请求读取、进度回调、更新/重启分派、诊断分派拆开,保留协议错误文本与响应行为。
|
||||
- `runServe` 将 native/container 运行时构建与资源关闭拆出,避免入口函数承担全部分支。
|
||||
- 容器槽位现场核对拆为容器检查、已提交部署校验和活动槽位选择,减少单函数分支嵌套。
|
||||
- 容器镜像更新拆为输入校验、事务恢复、镜像拉取、槽位准备和事务创建阶段,行为与幂等键保持不变。
|
||||
|
||||
验证:`GOCACHE=/tmp/yms-go-cache go test ./...`、`git diff --check` 通过。
|
||||
|
||||
@@ -72,85 +72,111 @@ type persistedContainerRequest struct {
|
||||
// 是否转发容器启动日志;report 用于回传实时进度,可为 nil。返回值为本次更新
|
||||
// 对应的事务记录以及错误。若已存在同镜像的活动事务则复用续跑。
|
||||
func (u *Updater) UpdateContainerImage(ctx context.Context, imageReference string, startLog bool, report ProgressReporter) (transaction.Transaction, error) {
|
||||
if err := u.validateImageUpdate(imageReference); err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
}
|
||||
if record, request, resumed, err := u.resumeContainerImage(ctx, imageReference, report); resumed || err != nil {
|
||||
return u.finishResumedContainer(ctx, record, request, err, report)
|
||||
}
|
||||
resolved, err := u.pullImageForUpdate(ctx, imageReference, report)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
}
|
||||
prepared, err := u.prepareContainerImage(ctx, imageReference, resolved, startLog)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
}
|
||||
return u.createContainerImageTransaction(ctx, prepared, report)
|
||||
}
|
||||
|
||||
func (u *Updater) validateImageUpdate(imageReference string) error {
|
||||
if u.containerExecutor == nil || u.engine == nil {
|
||||
return transaction.Transaction{}, errors.New("container backend updater is not configured")
|
||||
return errors.New("container backend updater is not configured")
|
||||
}
|
||||
if strings.TrimSpace(imageReference) != imageReference || imageReference == "" {
|
||||
return transaction.Transaction{}, errors.New("exact container image reference is required")
|
||||
}
|
||||
if err := u.validateContainerInputs(); err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return errors.New("exact container image reference is required")
|
||||
}
|
||||
return u.validateContainerInputs()
|
||||
}
|
||||
|
||||
func (u *Updater) resumeContainerImage(ctx context.Context, imageReference string, report ProgressReporter) (transaction.Transaction, persistedContainerRequest, bool, error) {
|
||||
active, err := u.store.ActiveTransaction(ctx)
|
||||
if err == nil {
|
||||
var request persistedContainerRequest
|
||||
if decodeErr := decodeContainerRequest(active.Request, &request); decodeErr != nil {
|
||||
return active, decodeErr
|
||||
}
|
||||
if active.Service != serviceBackend || request.InputType != inputTypeContainerImage {
|
||||
return active, &transaction.ActiveTransactionError{TransactionID: active.ID}
|
||||
}
|
||||
if request.ImageReference != imageReference {
|
||||
return active, &transaction.ActiveTransactionError{TransactionID: active.ID}
|
||||
}
|
||||
return u.runContainerUpdate(ctx, active, request, false, report)
|
||||
if errors.Is(err, transaction.ErrNotFound) {
|
||||
return transaction.Transaction{}, persistedContainerRequest{}, false, nil
|
||||
}
|
||||
if !errors.Is(err, transaction.ErrNotFound) {
|
||||
return transaction.Transaction{}, err
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, persistedContainerRequest{}, true, err
|
||||
}
|
||||
var request persistedContainerRequest
|
||||
if err := decodeContainerRequest(active.Request, &request); err != nil {
|
||||
return active, request, true, err
|
||||
}
|
||||
if active.Service != serviceBackend || request.InputType != inputTypeContainerImage || request.ImageReference != imageReference {
|
||||
return active, request, true, &transaction.ActiveTransactionError{TransactionID: active.ID}
|
||||
}
|
||||
return active, request, true, nil
|
||||
}
|
||||
|
||||
func (u *Updater) finishResumedContainer(ctx context.Context, record transaction.Transaction, request persistedContainerRequest, err error, report ProgressReporter) (transaction.Transaction, error) {
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
return u.runContainerUpdate(ctx, record, request, false, report)
|
||||
}
|
||||
|
||||
func (u *Updater) pullImageForUpdate(ctx context.Context, imageReference string, report ProgressReporter) (resolvedImage, error) {
|
||||
reportProgress(report, Progress{Message: "Pulling backend image " + imageReference})
|
||||
resolved, err := u.pullAndResolveImage(ctx, imageReference)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
if err == nil {
|
||||
reportProgress(report, Progress{Message: "Backend image resolved: " + resolved.ImmutableReference})
|
||||
}
|
||||
reportProgress(report, Progress{Message: "Backend image resolved: " + resolved.ImmutableReference})
|
||||
return resolved, err
|
||||
}
|
||||
|
||||
type preparedContainerImage struct {
|
||||
transactionID string
|
||||
request persistedContainerRequest
|
||||
resolved resolvedImage
|
||||
}
|
||||
|
||||
func (u *Updater) prepareContainerImage(ctx context.Context, imageReference string, resolved resolvedImage, startLog bool) (preparedContainerImage, error) {
|
||||
before, err := u.gateway.Read()
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
activeSlot, err := u.config.Backend.SlotForPort(before.ActivePort)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
deployment, deploymentErr := u.store.BackendContainerDeployment(ctx)
|
||||
hasDeployment := deploymentErr == nil
|
||||
if deploymentErr != nil && !errors.Is(deploymentErr, transaction.ErrNotFound) {
|
||||
return transaction.Transaction{}, deploymentErr
|
||||
return preparedContainerImage{}, deploymentErr
|
||||
}
|
||||
hasHistory, err := u.store.HasCommittedBackendContainerTransactionHistory(ctx)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
targetPort, targetSlot, previousContainer, err := u.resolveContainerSlots(
|
||||
ctx,
|
||||
before.ActivePort,
|
||||
activeSlot,
|
||||
deployment,
|
||||
hasDeployment,
|
||||
hasHistory,
|
||||
)
|
||||
targetPort, targetSlot, previousContainer, err := u.resolveContainerSlots(ctx, before.ActivePort, activeSlot, deployment, hasDeployment, hasHistory)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
afterContent, err := hostnginx.RenderBackendPort(before.Content, targetPort)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
transactionID := rand.Text()
|
||||
transactionRoot := filepath.Join(u.workRoot, transactionID)
|
||||
if err := os.MkdirAll(transactionRoot, 0o750); err != nil {
|
||||
return transaction.Transaction{}, fmt.Errorf("create container backend transaction directory: %w", err)
|
||||
return preparedContainerImage{}, fmt.Errorf("create container backend transaction directory: %w", err)
|
||||
}
|
||||
beforePath := filepath.Join(transactionRoot, "gateway.before.conf")
|
||||
afterPath := filepath.Join(transactionRoot, "gateway.after.conf")
|
||||
if err := writeImmutableFile(beforePath, before.Content, 0o640); err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
if err := writeImmutableFile(afterPath, afterContent, 0o640); err != nil {
|
||||
return transaction.Transaction{}, err
|
||||
return preparedContainerImage{}, err
|
||||
}
|
||||
request := persistedContainerRequest{
|
||||
InputType: inputTypeContainerImage, ImageReference: imageReference,
|
||||
@@ -162,12 +188,17 @@ func (u *Updater) UpdateContainerImage(ctx context.Context, imageReference strin
|
||||
GatewayBeforePath: beforePath, GatewayAfterPath: afterPath,
|
||||
GatewayReceiptPath: filepath.Join(transactionRoot, "gateway.applied"),
|
||||
}
|
||||
return preparedContainerImage{transactionID: transactionID, request: request, resolved: resolved}, nil
|
||||
}
|
||||
|
||||
func (u *Updater) createContainerImageTransaction(ctx context.Context, prepared preparedContainerImage, report ProgressReporter) (transaction.Transaction, error) {
|
||||
request, resolved := prepared.request, prepared.resolved
|
||||
content, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return transaction.Transaction{}, fmt.Errorf("encode container backend update request: %w", err)
|
||||
}
|
||||
record, created, err := u.store.CreateTransaction(ctx, transaction.CreateRequest{
|
||||
ID: transactionID, IdempotencyKey: serviceBackend + ":container:" + resolved.Digest,
|
||||
ID: prepared.transactionID, IdempotencyKey: serviceBackend + ":container:" + resolved.Digest,
|
||||
Source: sourceLocalCLI, Service: serviceBackend, Request: content,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user