feat(k8s): A1 K8s 双路由体系统一 — code 路由为主, ID 路由标记 deprecated
后端: - 新增 4 条 all-NS code 路由: services/ingresses/cronjobs/nodes - ListNodes 改用 getK8sClientByCode (兼容 ID+code) - ID 域资源路由标记 // Deprecated 前端: - clusterApi/nodeApi/workloadApi 资源方法迁移到 code 路由 - KubernetesClusterDetail/TopologyView/KubernetesNodes 组件从 cluster.id 迁移到 cluster.code - 修复潜在 bug: ClusterOverviewPage 曾将 code 传给 ID-based getNodes 验证: 16/16 API 测试通过 (7 新 code 路由 + 3 旧 ID 兼容 + 6 回归) go build=✅ tsc -b=✅ npm build=✅
This commit is contained in:
1 parent
bb2b91ebe0
commit
00a98e210d
13 files changed
+241
-39
No files matched your search
@@ -100,6 +100,27 @@ func (h *KubernetesHandler) ListCronJobs(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: cronjobs.Items})
|
||||
}
|
||||
|
||||
// ListCronJobsAllNS lists all CronJobs across all namespaces using clusterCode.
|
||||
// Canonical route: GET /kubernetes/:clusterCode/cronjobs
|
||||
func (h *KubernetesHandler) ListCronJobsAllNS(c *gin.Context) {
|
||||
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
|
||||
if clusterID == 0 {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "cluster not found"})
|
||||
return
|
||||
}
|
||||
client, _, err := h.getK8sClient(clusterID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
cronjobs, err := client.ListCronJobs()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: cronjobs.Items})
|
||||
}
|
||||
|
||||
// CreateCronJob 创建 CronJob
|
||||
func (h *KubernetesHandler) CreateCronJob(c *gin.Context) {
|
||||
clusterID, _ := strconv.Atoi(c.Param("id"))
|
||||
|
||||
@@ -13,9 +13,8 @@ import (
|
||||
)
|
||||
|
||||
func (h *KubernetesHandler) ListNodes(c *gin.Context) {
|
||||
clusterID, _ := strconv.Atoi(c.Param("id"))
|
||||
|
||||
client, _, err := h.getK8sClient(clusterID)
|
||||
// 兼容 /kubernetes-clusters/:id/nodes (ID) 和 /kubernetes/:clusterCode/nodes (code)
|
||||
client, _, err := h.getK8sClientByCode(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
|
||||
return
|
||||
|
||||
@@ -99,6 +99,94 @@ func (h *KubernetesHandler) ListIngresses(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result})
|
||||
}
|
||||
|
||||
// ListServicesAllNS lists all Services across all namespaces using clusterCode.
|
||||
// Canonical route: GET /kubernetes/:clusterCode/services
|
||||
func (h *KubernetesHandler) ListServicesAllNS(c *gin.Context) {
|
||||
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
|
||||
if clusterID == 0 {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "cluster not found"})
|
||||
return
|
||||
}
|
||||
namespace := c.Query("namespace")
|
||||
client, _, err := h.getK8sClient(clusterID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
svcs, err := client.ListServices()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
|
||||
return
|
||||
}
|
||||
type SvcSummary struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
ClusterIP string `json:"cluster_ip"`
|
||||
Ports int `json:"port_count"`
|
||||
}
|
||||
result := []SvcSummary{}
|
||||
for _, s := range svcs.Items {
|
||||
if namespace != "" && s.Metadata.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
result = append(result, SvcSummary{
|
||||
Namespace: s.Metadata.Namespace,
|
||||
Name: s.Metadata.Name,
|
||||
Type: s.Spec.Type,
|
||||
ClusterIP: s.Spec.ClusterIP,
|
||||
Ports: len(s.Spec.Ports),
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result})
|
||||
}
|
||||
|
||||
// ListIngressesAllNS lists all Ingresses across all namespaces using clusterCode.
|
||||
// Canonical route: GET /kubernetes/:clusterCode/ingresses
|
||||
func (h *KubernetesHandler) ListIngressesAllNS(c *gin.Context) {
|
||||
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
|
||||
if clusterID == 0 {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "cluster not found"})
|
||||
return
|
||||
}
|
||||
namespace := c.Query("namespace")
|
||||
client, _, err := h.getK8sClient(clusterID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
ings, err := client.ListIngresses()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
|
||||
return
|
||||
}
|
||||
type IngSummary struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
Hosts []string `json:"hosts"`
|
||||
TLS bool `json:"tls"`
|
||||
}
|
||||
result := []IngSummary{}
|
||||
for _, ing := range ings.Items {
|
||||
if namespace != "" && ing.Metadata.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
hosts := []string{}
|
||||
for _, rule := range ing.Spec.Rules {
|
||||
if rule.Host != "" {
|
||||
hosts = append(hosts, rule.Host)
|
||||
}
|
||||
}
|
||||
result = append(result, IngSummary{
|
||||
Namespace: ing.Metadata.Namespace,
|
||||
Name: ing.Metadata.Name,
|
||||
Hosts: hosts,
|
||||
TLS: len(ing.Spec.TLS) > 0,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "success", Data: result})
|
||||
}
|
||||
|
||||
// GetTopology 获取集群拓扑布局 // Logan.Li
|
||||
func (h *KubernetesHandler) GetTopology(c *gin.Context) {
|
||||
clusterID := c.Param("id")
|
||||
|
||||
@@ -319,7 +319,11 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
|
||||
resourceModels.DELETE("/:id", resourceModelHandler.DeleteResourceModel)
|
||||
}
|
||||
|
||||
// K8s集群路由
|
||||
// ============================================================================
|
||||
// 集群管理路由 (cluster CRUD + sync + topology — 合法 ID 路由)
|
||||
// 资源列举路由 (nodes/pods/deployments/services/...) 保留向后兼容,
|
||||
// 但应优先使用 /kubernetes/:clusterCode/... (Kuboard code 路由)。详见 ROADMAP A1。
|
||||
// ============================================================================
|
||||
clusters := authorized.Group("/kubernetes-clusters")
|
||||
{
|
||||
clusters.GET("", k8sHandler.ListClusters)
|
||||
@@ -329,12 +333,12 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
|
||||
clusters.PUT("/:id", k8sHandler.UpdateCluster)
|
||||
clusters.DELETE("/:id", k8sHandler.DeleteCluster)
|
||||
clusters.POST("/:id/sync", k8sHandler.SyncCluster) // 同步集群资源 // Logan.Li
|
||||
clusters.GET("/:id/nodes", k8sHandler.ListNodes)
|
||||
clusters.GET("/:id/nodes/:name", k8sHandler.GetNodeDetail) // M10 节点详情 // Logan.Li
|
||||
clusters.GET("/:id/pods", k8sHandler.ListPods)
|
||||
clusters.GET("/:id/deployments", k8sHandler.ListDeployments)
|
||||
clusters.GET("/:id/services", k8sHandler.ListServices)
|
||||
clusters.GET("/:id/ingresses", k8sHandler.ListIngresses)
|
||||
clusters.GET("/:id/nodes", k8sHandler.ListNodes) // Deprecated: use GET /kubernetes/:clusterCode/nodes
|
||||
clusters.GET("/:id/nodes/:name", k8sHandler.GetNodeDetail) // M10 节点详情 // Logan.Li
|
||||
clusters.GET("/:id/pods", k8sHandler.ListPods) // Deprecated: use GET /kubernetes/:clusterCode/pods
|
||||
clusters.GET("/:id/deployments", k8sHandler.ListDeployments) // Deprecated: use GET /kubernetes/:clusterCode/deployments
|
||||
clusters.GET("/:id/services", k8sHandler.ListServices) // Deprecated: use GET /kubernetes/:clusterCode/services
|
||||
clusters.GET("/:id/ingresses", k8sHandler.ListIngresses) // Deprecated: use GET /kubernetes/:clusterCode/ingresses
|
||||
clusters.GET("/:id/topology", k8sHandler.GetTopology) // 获取拓扑布局 // Logan.Li
|
||||
clusters.PUT("/:id/topology", k8sHandler.SaveTopology) // 保存拓扑布局 // Logan.Li
|
||||
|
||||
@@ -385,6 +389,8 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
|
||||
|
||||
// Node detail (cluster-scoped, not namespace-scoped) // M10
|
||||
kubernetesRouter.GET("/:clusterCode/nodes/:name", k8sHandler.GetNodeDetail)
|
||||
// Cluster-scoped node list (canonical — replaces /kubernetes-clusters/:id/nodes)
|
||||
kubernetesRouter.GET("/:clusterCode/nodes", k8sHandler.ListNodes)
|
||||
|
||||
// Deployments
|
||||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/deployments", k8sHandler.ListDeploymentsNS)
|
||||
@@ -443,10 +449,13 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
|
||||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/detail", k8sHandler.GetNamespaceDetailHandler)
|
||||
kubernetesRouter.POST("/:clusterCode/namespaces", k8sHandler.CreateNamespaceHandler)
|
||||
kubernetesRouter.DELETE("/:clusterCode/namespaces/:namespace", k8sHandler.DeleteNamespaceHandler)
|
||||
// All-namespace listing (no ns filter)
|
||||
// All-namespace listing (no ns filter) — canonical code-based routes (A1)
|
||||
kubernetesRouter.GET("/:clusterCode/deployments", k8sHandler.ListDeploymentsAllNS)
|
||||
kubernetesRouter.GET("/:clusterCode/pods", k8sHandler.ListPodsAllNS)
|
||||
kubernetesRouter.GET("/:clusterCode/hpas", k8sHandler.ListHPAsAllNS)
|
||||
kubernetesRouter.GET("/:clusterCode/services", k8sHandler.ListServicesAllNS)
|
||||
kubernetesRouter.GET("/:clusterCode/ingresses", k8sHandler.ListIngressesAllNS)
|
||||
kubernetesRouter.GET("/:clusterCode/cronjobs", k8sHandler.ListCronJobsAllNS)
|
||||
|
||||
// 🆕 Pod Metrics (kubectl top pods)
|
||||
kubernetesRouter.GET("/:clusterCode/metrics/pods", k8sHandler.GetPodMetrics)
|
||||
|
||||
Reference in new issue
Block a user