update
This commit is contained in:
1 parent
0690285147
commit
5618cd2738
154 files changed
+5021
-3161
No files matched your search
+139
-48
@@ -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)})
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user