Skip to content

Commit c337e7d

Browse files
committed
feat: Refactor download size query logic and optimize display
- Remove IsIncrementalUpdateCached function, use IncrementalUpdate flag to select backend - Implement queryDownloadSizeViaApt: Fetch download size via apt-get dist-upgrade command - Implement queryDownloadSizeForIncrementalUpdate: Fetch download size via deepin-immutable-ctl interface - QuerySourceDownloadSize automatically selects the method based on the IncrementalUpdate flag - Load IncrementalUpdate flag from config on startup and apply to system modules - Use FormatSize function for log output to standardize display format - Remove redundant checks from PackagesDownloadSize and updateModeStatusBySize Task: https://pms.uniontech.com/task-view-384185.html Change-Id: I427ccdd0a8d1151e48e2c305ff3008d5ba4cb25e
1 parent 0761a4b commit c337e7d

9 files changed

Lines changed: 206 additions & 87 deletions

File tree

debian/rules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ ifneq ($(DEB_BUILD_ARCH), mips64el)
1818
endif
1919

2020
%:
21-
dh $@
21+
dh $@ --buildsystem=makefile
2222

2323
override_dh_install:
2424
dh_install --sourcedir=debian/tmp

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: 112 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,107 @@ 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+
// queryDownloadSizeForIncrementalUpdate queries the download size information for incremental updates
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 queryDownloadSizeForIncrementalUpdate(path string, updateType UpdateType, pkgList []string) (float64, float64, error) {
260+
var sourceArgs string
261+
modeStr := updateType.JobType()
262+
if modeStr == "" {
263+
modeStr = fmt.Sprintf("mode-%d", updateType)
264+
}
265+
cmd := exec.Command(DeepinImmutableCtlPath, "upgrade", "check", "-j", "-m", modeStr)
266+
if path != "" {
267+
if utils2.IsDir(path) {
268+
sourceArgs = "-o Dir::Etc::sourcelist=/dev/null -o Dir::Etc::SourceParts=" + path
269+
} else {
270+
sourceArgs = "-o Dir::Etc::sourcelist=" + path + " -o Dir::Etc::SourceParts=/dev/null"
271+
}
272+
}
273+
cmd.Env = append(os.Environ(), "LC_ALL=C",
274+
"DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION="+sourceArgs)
275+
logger.Infof("%v download size cmd: %v, sourceArgs: %v", updateType.JobType(), cmd.String(), sourceArgs)
276+
out, err := cmd.Output()
277+
logger.Debugf("immutable upgrade check output: %s", out)
278+
if err != nil {
279+
return 0, 0, fmt.Errorf("run %v failed: %v", cmd.Args, err)
280+
}
281+
var result immutableUpgradeCheckOutput
282+
if err := json.Unmarshal(out, &result); err != nil {
283+
return 0, 0, fmt.Errorf("parse immutable upgrade check output failed: %v", err)
284+
}
285+
logger.Debugf("immutable upgrade check result: %+v", result)
286+
if result.Code != 0 {
287+
return 0, 0, fmt.Errorf("immutable upgrade check returned code %d: %s", result.Code, result.Message)
288+
}
289+
needDownload := result.Data.NeedDownload.Size
290+
totalSize := result.Data.TotalSize
291+
// If both needDownload and totalSize are 0 but cached ostree packages exist, the ostree
292+
// transfer stats have likely been discarded. Return a non-zero totalSize (1) as a
293+
// sentinel to prevent the control center upgrade UI from stalling on a zero-size result.
294+
if needDownload == 0 && totalSize == 0 && result.Data.CachedOstreePkgCount > 0 {
295+
return 0, 1, nil
296+
}
297+
return float64(needDownload), float64(totalSize), nil
298+
}
299+
300+
// QuerySourceDownloadSize returns the download size information for the given update type.
301+
// It selects the appropriate backend (apt or immutable-ctl) based on the IncrementalUpdate flag.
302+
// Returns: bytes needed to download, total size of all packages, error.
204303
func QuerySourceDownloadSize(updateType UpdateType, pkgList []string) (float64, float64, error) {
304+
logger.Debugf("QuerySourceDownloadSize updateType: %v, pkgList: %v", updateType, pkgList)
205305
startTime := time.Now()
206306
downloadSize := new(float64)
207307
allPackageSize := new(float64)
@@ -211,46 +311,25 @@ func QuerySourceDownloadSize(updateType UpdateType, pkgList []string) (float64,
211311
unref()
212312
}
213313
}()
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)
314+
queryFn := queryDownloadSizeViaApt
315+
if IncrementalUpdate {
316+
queryFn = queryDownloadSizeForIncrementalUpdate
236317
}
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
318+
needDownloadSize, allSize, err := queryFn(path, updateType, pkgList)
319+
if err != nil {
320+
return err
246321
}
322+
*downloadSize = needDownloadSize
323+
*allPackageSize = allSize
247324
return nil
248325
})
249326
if err != nil {
250327
logger.Warning(err)
251328
return SizeDownloaded, SizeDownloaded, err
252329
}
253330
logger.Debug("end QuerySourceDownloadSize duration:", time.Now().Sub(startTime))
331+
logger.Debugf("QuerySourceDownloadSize result, download size: %s, all package size: %s",
332+
utils.FormatSize(*downloadSize), utils.FormatSize(*allPackageSize))
254333
return *downloadSize, *allPackageSize, nil
255334
}
256335

@@ -329,7 +408,7 @@ func QuerySourceAddSize(updateType UpdateType) (float64, error) {
329408
logger.Warning(err)
330409
return SizeUnknown, err
331410
}
332-
logger.Debug("end QuerySourceDownloadSize duration:", time.Now().Sub(startTime))
411+
logger.Debug("end QuerySourceAddSize duration:", time.Now().Sub(startTime))
333412
return *addSize, nil
334413
}
335414

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)

0 commit comments

Comments
 (0)