package upgrade import ( "encoding/json" "strings" "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" ) type IndexAction struct { actionutils.ParentAction } func (this *IndexAction) Init() { this.Nav("", "", "") } func (this *IndexAction) RunGet(params struct{}) { // 加载升级配置 config, err := configloaders.LoadUpgradeConfig() if err != nil { this.ErrorPage(err) return } this.Data["config"] = config // 模块列表 modules := []maps.Map{} // 1. 边缘节点 (EdgeNode) nodeModule := this.loadEdgeNodeModule() if nodeModule != nil { modules = append(modules, nodeModule) } // 2. DNS节点 (EdgeDNS) — 仅Plus版本 if teaconst.IsPlus { dnsClusters := loadDNSUpgradeModules(&this.ParentAction) if len(dnsClusters) > 0 { totalCount := 0 for _, c := range dnsClusters { totalCount += c.GetInt("count") } modules = append(modules, maps.Map{ "name": "DNS节点", "code": "dns", "clusters": dnsClusters, "count": totalCount, }) } } // 3. HTTPDNS节点 httpdnsModule := this.loadHTTPDNSModule() if httpdnsModule != nil { modules = append(modules, httpdnsModule) } this.Data["modules"] = modules this.Show() } func (this *IndexAction) RunPost(params struct { AutoUpgrade bool }) { config, err := configloaders.LoadUpgradeConfig() if err != nil { this.ErrorPage(err) return } config.AutoUpgrade = params.AutoUpgrade err = configloaders.UpdateUpgradeConfig(config) if err != nil { this.ErrorPage(err) return } this.Success() } // loadEdgeNodeModule 加载边缘节点模块的待升级信息 func (this *IndexAction) loadEdgeNodeModule() maps.Map { // 获取所有集群 clustersResp, err := this.RPC().NodeClusterRPC().ListEnabledNodeClusters(this.AdminContext(), &pb.ListEnabledNodeClustersRequest{ Offset: 0, Size: 10000, }) if err != nil { return nil } var clusterMaps []maps.Map totalCount := 0 for _, cluster := range clustersResp.NodeClusters { resp, err := this.RPC().NodeRPC().FindAllUpgradeNodesWithNodeClusterId(this.AdminContext(), &pb.FindAllUpgradeNodesWithNodeClusterIdRequest{ NodeClusterId: cluster.Id, }) if err != nil { continue } if len(resp.Nodes) == 0 { continue } var nodeMaps []maps.Map for _, nodeUpgrade := range resp.Nodes { if nodeUpgrade.Node == nil { continue } installStatusMap := decodeInstallStatusFromPB(nodeUpgrade.Node.InstallStatus) accessIP, login, loginParams := decodeNodeAccessInfo(nodeUpgrade.Node.StatusJSON, nodeUpgrade.Node.NodeLogin, nodeUpgrade.Node.Name) nodeMaps = append(nodeMaps, maps.Map{ "id": nodeUpgrade.Node.Id, "name": nodeUpgrade.Node.Name, "os": nodeUpgrade.Os, "arch": nodeUpgrade.Arch, "oldVersion": nodeUpgrade.OldVersion, "newVersion": nodeUpgrade.NewVersion, "isOn": nodeUpgrade.Node.IsOn, "isUp": nodeUpgrade.Node.IsUp, "accessIP": accessIP, "login": login, "loginParams": loginParams, "installStatus": installStatusMap, }) } totalCount += len(nodeMaps) clusterMaps = append(clusterMaps, maps.Map{ "id": cluster.Id, "name": cluster.Name, "nodes": nodeMaps, "count": len(nodeMaps), }) } if len(clusterMaps) == 0 { return nil } return maps.Map{ "name": "边缘节点", "code": "node", "clusters": clusterMaps, "count": totalCount, } } // loadHTTPDNSModule 加载HTTPDNS模块的待升级信息 func (this *IndexAction) loadHTTPDNSModule() maps.Map { clustersResp, err := this.RPC().HTTPDNSClusterRPC().ListHTTPDNSClusters(this.AdminContext(), &pb.ListHTTPDNSClustersRequest{ Offset: 0, Size: 10000, }) if err != nil { return nil } var clusterMaps []maps.Map totalCount := 0 for _, cluster := range clustersResp.Clusters { resp, err := this.RPC().HTTPDNSNodeRPC().FindAllUpgradeHTTPDNSNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeHTTPDNSNodesWithClusterIdRequest{ ClusterId: cluster.Id, }) if err != nil { continue } if len(resp.Nodes) == 0 { continue } var nodeMaps []maps.Map for _, nodeUpgrade := range resp.Nodes { if nodeUpgrade.Node == nil { continue } installStatusMap := decodeInstallStatusFromJSON(nodeUpgrade.Node.InstallStatusJSON) accessIP, login, loginParams := decodeNodeAccessInfo(nodeUpgrade.Node.StatusJSON, nodeUpgrade.Node.NodeLogin, nodeUpgrade.Node.Name) nodeMaps = append(nodeMaps, maps.Map{ "id": nodeUpgrade.Node.Id, "name": nodeUpgrade.Node.Name, "os": nodeUpgrade.Os, "arch": nodeUpgrade.Arch, "oldVersion": nodeUpgrade.OldVersion, "newVersion": nodeUpgrade.NewVersion, "isOn": nodeUpgrade.Node.IsOn, "isUp": nodeUpgrade.Node.IsUp, "accessIP": accessIP, "login": login, "loginParams": loginParams, "installStatus": installStatusMap, }) } totalCount += len(nodeMaps) clusterMaps = append(clusterMaps, maps.Map{ "id": cluster.Id, "name": cluster.Name, "nodes": nodeMaps, "count": len(nodeMaps), }) } if len(clusterMaps) == 0 { return nil } return maps.Map{ "name": "HTTPDNS节点", "code": "httpdns", "clusters": clusterMaps, "count": totalCount, } } // decodeInstallStatusFromPB 从 protobuf InstallStatus 解码安装状态 func decodeInstallStatusFromPB(status *pb.NodeInstallStatus) maps.Map { if status == nil { return nil } // 历史成功状态,在待升级列表中忽略 if status.IsFinished && status.IsOk { return nil } return maps.Map{ "isRunning": status.IsRunning, "isFinished": status.IsFinished, "isOk": status.IsOk, "error": status.Error, "errorCode": status.ErrorCode, "updatedAt": status.UpdatedAt, } } // decodeInstallStatusFromJSON 从 JSON 字节解码安装状态 func decodeInstallStatusFromJSON(raw []byte) maps.Map { if len(raw) == 0 { return nil } result := maps.Map{} _ = json.Unmarshal(raw, &result) isFinished, _ := result["isFinished"].(bool) isOk, _ := result["isOk"].(bool) if isFinished && isOk { return nil } return result } // decodeNodeAccessInfo 从节点状态和登录信息中提取 accessIP、login、loginParams func decodeNodeAccessInfo(statusJSON []byte, nodeLogin *pb.NodeLogin, nodeName string) (accessIP string, login maps.Map, loginParams maps.Map) { // 从 statusJSON 中提取 hostIP 作为 accessIP if len(statusJSON) > 0 { statusMap := maps.Map{} _ = json.Unmarshal(statusJSON, &statusMap) accessIP = strings.TrimSpace(statusMap.GetString("hostIP")) } if len(accessIP) == 0 { accessIP = strings.TrimSpace(nodeName) } // 解码 login 信息 if nodeLogin != nil { login = maps.Map{ "id": nodeLogin.Id, "name": nodeLogin.Name, "type": nodeLogin.Type, } if len(nodeLogin.Params) > 0 { loginParams = maps.Map{} _ = json.Unmarshal(nodeLogin.Params, &loginParams) } } return }