From ae55cf428fef920b916d790749c4486a99bf5e27 Mon Sep 17 00:00:00 2001 From: electricface Date: Thu, 22 Jan 2026 16:53:50 +0800 Subject: [PATCH 1/2] wip:TODO --- src/internal/updateplatform/message_report.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/internal/updateplatform/message_report.go b/src/internal/updateplatform/message_report.go index 4b1a5ce5a..9d65d75f4 100644 --- a/src/internal/updateplatform/message_report.go +++ b/src/internal/updateplatform/message_report.go @@ -690,9 +690,20 @@ func getVersionData(data json.RawMessage) *updateMessage { logger.Warningf("%v failed to Unmarshal msg.Data to updateMessage: %v ", GetVersion.string(), err.Error()) return nil } + // TODO hack begin + hackVersionResponse(tmp) + // hack end return tmp } +// hackVersionResponse TODO: hack version response +func hackVersionResponse(versionResp *updateMessage) { + for idx, repoInfo := range versionResp.RepoInfos { + versionResp.RepoInfos[idx].Uri = strings.ReplaceAll(repoInfo.Uri, "p2p://", "https://") + versionResp.RepoInfos[idx].Source = strings.ReplaceAll(repoInfo.Source, "p2p://", "https://") + } +} + func getTargetPkgListData(data json.RawMessage) *PreInstalledPkgMeta { tmp := &PreInstalledPkgMeta{} err := json.Unmarshal(data, &tmp) From df1dde6a37b0720d43c28b2927c8bd8e325e3734 Mon Sep 17 00:00:00 2001 From: electricface Date: Thu, 22 Jan 2026 21:33:18 +0800 Subject: [PATCH 2/2] wip:TODO --- makefile | 5 + .../iup-tool/cmd_get_current_packages.go | 88 ++++++ src/local_cmd/iup-tool/cmd_get_cve_info.go | 89 ++++++ src/local_cmd/iup-tool/cmd_get_update_log.go | 90 ++++++ src/local_cmd/iup-tool/cmd_get_version.go | 72 +++++ .../iup-tool/cmd_post_process_event.go | 101 ++++++ src/local_cmd/iup-tool/cmd_post_result.go | 103 ++++++ src/local_cmd/iup-tool/iup-tool.go | 57 ++++ src/local_cmd/iup-tool/types.go | 295 ++++++++++++++++++ src/local_cmd/iup-tool/utils.go | 125 ++++++++ 10 files changed, 1025 insertions(+) create mode 100644 src/local_cmd/iup-tool/cmd_get_current_packages.go create mode 100644 src/local_cmd/iup-tool/cmd_get_cve_info.go create mode 100644 src/local_cmd/iup-tool/cmd_get_update_log.go create mode 100644 src/local_cmd/iup-tool/cmd_get_version.go create mode 100644 src/local_cmd/iup-tool/cmd_post_process_event.go create mode 100644 src/local_cmd/iup-tool/cmd_post_result.go create mode 100644 src/local_cmd/iup-tool/iup-tool.go create mode 100644 src/local_cmd/iup-tool/types.go create mode 100644 src/local_cmd/iup-tool/utils.go diff --git a/makefile b/makefile index f5eb7b2ec..b865ed368 100644 --- a/makefile +++ b/makefile @@ -35,6 +35,9 @@ build: prepare bin/lastore-agent bin/lastore-upgrade-query ${GoPath} ${GOBUILD_CGO_FLAGS} ${GOBUILD} -o bin/lastore-smartmirror-daemon ${GOBUILD_OPTIONS} ${GOPKG_PREFIX}/src/lastore-smartmirror-daemon || echo "build failed, disable smartmirror support " ${GoPath} ${GOBUILD_CGO_FLAGS} ${GOBUILD} -o bin/lastore-apt-clean ${GOBUILD_OPTIONS} ${GOPKG_PREFIX}/src/lastore-apt-clean +bin/iup-tool: + ${GoPath} ${GOBUILD_CGO_FLAGS} ${GOBUILD} -o bin/iup-tool ${GOBUILD_OPTIONS} ${GOPKG_PREFIX}/src/local_cmd/iup-tool + fetch-base-metadata: ./bin/lastore-tools update -r desktop -j applications -o var/lib/lastore/applications.json ./bin/lastore-tools update -r desktop -j categories -o var/lib/lastore/categories.json @@ -106,3 +109,5 @@ clean: check_code_quality: ${GoPath} go vet ./src/... + +.PHONY: bin/iup-tool \ No newline at end of file diff --git a/src/local_cmd/iup-tool/cmd_get_current_packages.go b/src/local_cmd/iup-tool/cmd_get_current_packages.go new file mode 100644 index 000000000..df241c842 --- /dev/null +++ b/src/local_cmd/iup-tool/cmd_get_current_packages.go @@ -0,0 +1,88 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "time" + + "github.com/spf13/cobra" +) + +var getCurrentPackagesCmd = &cobra.Command{ + Use: "get_current_packages", + Short: "Get current package lists from update platform", + Long: "Query the update platform for current version package lists", + Run: runGetCurrentPackages, +} + +var currentPkgBaseline string + +func init() { + getCurrentPackagesCmd.Flags().StringVar(¤tPkgBaseline, "baseline", "", "Current baseline number (required)") + _ = getCurrentPackagesCmd.MarkFlagRequired("baseline") + rootCmd.AddCommand(getCurrentPackagesCmd) +} + +func runGetCurrentPackages(cmd *cobra.Command, args []string) { + if currentPkgBaseline == "" { + logger.Warning("baseline is required") + os.Exit(1) + } + + response, err := genCurrentPkgListsResponse(updatePlatform.requestURL, updatePlatform.Token, currentPkgBaseline) + if err != nil { + logger.Warningf("genCurrentPkgListsResponse failed: %v", err) + os.Exit(1) + } + + data, err := getResponseData(response, GetCurrentPkgLists) + if err != nil { + logger.Warningf("getResponseData failed: %v", err) + os.Exit(1) + } + + pkgs := getCurrentPkgListsData(data) + if pkgs == nil { + logger.Warning("failed to parse current package list data") + os.Exit(1) + } + + logger.Infof("Current Package Lists for baseline: %s", currentPkgBaseline) + logger.Infof("PreCheck scripts: %d", len(pkgs.PreCheck)) + logger.Infof("MidCheck scripts: %d", len(pkgs.MidCheck)) + logger.Infof("PostCheck scripts: %d", len(pkgs.PostCheck)) + logger.Infof("Core packages: %d", len(pkgs.Packages.Core)) + logger.Infof("Select packages: %d", len(pkgs.Packages.Select)) + logger.Infof("Freeze packages: %d", len(pkgs.Packages.Freeze)) + logger.Infof("Purge packages: %d", len(pkgs.Packages.Purge)) +} + +func genCurrentPkgListsResponse(requestUrl, token, baseline string) (*http.Response, error) { + policyUrl := requestUrl + Urls[GetCurrentPkgLists].path + client := &http.Client{ + Timeout: 40 * time.Second, + } + values := url.Values{} + values.Add("baseline", baseline) + policyUrl = policyUrl + "?" + values.Encode() + request, err := http.NewRequest(Urls[GetCurrentPkgLists].method, policyUrl, nil) + if err != nil { + return nil, fmt.Errorf("%v new request failed: %v ", GetCurrentPkgLists.string(), err.Error()) + } + request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(token))) + return client.Do(request) +} + +func getCurrentPkgListsData(data json.RawMessage) *PreInstalledPkgMeta { + tmp := &PreInstalledPkgMeta{} + err := json.Unmarshal(data, &tmp) + if err != nil { + logger.Warningf("%v failed to Unmarshal msg.Data to PreInstalledPkgMeta: %v ", GetCurrentPkgLists.string(), err.Error()) + return nil + } + return tmp +} diff --git a/src/local_cmd/iup-tool/cmd_get_cve_info.go b/src/local_cmd/iup-tool/cmd_get_cve_info.go new file mode 100644 index 000000000..94c9e59d2 --- /dev/null +++ b/src/local_cmd/iup-tool/cmd_get_cve_info.go @@ -0,0 +1,89 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "time" + + "github.com/spf13/cobra" +) + +var getCVEInfoCmd = &cobra.Command{ + Use: "get_cve_info", + Short: "Get CVE vulnerability information from update platform", + Long: "Query the update platform for CVE (Common Vulnerabilities and Exposures) information", + Run: runGetCVEInfo, +} + +var cveSyncTime string + +func init() { + getCVEInfoCmd.Flags().StringVar(&cveSyncTime, "sync-time", "", "Sync time for CVE data (optional, format: 2006-01-02)") + rootCmd.AddCommand(getCVEInfoCmd) +} + +func runGetCVEInfo(cmd *cobra.Command, args []string) { + response, err := genCVEInfoResponse(updatePlatform.requestURL, updatePlatform.Token, cveSyncTime) + if err != nil { + logger.Warningf("genCVEInfoResponse failed: %v", err) + os.Exit(1) + } + + data, err := getResponseData(response, GetPkgCVEs) + if err != nil { + logger.Warningf("getResponseData failed: %v", err) + os.Exit(1) + } + + cveMeta := getCVEData(data) + if cveMeta == nil { + logger.Warning("failed to parse CVE data") + os.Exit(1) + } + + logger.Infof("CVE Data Time: %s", cveMeta.DataTime) + logger.Infof("Total CVEs: %d", len(cveMeta.CVEs)) + logger.Infof("Total Packages with CVEs: %d", len(cveMeta.PkgCVEs)) + + // 打印部分CVE信息 + count := 0 + for cveID, cveInfo := range cveMeta.CVEs { + if count >= 10 { + logger.Infof("... and %d more CVEs", len(cveMeta.CVEs)-10) + break + } + logger.Infof("CVE %s: Severity=%s", cveID, cveInfo.Severity) + logger.Debugf(" Description: %s", cveInfo.Description) + count++ + } +} + +func genCVEInfoResponse(requestUrl, token, syncTime string) (*http.Response, error) { + policyUrl := requestUrl + Urls[GetPkgCVEs].path + client := &http.Client{ + Timeout: 40 * time.Second, + } + values := url.Values{} + values.Add("synctime", syncTime) + policyUrl = policyUrl + "?" + values.Encode() + request, err := http.NewRequest(Urls[GetPkgCVEs].method, policyUrl, nil) + if err != nil { + return nil, fmt.Errorf("%v new request failed: %v ", GetPkgCVEs.string(), err.Error()) + } + request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(token))) + return client.Do(request) +} + +func getCVEData(data json.RawMessage) *CVEMeta { + tmp := &CVEMeta{} + err := json.Unmarshal(data, &tmp) + if err != nil { + logger.Warningf("%v failed to Unmarshal msg.Data to CVEMeta: %v ", GetPkgCVEs.string(), err.Error()) + return nil + } + return tmp +} diff --git a/src/local_cmd/iup-tool/cmd_get_update_log.go b/src/local_cmd/iup-tool/cmd_get_update_log.go new file mode 100644 index 000000000..87370022c --- /dev/null +++ b/src/local_cmd/iup-tool/cmd_get_update_log.go @@ -0,0 +1,90 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "time" + + "github.com/spf13/cobra" +) + +var getUpdateLogCmd = &cobra.Command{ + Use: "get_update_log", + Short: "Get system update logs from update platform", + Long: "Query the update platform for system update logs based on baseline and unstable version", + Run: runGetUpdateLog, +} + +var ( + updateLogBaseline string + updateLogIsUnstable int +) + +func init() { + getUpdateLogCmd.Flags().StringVar(&updateLogBaseline, "baseline", "", "Target baseline number (required)") + getUpdateLogCmd.Flags().IntVar(&updateLogIsUnstable, "is-unstable", 1, "Is unstable version (1 for release, 2 for unstable)") + _ = getUpdateLogCmd.MarkFlagRequired("baseline") + rootCmd.AddCommand(getUpdateLogCmd) +} + +func runGetUpdateLog(cmd *cobra.Command, args []string) { + if updateLogBaseline == "" { + logger.Warning("baseline is required") + os.Exit(1) + } + + response, err := genUpdateLogResponse(updatePlatform.requestURL, updatePlatform.Token, updateLogBaseline, updateLogIsUnstable) + if err != nil { + logger.Warningf("genUpdateLogResponse failed: %v", err) + os.Exit(1) + } + + data, err := getResponseData(response, GetUpdateLog) + if err != nil { + logger.Warningf("getResponseData failed: %v", err) + os.Exit(1) + } + + logs := getUpdateLogData(data) + if logs == nil { + logger.Warning("failed to parse update log data") + os.Exit(1) + } + + logger.Infof("Found %d update logs", len(logs)) + for i, log := range logs { + logger.Infof("Log %d: Type=%s, Title=%s", i+1, log.Type, log.Title) + logger.Debugf(" Content: %s", log.Content) + } +} + +func genUpdateLogResponse(requestUrl, token, baseline string, isUnstable int) (*http.Response, error) { + policyUrl := requestUrl + Urls[GetUpdateLog].path + client := &http.Client{ + Timeout: 40 * time.Second, + } + values := url.Values{} + values.Add("baseline", baseline) + values.Add("isUnstable", fmt.Sprintf("%d", isUnstable)) + policyUrl = policyUrl + "?" + values.Encode() + request, err := http.NewRequest(Urls[GetUpdateLog].method, policyUrl, nil) + if err != nil { + return nil, fmt.Errorf("%v new request failed: %v ", GetUpdateLog.string(), err.Error()) + } + request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(token))) + return client.Do(request) +} + +func getUpdateLogData(data json.RawMessage) []UpdateLogMeta { + var tmp []UpdateLogMeta + err := json.Unmarshal(data, &tmp) + if err != nil { + logger.Warningf("%v failed to Unmarshal msg.Data to UpdateLogMeta: %v ", GetUpdateLog.string(), err.Error()) + return nil + } + return tmp +} diff --git a/src/local_cmd/iup-tool/cmd_get_version.go b/src/local_cmd/iup-tool/cmd_get_version.go new file mode 100644 index 000000000..051ff0069 --- /dev/null +++ b/src/local_cmd/iup-tool/cmd_get_version.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/spf13/cobra" +) + +// getVersionData 解析版本数据 +func getVersionData(data json.RawMessage) *updateMessage { + tmp := &updateMessage{} + err := json.Unmarshal(data, &tmp) + if err != nil { + logger.Warningf("%v failed to Unmarshal msg.Data to updateMessage: %v ", GetVersion.string(), err.Error()) + return nil + } + return tmp +} + +// genVersionResponse 生成版本请求 +func (m *UpdatePlatformManager) genVersionResponse() (*http.Response, error) { + policyURL := m.requestURL + Urls[GetVersion].path + client := &http.Client{ + Timeout: 40 * time.Second, + } + request, err := http.NewRequest(Urls[GetVersion].method, policyURL, nil) + if err != nil { + return nil, fmt.Errorf("%v new request failed: %v ", GetVersion.string(), err.Error()) + } + request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(m.Token))) + request.Header.Set("X-Packages", base64.RawStdEncoding.EncodeToString([]byte(getClientPackageInfo("")))) + return client.Do(request) +} + +// genUpdatePolicyByToken 检查更新时将token数据发送给更新平台,获取本次更新信息 +func (m *UpdatePlatformManager) genUpdatePolicyByToken() error { + response, err := m.genVersionResponse() + if err != nil { + return fmt.Errorf("failed get version data %v", err) + } + logger.Debugf("response: %v", response) + data, err := getResponseData(response, GetVersion) + if err != nil { + return fmt.Errorf("failed get version data %v", err) + } + msg := getVersionData(data) + logger.Infof("msg: %s", spew.Sdump(msg)) + return nil +} + +var getVersionCmd = &cobra.Command{ + Use: "get_version", + Short: "Get version information from update platform", + Long: "Query the update platform for version information and update policy", + Run: func(cmd *cobra.Command, args []string) { + err := updatePlatform.genUpdatePolicyByToken() + if err != nil { + logger.Warningf("genUpdatePolicyByToken failed: %v", err) + os.Exit(1) + } + }, +} + +func init() { + rootCmd.AddCommand(getVersionCmd) +} diff --git a/src/local_cmd/iup-tool/cmd_post_process_event.go b/src/local_cmd/iup-tool/cmd_post_process_event.go new file mode 100644 index 000000000..f166b4bf1 --- /dev/null +++ b/src/local_cmd/iup-tool/cmd_post_process_event.go @@ -0,0 +1,101 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/spf13/cobra" +) + +var postProcessEventCmd = &cobra.Command{ + Use: "post_process_event", + Short: "Post upgrade process event to update platform", + Long: "Send upgrade process events to the update platform for tracking", + Run: runPostProcessEvent, +} + +var ( + eventTaskID int + eventType ProcessEventType + eventStatus bool + eventContent string + eventDataFile string +) + +func init() { + postProcessEventCmd.Flags().IntVarP(&eventTaskID, "task-id", "t", 0, "Task ID") + postProcessEventCmd.Flags().IntVarP((*int)(&eventType), "event-type", "e", 0, "Event type (1:CheckEnv, 2:GetUpdate, 3:StartDownload, 4:DownloadComplete, 5:StartBackUp, 6:BackUpComplete, 7:StartInstall)") + postProcessEventCmd.Flags().BoolVarP(&eventStatus, "status", "s", false, "Event status (true for success, false for failure)") + postProcessEventCmd.Flags().StringVarP(&eventContent, "content", "c", "", "Event content/message") + postProcessEventCmd.Flags().StringVarP(&eventDataFile, "data-file", "f", "", "JSON file containing event data (alternative to flags)") + rootCmd.AddCommand(postProcessEventCmd) +} + +func runPostProcessEvent(cmd *cobra.Command, args []string) { + var event *ProcessEvent + + // 从文件读取或从命令行参数构建 + if eventDataFile != "" { + data, err := os.ReadFile(eventDataFile) + if err != nil { + logger.Warningf("failed to read data file: %v", err) + os.Exit(1) + } + event = &ProcessEvent{} + err = json.Unmarshal(data, event) + if err != nil { + logger.Warningf("failed to parse JSON data: %v", err) + os.Exit(1) + } + } else { + event = &ProcessEvent{ + TaskID: eventTaskID, + EventType: eventType, + EventStatus: eventStatus, + EventContent: eventContent, + } + } + logger.Debugf("Process Event: %s", spew.Sdump(event)) + + response, err := genPostProcessEventResponse(updatePlatform.requestURL, updatePlatform.Token, event) + if err != nil { + logger.Warningf("genPostProcessEventResponse failed: %v", err) + os.Exit(1) + } + + data, err := getResponseData(response, PostProcessEvent) + if err != nil { + logger.Warningf("getResponseData failed: %v", err) + os.Exit(1) + } + + logger.Infof("Process event posted successfully") + logger.Debugf("Response data: %s", string(data)) +} + +func genPostProcessEventResponse(requestUrl, token string, event *ProcessEvent) (*http.Response, error) { + policyUrl := requestUrl + Urls[PostProcessEvent].path + client := &http.Client{ + Timeout: 40 * time.Second, + } + + eventData, err := json.Marshal(event) + if err != nil { + return nil, fmt.Errorf("failed to marshal event data: %v", err) + } + + body := bytes.NewBuffer(eventData) + request, err := http.NewRequest(Urls[PostProcessEvent].method, policyUrl, body) + if err != nil { + return nil, fmt.Errorf("%v new request failed: %v ", PostProcessEvent.string(), err.Error()) + } + request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(token))) + request.Header.Set("Content-Type", "application/json") + return client.Do(request) +} diff --git a/src/local_cmd/iup-tool/cmd_post_result.go b/src/local_cmd/iup-tool/cmd_post_result.go new file mode 100644 index 000000000..1ddf561bd --- /dev/null +++ b/src/local_cmd/iup-tool/cmd_post_result.go @@ -0,0 +1,103 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + "github.com/spf13/cobra" +) + +var postResultCmd = &cobra.Command{ + Use: "post_result", + Short: "Post upgrade result to update platform", + Long: "Send final upgrade result to the update platform for reporting", + Run: runPostResult, +} + +var ( + resultDataFile string + resultTaskID int + resultStatus int + resultMsg string + resultPreBaseline string + resultNextBaseline string +) + +func init() { + postResultCmd.Flags().StringVar(&resultDataFile, "data-file", "", "JSON file containing upgrade result data") + postResultCmd.Flags().IntVar(&resultTaskID, "task-id", 0, "Task ID") + postResultCmd.Flags().IntVar(&resultStatus, "status", 0, "Upgrade status (0:success, 1:failed, 2:check-failed)") + postResultCmd.Flags().StringVar(&resultMsg, "message", "", "Error message (if failed)") + postResultCmd.Flags().StringVar(&resultPreBaseline, "pre-baseline", "", "Previous baseline") + postResultCmd.Flags().StringVar(&resultNextBaseline, "next-baseline", "", "Next baseline") + rootCmd.AddCommand(postResultCmd) +} + +func runPostResult(cmd *cobra.Command, args []string) { + var result *UpgradePostMsg + + // 从文件读取或从命令行参数构建 + if resultDataFile != "" { + data, err := os.ReadFile(resultDataFile) + if err != nil { + logger.Warningf("failed to read data file: %v", err) + os.Exit(1) + } + result = &UpgradePostMsg{} + err = json.Unmarshal(data, result) + if err != nil { + logger.Warningf("failed to parse JSON data: %v", err) + os.Exit(1) + } + } else { + result = &UpgradePostMsg{ + TaskId: resultTaskID, + UpgradeStatus: UpgradeResult(resultStatus), + UpgradeErrorMsg: resultMsg, + PreBaseline: resultPreBaseline, + NextBaseline: resultNextBaseline, + TimeStamp: time.Now().Unix(), + } + } + + response, err := genPostResultResponse(updatePlatform.requestURL, updatePlatform.Token, result) + if err != nil { + logger.Warningf("genPostResultResponse failed: %v", err) + os.Exit(1) + } + + data, err := getResponseData(response, PostResult) + if err != nil { + logger.Warningf("getResponseData failed: %v", err) + os.Exit(1) + } + + logger.Infof("Upgrade result posted successfully") + logger.Debugf("Response data: %s", string(data)) +} + +func genPostResultResponse(requestUrl, token string, result *UpgradePostMsg) (*http.Response, error) { + policyUrl := requestUrl + Urls[PostResult].path + client := &http.Client{ + Timeout: 40 * time.Second, + } + + resultData, err := json.Marshal(result) + if err != nil { + return nil, fmt.Errorf("failed to marshal result data: %v", err) + } + + body := bytes.NewBuffer(resultData) + request, err := http.NewRequest(Urls[PostResult].method, policyUrl, body) + if err != nil { + return nil, fmt.Errorf("%v new request failed: %v ", PostResult.string(), err.Error()) + } + request.Header.Set("X-Repo-Token", base64.RawStdEncoding.EncodeToString([]byte(token))) + request.Header.Set("Content-Type", "application/json") + return client.Do(request) +} diff --git a/src/local_cmd/iup-tool/iup-tool.go b/src/local_cmd/iup-tool/iup-tool.go new file mode 100644 index 000000000..81ba50191 --- /dev/null +++ b/src/local_cmd/iup-tool/iup-tool.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "os" + + "github.com/linuxdeepin/go-lib/log" + "github.com/spf13/cobra" +) + +var logger = log.NewLogger("lastore/iup-tool") + +var updatePlatform UpdatePlatformManager + +// initUpdatePlatform initialize update platform manager +func initUpdatePlatform() { + updatePlatform = UpdatePlatformManager{ + requestURL: getPlatformURLFromDSettings(), + Token: getTokenFromAptConfig(), + } +} + +var rootCmd = &cobra.Command{ + Use: "iup-tool", + Short: "tool for intranet update platform operations", + Long: "A tool for interacting with the IUP (Intranet Update Platform)", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + // set log level + if globalDebug { + logger.SetLogLevel(log.LevelDebug) + } else { + logger.SetLogLevel(log.LevelInfo) + } + logger.Debug("Starting iup-tool") + + // initialize update platform manager + initUpdatePlatform() + }, +} + +var ( + globalTimeout int + globalDebug bool +) + +func init() { + // 全局 flags + rootCmd.PersistentFlags().IntVar(&globalTimeout, "timeout", 40, "HTTP request timeout in seconds") + rootCmd.PersistentFlags().BoolVar(&globalDebug, "debug", false, "Enable debug logging") +} + +func main() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/src/local_cmd/iup-tool/types.go b/src/local_cmd/iup-tool/types.go new file mode 100644 index 000000000..29a49c03c --- /dev/null +++ b/src/local_cmd/iup-tool/types.go @@ -0,0 +1,295 @@ +package main + +import ( + "encoding/json" + "fmt" +) + +// requestType 请求类型枚举 +type requestType uint + +const ( + GetVersion requestType = iota + GetUpdateLog + GetTargetPkgLists // 系统软件包清单 + GetCurrentPkgLists + GetPkgCVEs // CVE 信息 + PostProcess + PostProcessEvent + PostResult +) + +func (r requestType) string() string { + return fmt.Sprintf("%v %v", Urls[r].method, Urls[r].path) +} + +// requestContent 请求内容 +type requestContent struct { + path string + method string +} + +// Urls API 端点映射 +var Urls = map[requestType]requestContent{ + GetVersion: { + "/api/v1/version", + "GET", + }, + GetTargetPkgLists: { + "/api/v1/package", + "GET", + }, + GetCurrentPkgLists: { + "/api/v1/package", + "GET", + }, + GetUpdateLog: { + "/api/v1/systemupdatelogs", + "GET", + }, + GetPkgCVEs: { + "/api/v1/cve/sync", + "GET", + }, + PostProcess: { + "/api/v1/process", + "POST", + }, + PostProcessEvent: { + "/api/v1/process/events", + "POST", + }, + PostResult: { + "/api/v1/update/status", + "POST", + }, +} + +// tokenMessage 通用响应消息 +type tokenMessage struct { + Result bool `json:"result"` + Code int `json:"code"` + Data json.RawMessage `json:"data"` +} + +// tokenErrorMessage 错误响应消息 +type tokenErrorMessage struct { + Result bool `json:"result"` + Code int `json:"code"` + Msg string `json:"msg"` +} + +// UpdateTp 更新策略类型 +type UpdateTp int + +const ( + UnknownUpdate UpdateTp = 0 + NormalUpdate UpdateTp = 1 // 更新 + UpdateNow UpdateTp = 2 // 立即更新 // 以下为强制更新 + UpdateShutdown UpdateTp = 3 // 关机更新 + UpdateRegularly UpdateTp = 4 // 定时更新 +) + +// String 返回 UpdateTp 的字符串表示 +func (u UpdateTp) String() string { + switch u { + case UnknownUpdate: + return "UnknownUpdate" + case NormalUpdate: + return "NormalUpdate" + case UpdateNow: + return "UpdateNow" + case UpdateShutdown: + return "UpdateShutdown" + case UpdateRegularly: + return "UpdateRegularly" + default: + return fmt.Sprintf("UpdateTp(%d)", u) + } +} + +// Version 版本信息 +type Version struct { + Version string `json:"version"` + Baseline string `json:"baseline"` + TaskID int `json:"taskID"` +} + +// Policy 更新策略 +type Policy struct { + Tp UpdateTp `json:"tp"` + Data policyData `json:"data"` +} + +type policyData struct { + UpdateTime string `json:"updateTime"` +} + +// repoInfo 仓库信息 +type repoInfo struct { + URI string `json:"uri"` + Cdn string `json:"cdn"` + CodeName string `json:"codename"` + Version string `json:"version"` + Source string `json:"source"` +} + +// ClientPollSetting 客户端轮询设置 +type ClientPollSetting struct { + CheckPolicyInterval int `json:"checkPolicyInterval"` + StartCheckRange []int `json:"startCheckRange"` +} + +// updateMessage 更新消息 +type updateMessage struct { + SystemType string `json:"systemType"` + Version Version `json:"version"` + Policy Policy `json:"policy"` + RepoInfos []repoInfo `json:"repoInfos"` + ClientPollSetting ClientPollSetting `json:"clientPollSetting"` +} + +// UpdateLogMeta 更新日志元数据 +type UpdateLogMeta struct { + Type string `json:"type"` + Title string `json:"title"` + Content string `json:"content"` +} + +// ShellCheck 检查脚本 +type ShellCheck struct { + Name string `json:"name"` // 检查脚本的名字 + Shell string `json:"shell"` // 检查脚本的内容 +} + +// PackageInfo 软件包信息 +type PackageInfo struct { + Name string `json:"name"` + Version string `json:"version"` + Need bool `json:"need"` +} + +// PlatformPackageInfo 平台软件包信息 +type PlatformPackageInfo struct { + Name string `json:"name"` + Need bool `json:"need"` + AllArchVersion []ArchVersionInfo `json:"allArchVersion"` +} + +// ArchVersionInfo 架构版本信息 +type ArchVersionInfo struct { + Arch string `json:"arch"` + Version string `json:"version"` +} + +// packageLists 软件包清单 +type packageLists struct { + Core []PlatformPackageInfo `json:"core"` // 必须安装软件包清单 + Select []PlatformPackageInfo `json:"select"` // 可选软件包清单 + Freeze []PlatformPackageInfo `json:"freeze"` // 禁止升级包清单 + Purge []PlatformPackageInfo `json:"purge"` // 删除软件包清单 +} + +// PreInstalledPkgMeta 预装软件包元数据 +type PreInstalledPkgMeta struct { + PreCheck []ShellCheck `json:"preCheck"` // 更新前检查脚本 + MidCheck []ShellCheck `json:"midCheck"` // 更新后检查脚本 + PostCheck []ShellCheck `json:"postCheck"` // 更新完成重启后检查脚本 + Packages packageLists `json:"packages"` // 基线软件包清单 +} + +// CVEInfo CVE 信息 +type CVEInfo struct { + ID string `json:"id"` + Description string `json:"description"` + Severity string `json:"severity"` +} + +// CVEMeta CVE 元数据 +type CVEMeta struct { + DataTime string `json:"dataTime"` + CVEs map[string]CVEInfo `json:"cves"` + PkgCVEs map[string][]string `json:"pkgCves"` +} + +// ProcessEventType 进程事件类型 +type ProcessEventType int + +const ( + CheckEnv ProcessEventType = 1 + GetUpdateEvent ProcessEventType = 2 + StartDownload ProcessEventType = 3 + DownloadComplete ProcessEventType = 4 + StartBackUp ProcessEventType = 5 + BackUpComplete ProcessEventType = 6 + StartInstall ProcessEventType = 7 +) + +// String 返回 ProcessEventType 的字符串表示 +func (p ProcessEventType) String() string { + switch p { + case CheckEnv: + return "CheckEnv" + case GetUpdateEvent: + return "GetUpdateEvent" + case StartDownload: + return "StartDownload" + case DownloadComplete: + return "DownloadComplete" + case StartBackUp: + return "StartBackUp" + case BackUpComplete: + return "BackUpComplete" + case StartInstall: + return "StartInstall" + default: + return fmt.Sprintf("ProcessEventType(%d)", p) + } +} + +// ProcessEvent 进程事件 +type ProcessEvent struct { + TaskID int `json:"taskID"` + EventType ProcessEventType `json:"eventType"` + EventStatus bool `json:"eventStatus"` // 是否成功 + EventContent string `json:"eventContent"` +} + +// UpgradeResult 升级结果 +type UpgradeResult int8 + +const ( + UpgradeSucceed UpgradeResult = 0 + UpgradeFailed UpgradeResult = 1 + CheckFailed UpgradeResult = 2 +) + +// MsgPostStatus 消息上报状态 +type MsgPostStatus string + +const ( + NotReady MsgPostStatus = "not ready" + WaitPost MsgPostStatus = "wait post" + PostSuccess MsgPostStatus = "post success" + PostFailure MsgPostStatus = "post failure" +) + +// UpgradePostMsg 升级上报消息 +type UpgradePostMsg struct { + SerialNumber string `json:"serialNumber"` + MachineID string `json:"machineId"` + UpgradeStatus UpgradeResult `json:"status"` + UpgradeErrorMsg string `json:"msg"` + TimeStamp int64 `json:"timestamp"` + SourceUrl []string `json:"sourceUrl"` + Version string `json:"version"` + + PreBuild string `json:"preBuild"` + NextShowVersion string `json:"nextShowVersion"` + PreBaseline string `json:"preBaseline"` + NextBaseline string `json:"nextBaseline"` + + UpgradeStartTime int64 `json:"updateStartAt"` + UpgradeEndTime int64 `json:"updateFinishAt"` + TaskId int `json:"taskId"` +} diff --git a/src/local_cmd/iup-tool/utils.go b/src/local_cmd/iup-tool/utils.go new file mode 100644 index 000000000..24654b2db --- /dev/null +++ b/src/local_cmd/iup-tool/utils.go @@ -0,0 +1,125 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os/exec" + "strings" + + "github.com/godbus/dbus/v5" + ConfigManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager" +) + +// UpdatePlatformManager 更新平台管理器 +type UpdatePlatformManager struct { + requestURL string + Token string +} + +const ( + dSettingsAppID = "org.deepin.dde.lastore" + dSettingsLastoreName = "org.deepin.dde.lastore" + dSettingsKeyPlatformUrl = "platform-url" +) + +// getTokenFromAptConfig 从 apt-config 获取 Token +func getTokenFromAptConfig() string { + cmd := exec.Command("apt-config", "dump", "Acquire::SmartMirrors::Token") + output, err := cmd.Output() + if err != nil { + logger.Warningf("failed to get token from apt-config: %v", err) + return "" + } + + // 解析输出: Acquire::SmartMirrors::Token "token_value"; + line := strings.TrimSpace(string(output)) + if line == "" { + logger.Warning("apt-config returned empty output") + return "" + } + + // 查找双引号之间的内容 + startIdx := strings.Index(line, "\"") + if startIdx == -1 { + logger.Warningf("failed to parse token: no opening quote found in: %s", line) + return "" + } + endIdx := strings.LastIndex(line, "\"") + if endIdx == -1 || endIdx <= startIdx { + logger.Warningf("failed to parse token: no closing quote found in: %s", line) + return "" + } + + token := line[startIdx+1 : endIdx] + logger.Debugf("Token loaded from apt-config: %s", token) + return token +} + +// getPlatformURLFromDSettings 从 dSettings 获取平台 URL +func getPlatformURLFromDSettings() string { + sysBus, err := dbus.SystemBus() + if err != nil { + logger.Warningf("failed to get system bus: %v", err) + return "" + } + + ds := ConfigManager.NewConfigManager(sysBus) + dsPath, err := ds.AcquireManager(0, dSettingsAppID, dSettingsLastoreName, "") + if err != nil { + logger.Warningf("failed to acquire dSettings manager: %v", err) + return "" + } + + dsManager, err := ConfigManager.NewManager(sysBus, dsPath) + if err != nil { + logger.Warningf("failed to create dSettings manager: %v", err) + return "" + } + + v, err := dsManager.Value(0, dSettingsKeyPlatformUrl) + if err != nil { + logger.Warningf("failed to get platform URL from dSettings: %v", err) + return "" + } + + url := v.Value().(string) + logger.Debugf("Platform URL loaded from dSettings: %s", url) + return url +} + +// getClientPackageInfo 获取客户端包信息 +func getClientPackageInfo(clientPackageName string) string { + _ = clientPackageName + return "client=lastore-daemon&version=6.2.45" +} + +// getResponseData 解析 HTTP 响应数据 +func getResponseData(response *http.Response, reqType requestType) (json.RawMessage, error) { + if http.StatusOK == response.StatusCode { + respData, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("%v failed to read response body: %v ", response.Request.RequestURI, err.Error()) + } + logger.Debugf("%v request for %v respData:%s ", reqType.string(), response.Request.URL, string(respData)) + msg := &tokenMessage{} + err = json.Unmarshal(respData, msg) + if err != nil { + logger.Warningf("%v request for %v respData:%s ", reqType.string(), response.Request.URL, string(respData)) + return nil, fmt.Errorf("%v failed to Unmarshal respData to tokenMessage: %v ", reqType.string(), err.Error()) + } + if !msg.Result { + logger.Warningf("%v request for %v respData:%s ", reqType.string(), response.Request.URL, string(respData)) + errorMsg := &tokenErrorMessage{} + err = json.Unmarshal(respData, errorMsg) + if err != nil { + return nil, fmt.Errorf("%v request for %s", reqType.string(), response.Request.RequestURI) + } + return nil, fmt.Errorf("%v request for %s err:%s", reqType.string(), response.Request.RequestURI, errorMsg.Msg) + } + return msg.Data, nil + } else { + return nil, fmt.Errorf("request for %s failed, response code=%d", response.Request.RequestURI, response.StatusCode) + } +}