Skip to content

Commit b49f0f6

Browse files
committed
feat(system): 支持增量更新及下载大小统计
- 新增 IncrementalUpdate 变量用于控制增量更新逻辑 - QuerySourceDownloadSize 中根据 IncrementalUpdate 选择不同下载大小计算方法 - 实现基于 apt-get dist-upgrade 命令获取包下载大小的新函数 buildAndRunDownloadSizeCmd - 实现基于 deepin-immutable-ctl 接口获取包下载大小的新函数 buildAndRunDownloadSizeCmdViaDIC - 日志中增加对增量更新状态和下载大小结果的详细记录 - 启动时从配置加载并设置 IncrementalUpdate 标志 Change-Id: I844b1e18e7e7dce38e2539853576117520ed1bf4
1 parent 0761a4b commit b49f0f6

File tree

8 files changed

+199
-86
lines changed

8 files changed

+199
-86
lines changed

src/internal/system/apt/apt.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"bytes"
99
"os"
1010
"os/exec"
11-
"strconv"
1211
"strings"
1312
"syscall"
1413

@@ -288,30 +287,3 @@ func DownloadPackages(packages []string, environ map[string]string, options map[
288287
}
289288
return tmpPath, nil
290289
}
291-
292-
// In incremental update mode, returns true if all packages are cached, otherwise returns false.
293-
func IsIncrementalUpdateCached(sourceArgs string) bool {
294-
cmd := exec.Command("/usr/sbin/deepin-immutable-ctl", "upgrade", "check")
295-
if sourceArgs != "" {
296-
cmd.Env = append(os.Environ(), "DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION="+sourceArgs)
297-
}
298-
// Need download count: xxx
299-
output, err := cmd.Output()
300-
if err == nil {
301-
matchFlag := "Need download count: "
302-
lines := strings.Split(string(output), "\n")
303-
for _, line := range lines {
304-
line = strings.TrimSpace(line)
305-
index := strings.Index(line, matchFlag)
306-
if index >= 0 {
307-
count, err := strconv.Atoi(strings.TrimSpace(line[index+len(matchFlag):]))
308-
if err == nil {
309-
if count == 0 {
310-
return true
311-
}
312-
}
313-
}
314-
}
315-
}
316-
return false
317-
}

src/internal/system/system.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
const VarLibDir = "/var/lib/lastore"
1414

1515
var IntranetUpdate bool
16+
var IncrementalUpdate bool
1617

1718
type Status string
1819

src/internal/system/system_apt.go

Lines changed: 106 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package system
1010

