浏览代码

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

Jacky 1 周之前
父节点
当前提交
ff7f2e4f73
共有 4 个文件被更改,包括 91 次插入12 次删除
  1. 1 1
      .devcontainer/Dockerfile
  2. 53 3
      internal/docker/ota.go
  3. 1 0
      internal/upgrader/binary.go
  4. 36 8
      internal/upgrader/docker.go

+ 1 - 1
.devcontainer/Dockerfile

@@ -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 && \

+ 53 - 3
internal/docker/ota.go

@@ -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

+ 1 - 0
internal/upgrader/binary.go

@@ -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,

+ 36 - 8
internal/upgrader/docker.go

@@ -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...",
+	})
 }