feat(ota): enhance Docker upgrade process with progress tracking

This commit is contained in:
Jacky 2025-04-21 13:57:49 +00:00
parent d0cf93d5e3
commit ff7f2e4f73
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
4 changed files with 91 additions and 12 deletions

View file

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/base:jammy
FROM mcr.microsoft.com/devcontainers/base:noble
# Combine installation steps for Nginx and Go to avoid repetitive update/cleanup commands
RUN apt-get update && \

View file

@ -2,6 +2,7 @@ package docker
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
@ -54,7 +55,7 @@ func removeAllTempContainers(ctx context.Context, cli *client.Client) (err error
}
// UpgradeStepOne Trigger in the OTA upgrade
func UpgradeStepOne(channel string) (err error) {
func UpgradeStepOne(channel string, progressChan chan<- float64) (err error) {
ctx := context.Background()
// 1. Get the tag of the latest release
@ -78,8 +79,57 @@ func UpgradeStepOne(channel string) (err error) {
}
defer out.Close()
// Wait for pull to complete by reading the output
io.Copy(os.Stdout, out)
// Parse JSON stream and send progress updates through channel
decoder := json.NewDecoder(out)
type ProgressDetail struct {
Current int64 `json:"current"`
Total int64 `json:"total"`
}
type PullStatus struct {
Status string `json:"status"`
ProgressDetail ProgressDetail `json:"progressDetail"`
ID string `json:"id"`
}
layers := make(map[string]float64)
var status PullStatus
var lastProgress float64
for {
if err := decoder.Decode(&status); err != nil {
if err == io.EOF {
break
}
logger.Error("Error decoding Docker pull status:", err)
continue
}
// Only process layers with progress information
if status.ProgressDetail.Total > 0 {
progress := float64(status.ProgressDetail.Current) / float64(status.ProgressDetail.Total) * 100
layers[status.ID] = progress
// Calculate overall progress (average of all layers)
var totalProgress float64
for _, p := range layers {
totalProgress += p
}
overallProgress := totalProgress / float64(len(layers))
// Only send progress updates when there's a meaningful change
if overallProgress > lastProgress+1 || overallProgress >= 100 {
if progressChan != nil {
progressChan <- overallProgress
}
lastProgress = overallProgress
}
}
}
// Ensure we send 100% at the end
if progressChan != nil && lastProgress < 100 {
progressChan <- 100
}
// 3. Create a temp container
// Clean up any existing temp containers

View file

@ -13,6 +13,7 @@ type Control struct {
Channel string `json:"channel"`
}
// BinaryUpgrade Upgrade the binary
func BinaryUpgrade(ws *websocket.Conn, control *Control) {
_ = ws.WriteJSON(CoreUpgradeResp{
Status: UpgradeStatusInfo,

View file

@ -6,14 +6,42 @@ import (
"github.com/uozi-tech/cosy/logger"
)
// DockerUpgrade Upgrade the Docker container
func DockerUpgrade(ws *websocket.Conn, control *Control) {
err := docker.UpgradeStepOne(control.Channel)
if err != nil {
_ = ws.WriteJSON(CoreUpgradeResp{
Status: UpgradeStatusError,
Message: err.Error(),
})
logger.Error(err)
return
progressChan := make(chan float64)
// Start a goroutine to listen for progress updates and send them via WebSocket
go func() {
for progress := range progressChan {
err := ws.WriteJSON(CoreUpgradeResp{
Status: UpgradeStatusProgress,
Progress: progress,
Message: "Pulling Docker image...",
})
if err != nil {
logger.Error("Failed to send progress update:", err)
return
}
}
}()
defer close(progressChan)
if !control.DryRun {
err := docker.UpgradeStepOne(control.Channel, progressChan)
if err != nil {
_ = ws.WriteJSON(CoreUpgradeResp{
Status: UpgradeStatusError,
Message: err.Error(),
})
logger.Error(err)
return
}
}
// Send completion message
_ = ws.WriteJSON(CoreUpgradeResp{
Status: UpgradeStatusInfo,
Progress: 100,
Message: "Docker image pull completed, upgrading...",
})
}