1111
import (
1212
"bytes"
13+
"encoding/json"
1314
"errors"
1415
"fmt"
1516
"os"
@@ -200,8 +201,101 @@ func QueryPackageDownloadSize(updateType UpdateType, packages ...string) (float6
200201
return *downloadSize, *allPackageSize, nil
201202
}
202203

203-
// QuerySourceDownloadSize 根据更新类型(仓库),获取需要的下载量,return arg0:需要下载的量;arg1:所有包的大小;arg2:error
204+
// queryDownloadSizeViaApt queries the download size information by running apt-get dist-upgrade
205+
// with the given source path. It parses the command output to extract package size data.
206+
// Returns: bytes needed to download, total size of all packages, error.
207+
func queryDownloadSizeViaApt(path string, updateType UpdateType, pkgList []string) (float64, float64, error) {
208+
var cmd *exec.Cmd
209+
if utils2.IsDir(path) {
210+
// #nosec G204
211+
cmd = exec.Command("/usr/bin/apt-get",
212+
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
213+
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", "/dev/null"),
214+
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", path)}, pkgList...)...)
215+
} else {
216+
// #nosec G204
217+
cmd = exec.Command("/usr/bin/apt-get",
218+
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
219+
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", path),
220+
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", "/dev/null")}, pkgList...)...)
221+
}
222+
cmd.Env = append(os.Environ(), "LC_ALL=C")
223+
logger.Infof("%v download size cmd: %v", updateType.JobType(), cmd.String())
224+
lines, err := utils.FilterExecOutput(cmd, time.Second*120, func(line string) bool {
225+
_, _, _err := parsePackageSize(line)
226+
return _err == nil
227+
})
228+
if err != nil && len(lines) == 0 {
229+
return 0, 0, fmt.Errorf("run:%v failed-->%v", cmd.Args, err)
230+
}
231+
if len(lines) != 0 {
232+
needDownloadSize, allSize, err := parsePackageSize(lines[0])
233+
if err != nil {
234+
logger.Warning(err)
235+
return 0, 0, err
236+
}
237+
return needDownloadSize, allSize, nil
238+
}
239+
return 0, 0, nil
240+
}
241+
242+
// immutableUpgradeCheckOutput represents the JSON output of deepin-immutable-ctl upgrade check -j.
243+
type immutableUpgradeCheckOutput struct {
244+
Code int `json:"code"`
245+
Message string `json:"message"`
246+
Data struct {
247+
TotalSize uint64 `json:"totalSize"`
248+
CachedOstreePkgCount int `json:"cachedOstreePkgCount"`
249+
NeedDownload struct {
250+
Size uint64 `json:"size"`
251+
} `json:"needDownload"`
252+
} `json:"data"`
253+
}
254+
255+
// queryDownloadSizeForImmutable queries the download size information for immutable systems
256+
// by invoking deepin-immutable-ctl upgrade check. The source path is passed via the
257+
// DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION environment variable.
258+
// Returns: bytes needed to download, total size of all packages, error.
259+
func queryDownloadSizeForImmutable(path string, updateType UpdateType, pkgList []string) (float64, float64, error) {
260+
var sourceArgs string
261+
cmd := exec.Command(DeepinImmutableCtlPath, "upgrade", "check", "-j")
262+
if path != "" {
263+
if utils2.IsDir(path) {
264+
sourceArgs = "-o Dir::Etc::sourcelist=/dev/null -o Dir::Etc::SourceParts=" + path
265+
} else {
266+
sourceArgs = "-o Dir::Etc::sourcelist=" + path + " -o Dir::Etc::SourceParts=/dev/null"
267+
}
268+
}
269+
cmd.Env = append(os.Environ(), "LC_ALL=C",
270+
"DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION="+sourceArgs)
271+
logger.Infof("%v download size cmd (immutable): %v", updateType.JobType(), cmd.String())
272+
out, err := cmd.Output()
273+
logger.Debugf("immutable upgrade check output: %s", out)
274+
if err != nil {
275+
return 0, 0, fmt.Errorf("run %v failed: %v", cmd.Args, err)
276+
}
277+
var result immutableUpgradeCheckOutput
278+
if err := json.Unmarshal(out, &result); err != nil {
279+
return 0, 0, fmt.Errorf("parse immutable upgrade check output failed: %v", err)
280+
}
281+
logger.Debugf("immutable upgrade check result: %+v", result)
282+
if result.Code != 0 {
283+
return 0, 0, fmt.Errorf("immutable upgrade check returned code %d: %s", result.Code, result.Message)
284+
}
285+
needDownload := result.Data.NeedDownload.Size
286+
totalSize := result.Data.TotalSize
287+
if needDownload == 0 && totalSize == 0 && result.Data.CachedOstreePkgCount > 0 {
288+
// TODO 此时如果返回 0, 0, nil 会导致控制中心界面卡住。
289+
return 0, 1, nil
290+
}
291+
return float64(needDownload), float64(totalSize), nil
292+
}
293+
294+
// QuerySourceDownloadSize returns the download size information for the given update type.
295+
// It selects the appropriate backend (apt or immutable-ctl) based on the IncrementalUpdate flag.
296+
// Returns: bytes needed to download, total size of all packages, error.
204297
func QuerySourceDownloadSize(updateType UpdateType, pkgList []string) (float64, float64, error) {
298+
logger.Debugf("QuerySourceDownloadSize updateType: %v, pkgList: %v", updateType, pkgList)
205299
startTime := time.Now()
206300
downloadSize := new(float64)
207301
allPackageSize := new(float64)
@@ -211,46 +305,25 @@ func QuerySourceDownloadSize(updateType UpdateType, pkgList []string) (float64,
211305
unref()
212306
}
213307
}()
214-
var cmd *exec.Cmd
215-
if utils2.IsDir(path) {
216-
// #nosec G204
217-
cmd = exec.Command("/usr/bin/apt-get",
218-
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
219-
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", "/dev/null"),
220-
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", path)}, pkgList...)...)
221-
} else {
222-
// #nosec G204
223-
cmd = exec.Command("/usr/bin/apt-get",
224-
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
225-
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", path),
226-
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", "/dev/null")}, pkgList...)...)
227-
}
228-
cmd.Env = append(os.Environ(), "LC_ALL=C")
229-
logger.Infof("%v download size cmd: %v", updateType.JobType(), cmd.String())
230-
lines, err := utils.FilterExecOutput(cmd, time.Second*120, func(line string) bool {
231-
_, _, _err := parsePackageSize(line)
232-
return _err == nil
233-
})
234-
if err != nil && len(lines) == 0 {
235-
return fmt.Errorf("run:%v failed-->%v", cmd.Args, err)
308+
queryFn := queryDownloadSizeViaApt
309+
if IncrementalUpdate {
310+
queryFn = queryDownloadSizeForImmutable
236311
}
237-
238-
if len(lines) != 0 {
239-
needDownloadSize, allSize, err := parsePackageSize(lines[0])
240-
if err != nil {
241-
logger.Warning(err)
242-
return err
243-
}
244-
*downloadSize = needDownloadSize
245-
*allPackageSize = allSize
312+
needDownloadSize, allSize, err := queryFn(path, updateType, pkgList)
313+
if err != nil {
314+
return err
246315
}
316+
*downloadSize = needDownloadSize
317+
*allPackageSize = allSize
247318
return nil
248319
})
249320
if err != nil {
250321
logger.Warning(err)
251322
return SizeDownloaded, SizeDownloaded, err
252323
}
253324
logger.Debug("end QuerySourceDownloadSize duration:", time.Now().Sub(startTime))
325+
logger.Debugf("QuerySourceDownloadSize result, download size: %s, all package size: %s",
326+
utils.FormatSize(*downloadSize), utils.FormatSize(*allPackageSize))
254327
return *downloadSize, *allPackageSize, nil
255328
}
256329

@@ -329,7 +402,7 @@ func QuerySourceAddSize(updateType UpdateType) (float64, error) {
329402
logger.Warning(err)
330403
return SizeUnknown, err
331404
}
332-
logger.Debug("end QuerySourceDownloadSize duration:", time.Now().Sub(startTime))
405+
logger.Debug("end QuerySourceAddSize duration:", time.Now().Sub(startTime))
333406
return *addSize, nil
334407
}
335408

src/internal/utils/utils.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,25 @@ func doUnsetEnvC(envName string) {
179179
defer C.free(unsafe.Pointer(cname))
180180
C.unsetenv(cname)
181181
}
182+
183+
// FormatSize formats bytes to human readable format (e.g., 1.5 MiB, 2.3 GiB)
184+
// Uses binary units (1 KiB = 1024 B)
185+
func FormatSize(bytes float64) string {
186+
if bytes < 0 {
187+
return "unknown"
188+
}
189+
190+
units := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
191+
unitIndex := 0
192+
size := bytes
193+
194+
for size >= 1024 && unitIndex < len(units)-1 {
195+
size /= 1024
196+
unitIndex++
197+
}
198+
199+
if unitIndex == 0 {
200+
return fmt.Sprintf("%.0f %s", size, units[unitIndex])
201+
}
202+
return fmt.Sprintf("%.2f %s", size, units[unitIndex])
203+
}

src/internal/utils/utils_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,65 @@
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
package utils
6+
7+
import (
8+
"testing"
9+
)
10+
11+
func TestFormatSize(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
bytes float64
15+
expected string
16+
}{
17+
// 字节 (B)
18+
{"zero bytes", 0, "0 B"},
19+
{"1 byte", 1, "1 B"},
20+
{"500 bytes", 500, "500 B"},
21+
{"999 bytes", 999, "999 B"},
22+
23+
// KiB (1024 bytes)
24+
{"1 KiB", 1024, "1.00 KiB"},
25+
{"1.5 KiB", 1536, "1.50 KiB"},
26+
{"10 KiB", 10240, "10.00 KiB"},
27+
{"100 KiB", 102400, "100.00 KiB"},
28+
{"1023.99 KiB", 1048575, "1024.00 KiB"},
29+
30+
// MiB (1024 KiB = 1,048,576 bytes)
31+
{"1 MiB", 1048576, "1.00 MiB"},
32+
{"1.5 MiB", 1572864, "1.50 MiB"},
33+
{"15.03 MiB", 15754620, "15.02 MiB"},
34+
{"100 MiB", 104857600, "100.00 MiB"},
35+
{"999.99 MiB", 1048575000, "1000.00 MiB"},
36+
37+
// GiB (1024 MiB = 1,073,741,824 bytes)
38+
{"1 GiB", 1073741824, "1.00 GiB"},
39+
{"1.5 GiB", 1610612736, "1.50 GiB"},
40+
{"2.3 GiB", 2469606195, "2.30 GiB"},
41+
{"10 GiB", 10737418240, "10.00 GiB"},
42+
{"100 GiB", 107374182400, "100.00 GiB"},
43+
44+
// TiB (1024 GiB)
45+
{"1 TiB", 1099511627776, "1.00 TiB"},
46+
{"2 TiB", 2199023255552, "2.00 TiB"},
47+
48+
// 边界值测试
49+
{"boundary 1023 bytes", 1023, "1023 B"},
50+
{"boundary 1024 bytes", 1024, "1.00 KiB"},
51+
{"boundary 1048575 bytes", 1048575, "1024.00 KiB"},
52+
{"boundary 1048576 bytes", 1048576, "1.00 MiB"},
53+
54+
// 负数处理
55+
{"negative value", -1, "unknown"},
56+
{"negative large", -1000, "unknown"},
57+
}
58+
59+
for _, tt := range tests {
60+
t.Run(tt.name, func(t *testing.T) {
61+
result := FormatSize(tt.bytes)
62+
if result != tt.expected {
63+
t.Errorf("FormatSize(%v) = %v, want %v", tt.bytes, result, tt.expected)
64+
}
65+
})
66+
}
67+
}

src/lastore-daemon/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ func main() {
7676
config := NewConfig(path.Join(system.VarLibDir, "config.json"))
7777
logger.Info("intranet update:", config.IntranetUpdate)
7878
system.IntranetUpdate = config.IntranetUpdate
79+
logger.Info("incremental update:", config.IncrementalUpdate)
80+
system.IncrementalUpdate = config.IncrementalUpdate
7981
if config.IntranetUpdate {
8082
go func() {
8183
out, err := exec.Command("/usr/bin/lastore-tools", "gatherinfo", "-type=post").CombinedOutput()

src/lastore-daemon/manager_ifc.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
utils2 "github.com/linuxdeepin/go-lib/utils"
2727
"github.com/linuxdeepin/lastore-daemon/src/internal/config"
2828
"github.com/linuxdeepin/lastore-daemon/src/internal/system"
29-
"github.com/linuxdeepin/lastore-daemon/src/internal/system/apt"
29+
"github.com/linuxdeepin/lastore-daemon/src/internal/utils"
3030
)
3131

3232
/*
@@ -214,10 +214,6 @@ func (m *Manager) PackagesDownloadSize(packages []string) (int64, *dbus.Error) {
214214
logger.Warningf("PackagesDownloadSize(%q)=%0.2f %v\n", strings.Join(packages, " "), size, err)
215215
}
216216

217-
if m.config.IncrementalUpdate && size > 0 && apt.IsIncrementalUpdateCached("") {
218-
size = 0.0
219-
}
220-
221217
return int64(size), dbusutil.ToError(err)
222218
}
223219

@@ -485,9 +481,9 @@ func (m *Manager) QueryAllSizeWithSource(mode system.UpdateType) (int64, *dbus.E
485481
}
486482
_, allSize, err := system.QuerySourceDownloadSize(mode, pkgList)
487483
if err != nil || allSize == system.SizeUnknown {
488-
logger.Warningf("failed to get %v source size:%v", strings.Join(sourcePathList, " and "), err)
484+
logger.Warningf("failed to query %v all size: %v", strings.Join(sourcePathList, " and "), err)
489485
} else {
490-
logger.Infof("%v size is:%v M", strings.Join(sourcePathList, " and "), int64(allSize/(1000*1000)))
486+
logger.Infof("%v all size: %s", strings.Join(sourcePathList, " and "), utils.FormatSize(allSize))
491487
}
492488

493489
return int64(allSize), dbusutil.ToError(err)

src/lastore-daemon/update_status.go

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import (
99
"strings"
1010
"sync"
1111

12-
"github.com/linuxdeepin/go-lib/utils"
1312
"github.com/linuxdeepin/lastore-daemon/src/internal/config"
1413
"github.com/linuxdeepin/lastore-daemon/src/internal/system"
15-
"github.com/linuxdeepin/lastore-daemon/src/internal/system/apt"
14+
"github.com/linuxdeepin/lastore-daemon/src/internal/utils"
1615
)
1716

1817
type UpdateModeStatusManager struct {
@@ -481,25 +480,11 @@ func (m *UpdateModeStatusManager) updateModeStatusBySize(mode system.UpdateType,
481480
changed = true
482481
}
483482
} else {
484-
sourceList, ok := system.GetCategorySourceMap()[typ]
485-
sourceArgs := ""
486-
if ok && sourceList != "" {
487-
if utils.IsDir(sourceList) {
488-
sourceArgs = "-o Dir::Etc::sourcelist=/dev/null -o Dir::Etc::SourceParts=" + sourceList
489-
} else {
490-
sourceArgs = "-o Dir::Etc::sourcelist=" + sourceList + " -o Dir::Etc::SourceParts=/dev/null"
491-
}
492-
}
493-
if m.lsConfig.IncrementalUpdate && needDownloadSize > 0 && apt.IsIncrementalUpdateCached(sourceArgs) {
494-
needDownloadSize = 0.0
495-
}
496-
497483
m.updateModeDownloadSizeMapLock.Lock()
498484
m.updateModeDownloadSizeMap[currentMode.JobType()] = needDownloadSize
499485
m.updateModeDownloadSizeMapLock.Unlock()
500-
logger.Infof("currentMode: %v, needDownloadSize: %v,"+
501-
" allPackageSize: %v, oldStatus: %v",
502-
currentMode.JobType(), needDownloadSize, allPackageSize, oldStatus)
486+
logger.Infof("currentMode: %v, needDownloadSize: %s, allPackageSize: %s, oldStatus: %v",
487+
currentMode.JobType(), utils.FormatSize(needDownloadSize), utils.FormatSize(allPackageSize), oldStatus)
503488
// allPackageSize == 0 有两种情况:1.无需更新;2.更新完成需要重启;
504489
if allPackageSize == 0 {
505490
if oldStatus != system.Upgraded {

0 commit comments

Comments
 (0)