This commit is contained in:
Logan committed 2026-07-09 14:16:56 +08:00
1 parent 0690285147
commit 5618cd2738
154 files changed
+5021 -3161

No files matched your search

+139 -48
View File
@@ -198,8 +198,8 @@ func (h *KubernetesHandler) ExecTerminal(c *gin.Context) {
// Dial K8s API server via WebSocket with channel protocol subprotocol
k8sDialer := &websocket.Dialer{
TLSClientConfig: client.TLSConfig,
Subprotocols: []string{"v4.channel.k8s.io", "v3.channel.k8s.io", "v5.channel.k8s.io"},
TLSClientConfig: client.TLSConfig,
Subprotocols: []string{"v4.channel.k8s.io", "v3.channel.k8s.io", "v5.channel.k8s.io"},
HandshakeTimeout: 10 * time.Second,
}
k8sHeader := http.Header{}
@@ -354,30 +354,46 @@ func (h *KubernetesHandler) ExecInPodNS(c *gin.Context) {
// Internal helpers that resolve clusterCode and inject namespace
func (h *KubernetesHandler) listPodsWithNS(c *gin.Context, ns string) {
clusterID, _ := strconv.Atoi(c.Param("id"))
if clusterID == 0 { clusterID = h.resolveClusterCode(c.Param("clusterCode")) }
if clusterID == 0 {
clusterID = h.resolveClusterCode(c.Param("clusterCode"))
}
client, _, err := h.getK8sClient(clusterID)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: err.Error()})
return
}
pods, err := client.ListPods()
if err != nil { c.JSON(500, model.Response{Code: 500, Message: "K8s API error: " + err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
return
}
// M2: inject pod metrics (best-effort) // Logan.Li
podMetrics := buildPodMetricsMap(client, ns)
type PodSummary struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Status string `json:"status"`
NodeName string `json:"node_name"`
IP string `json:"ip"`
RestartCount int `json:"restart_count"`
Image string `json:"image"`
CPUUsageM string `json:"cpu_usage_m,omitempty"`
MemUsageMi string `json:"mem_usage_mi,omitempty"`
Namespace string `json:"namespace"`
Name string `json:"name"`
Status string `json:"status"`
NodeName string `json:"node_name"`
IP string `json:"ip"`
RestartCount int `json:"restart_count"`
Image string `json:"image"`
CPUUsageM string `json:"cpu_usage_m,omitempty"`
MemUsageMi string `json:"mem_usage_mi,omitempty"`
Conditions []k8sclient.PodCondition `json:"conditions,omitempty"`
}
result := []PodSummary{}
for _, p := range pods.Items {
if p.Metadata.Namespace != ns { continue }
image := ""; if len(p.Spec.Containers) > 0 { image = p.Spec.Containers[0].Image }
restarts := 0; if len(p.Status.ContainerStatuses) > 0 { restarts = p.Status.ContainerStatuses[0].RestartCount }
if p.Metadata.Namespace != ns {
continue
}
image := ""
if len(p.Spec.Containers) > 0 {
image = p.Spec.Containers[0].Image
}
restarts := 0
if len(p.Status.ContainerStatuses) > 0 {
restarts = p.Status.ContainerStatuses[0].RestartCount
}
ps := PodSummary{
Namespace: p.Metadata.Namespace,
Name: p.Metadata.Name,
@@ -398,7 +414,9 @@ func (h *KubernetesHandler) listPodsWithNS(c *gin.Context, ns string) {
}
func (h *KubernetesHandler) getPodLogsWithNS(c *gin.Context, ns string) {
clusterID, _ := strconv.Atoi(c.Param("id"))
if clusterID == 0 { clusterID = h.resolveClusterCode(c.Param("clusterCode")) }
if clusterID == 0 {
clusterID = h.resolveClusterCode(c.Param("clusterCode"))
}
podName := c.Param("podName")
stream := c.Query("stream") == "true"
// 日志增强参数:container / sinceTime / previous / tailLines(兼容 tail) // Logan.Li
@@ -416,34 +434,61 @@ func (h *KubernetesHandler) getPodLogsWithNS(c *gin.Context, ns string) {
Previous: c.Query("previous") == "true",
}
client, _, err := h.getK8sClient(clusterID)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: err.Error()})
return
}
if stream {
body, err := client.GetPodLogsStreamWithOptions(ns, podName, opts)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: "获取日志流失败: " + err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: "获取日志流失败: " + err.Error()})
return
}
defer body.Close()
c.Header("Content-Type", "text/plain; charset=utf-8")
c.Header("Cache-Control", "no-cache")
c.Stream(func(w io.Writer) bool {
buf := make([]byte, 4096); n, err := body.Read(buf)
if n > 0 { w.Write(buf[:n]); return true }
buf := make([]byte, 4096)
n, err := body.Read(buf)
if n > 0 {
w.Write(buf[:n])
return true
}
return err == nil
})
return
}
logs, err := client.GetPodLogsWithOptions(ns, podName, opts)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: "获取日志失败: " + err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: "获取日志失败: " + err.Error()})
return
}
c.JSON(200, model.Response{Code: 200, Message: "success", Data: gin.H{"logs": logs, "pod": podName, "namespace": ns}})
}
func (h *KubernetesHandler) execInPodWithNS(c *gin.Context, ns string) {
clusterID, _ := strconv.Atoi(c.Param("id"))
if clusterID == 0 { clusterID = h.resolveClusterCode(c.Param("clusterCode")) }
if clusterID == 0 {
clusterID = h.resolveClusterCode(c.Param("clusterCode"))
}
podName := c.Param("podName")
var req struct { Command []string `json:"command"`; Container string `json:"container"` }
if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, model.Response{Code: 400, Message: "参数错误"}); return }
var req struct {
Command []string `json:"command"`
Container string `json:"container"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, model.Response{Code: 400, Message: "参数错误"})
return
}
client, _, err := h.getK8sClient(clusterID)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: err.Error()})
return
}
result, err := client.ExecInPod(ns, podName, req.Container, req.Command)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: "执行失败: " + err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: "执行失败: " + err.Error()})
return
}
c.JSON(200, model.Response{Code: 200, Data: result})
}
@@ -454,11 +499,20 @@ func (h *KubernetesHandler) execInPodWithNS(c *gin.Context, ns string) {
// ListDeploymentsAllNS lists all deployments across all namespaces using clusterCode.
func (h *KubernetesHandler) ListPodsAllNS(c *gin.Context) {
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
if clusterID == 0 { c.JSON(500, model.Response{Code: 500, Message: "cluster not found"}); return }
if clusterID == 0 {
c.JSON(500, model.Response{Code: 500, Message: "cluster not found"})
return
}
client, _, err := h.getK8sClient(clusterID)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: err.Error()})
return
}
pods, err := client.ListPods()
if err != nil { c.JSON(500, model.Response{Code: 500, Message: "K8s API error: " + err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: "K8s API error: " + err.Error()})
return
}
// M2: inject pod metrics (best-effort) // Logan.Li
podMetrics := buildPodMetricsMap(client, "")
type PodSummary struct {
@@ -474,8 +528,14 @@ func (h *KubernetesHandler) ListPodsAllNS(c *gin.Context) {
}
result := []PodSummary{}
for _, p := range pods.Items {
image := ""; if len(p.Spec.Containers) > 0 { image = p.Spec.Containers[0].Image }
restarts := 0; if len(p.Status.ContainerStatuses) > 0 { restarts = p.Status.ContainerStatuses[0].RestartCount }
image := ""
if len(p.Spec.Containers) > 0 {
image = p.Spec.Containers[0].Image
}
restarts := 0
if len(p.Status.ContainerStatuses) > 0 {
restarts = p.Status.ContainerStatuses[0].RestartCount
}
ps := PodSummary{
Namespace: p.Metadata.Namespace,
Name: p.Metadata.Name,
@@ -559,58 +619,80 @@ func parseMemResource(val string) int64 {
// formatCpuFromNano converts K8s metrics CPU (nanocores "80410173n") to millicores "80m"
func formatCpuFromNano(raw string) string {
if raw == "" { return "" }
if raw == "" {
return ""
}
val := raw
val = strings.TrimSuffix(val, "n")
var nano int64
fmt.Sscanf(val, "%d", &nano)
milli := nano / 1000000
if milli < 1 { milli = 1 }
if milli < 1 {
milli = 1
}
return fmt.Sprintf("%dm", milli)
}
// parseCpuNanoToMilli converts K8s metrics CPU (nanocores "100354992n") to millicores (int) // Logan.Li
func parseCpuNanoToMilli(raw string) int {
if raw == "" { return 0 }
if raw == "" {
return 0
}
val := strings.TrimSuffix(raw, "n")
var nano int64
fmt.Sscanf(val, "%d", &nano)
milli := nano / 1000000
if milli < 1 { milli = 1 }
if milli < 1 {
milli = 1
}
return int(milli)
}
// formatMemFromKi converts K8s metrics memory (Ki "2154292Ki") to Mi "2105Mi"
func formatMemFromKi(raw string) string {
if raw == "" { return "" }
if raw == "" {
return ""
}
val := strings.TrimSuffix(raw, "Ki")
var ki int64
fmt.Sscanf(val, "%d", &ki)
mi := ki / 1024
if mi < 1 { mi = 1 }
if mi < 1 {
mi = 1
}
return fmt.Sprintf("%dMi", mi)
}
// parseMemKiToMi converts K8s metrics memory (Ki "2154292Ki") to Mi (int) // Logan.Li
func parseMemKiToMi(raw string) int {
if raw == "" { return 0 }
if raw == "" {
return 0
}
val := strings.TrimSuffix(raw, "Ki")
var ki int64
fmt.Sscanf(val, "%d", &ki)
return int(ki / 1024)
}
// GetResourceYAMLNS returns the YAML of a K8s resource
func (h *KubernetesHandler) GetPodMetrics(c *gin.Context) {
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
if clusterID == 0 { c.JSON(500, model.Response{Code: 500, Message: "cluster not found"}); return }
if clusterID == 0 {
c.JSON(500, model.Response{Code: 500, Message: "cluster not found"})
return
}
client, _, err := h.getK8sClient(clusterID)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: err.Error()})
return
}
ns := c.Query("namespace")
metrics, err := client.ListPodMetrics(ns)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: "metrics API error: " + err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: "metrics API error: " + err.Error()})
return
}
type ContainerMetric struct {
Name string `json:"name"`
@@ -643,13 +725,22 @@ func (h *KubernetesHandler) DeletePodNS(c *gin.Context) {
}
func (h *KubernetesHandler) deletePodWithNS(c *gin.Context, ns string) {
clusterID, _ := strconv.Atoi(c.Param("id"))
if clusterID == 0 { clusterID = h.resolveClusterCode(c.Param("clusterCode")) }
if clusterID == 0 {
clusterID = h.resolveClusterCode(c.Param("clusterCode"))
}
podName := c.Param("podName")
if podName == "" { c.JSON(400, model.Response{Code: 400, Message: "缺少 podName"}); return }
if podName == "" {
c.JSON(400, model.Response{Code: 400, Message: "缺少 podName"})
return
}
client, _, err := h.getK8sClient(clusterID)
if err != nil { c.JSON(500, model.Response{Code: 500, Message: err.Error()}); return }
if err != nil {
c.JSON(500, model.Response{Code: 500, Message: err.Error()})
return
}
if err := client.DeletePod(ns, podName); err != nil {
c.JSON(500, model.Response{Code: 500, Message: "删除 Pod 失败: " + err.Error()}); return
c.JSON(500, model.Response{Code: 500, Message: "删除 Pod 失败: " + err.Error()})
return
}
c.JSON(200, model.Response{Code: 200, Message: fmt.Sprintf("Pod %s 已删除", podName)})
}