package handlers import ( "fmt" "io" "strings" "ezops/internal/models" "github.com/gin-gonic/gin" ) func (h *KubernetesHandler) GetResourceYAMLNS(c *gin.Context) { clusterID := h.resolveClusterCode(c.Param("clusterCode")) if clusterID == 0 { c.JSON(500, models.Response{Code: 500, Message: "cluster not found"}); return } client, _, err := h.getK8sClient(clusterID) if err != nil { c.JSON(500, models.Response{Code: 500, Message: err.Error()}); return } ns := c.Param("namespace") resource := c.Param("resource") // Fallback: specific routes (statefulsets/:name/yaml, daemonsets/:name/yaml) have no :resource param if resource == "" { pathParts := strings.Split(c.Request.URL.Path, "/") for _, p := range pathParts { if p == "statefulsets" || p == "daemonsets" || p == "deployments" || p == "services" || p == "configmaps" || p == "secrets" { resource = p break } } } name := c.Param("name") // Build API path based on resource type var apiPath string switch resource { case "deployments", "deployment": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/%s", ns, name) case "statefulsets", "statefulset": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/statefulsets/%s", ns, name) case "daemonsets", "daemonset": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/daemonsets/%s", ns, name) case "replicasets", "replicaset": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/replicasets/%s", ns, name) case "pods", "pod": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", ns, name) case "services", "service": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/services/%s", ns, name) case "ingresses", "ingress": apiPath = fmt.Sprintf("/apis/networking.k8s.io/v1/namespaces/%s/ingresses/%s", ns, name) case "configmaps", "configmap": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/configmaps/%s", ns, name) case "secrets", "secret": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/secrets/%s", ns, name) case "pvcs", "persistentvolumeclaim": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/persistentvolumeclaims/%s", ns, name) case "hpas", "hpa", "horizontalpodautoscaler": apiPath = fmt.Sprintf("/apis/autoscaling/v2/namespaces/%s/horizontalpodautoscalers/%s", ns, name) case "cronjobs", "cronjob": apiPath = fmt.Sprintf("/apis/batch/v1/namespaces/%s/cronjobs/%s", ns, name) case "serviceaccounts", "serviceaccount": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts/%s", ns, name) case "roles", "role": apiPath = fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/roles/%s", ns, name) case "rolebindings", "rolebinding": apiPath = fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/rolebindings/%s", ns, name) default: c.JSON(400, models.Response{Code: 400, Message: "unsupported resource type: " + resource}); return } yamlBytes, err := client.GetResourceYAML(apiPath) if err != nil { c.JSON(500, models.Response{Code: 500, Message: "获取YAML失败: " + err.Error()}); return } c.Header("Content-Type", "application/yaml") c.String(200, string(yamlBytes)) } // UpdateResourceYAMLNS applies YAML to update a K8s resource func (h *KubernetesHandler) UpdateResourceYAMLNS(c *gin.Context) { clusterID := h.resolveClusterCode(c.Param("clusterCode")) if clusterID == 0 { c.JSON(500, models.Response{Code: 500, Message: "cluster not found"}); return } client, _, err := h.getK8sClient(clusterID) if err != nil { c.JSON(500, models.Response{Code: 500, Message: err.Error()}); return } ns := c.Param("namespace") resource := c.Param("resource") // Fallback: specific routes (statefulsets/:name/yaml, daemonsets/:name/yaml) have no :resource param if resource == "" { pathParts := strings.Split(c.Request.URL.Path, "/") for _, p := range pathParts { if p == "statefulsets" || p == "daemonsets" || p == "deployments" || p == "services" || p == "configmaps" || p == "secrets" { resource = p break } } } name := c.Param("name") yamlData, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(400, models.Response{Code: 400, Message: "读取body失败"}); return } var apiPath string switch resource { case "deployments", "deployment": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/%s", ns, name) case "statefulsets", "statefulset": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/statefulsets/%s", ns, name) case "daemonsets", "daemonset": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/daemonsets/%s", ns, name) case "replicasets", "replicaset": apiPath = fmt.Sprintf("/apis/apps/v1/namespaces/%s/replicasets/%s", ns, name) case "pods", "pod": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", ns, name) case "services", "service": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/services/%s", ns, name) case "ingresses", "ingress": apiPath = fmt.Sprintf("/apis/networking.k8s.io/v1/namespaces/%s/ingresses/%s", ns, name) case "configmaps", "configmap": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/configmaps/%s", ns, name) case "secrets", "secret": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/secrets/%s", ns, name) case "pvcs", "persistentvolumeclaim": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/persistentvolumeclaims/%s", ns, name) case "hpas", "hpa", "horizontalpodautoscaler": apiPath = fmt.Sprintf("/apis/autoscaling/v2/namespaces/%s/horizontalpodautoscalers/%s", ns, name) case "cronjobs", "cronjob": apiPath = fmt.Sprintf("/apis/batch/v1/namespaces/%s/cronjobs/%s", ns, name) case "serviceaccounts", "serviceaccount": apiPath = fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts/%s", ns, name) case "roles", "role": apiPath = fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/roles/%s", ns, name) case "rolebindings", "rolebinding": apiPath = fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/rolebindings/%s", ns, name) default: c.JSON(400, models.Response{Code: 400, Message: "unsupported resource type: " + resource}); return } if err := client.UpdateResourceYAML(apiPath, yamlData); err != nil { c.JSON(500, models.Response{Code: 500, Message: "更新失败: " + err.Error()}); return } c.JSON(200, models.Response{Code: 200, Message: "YAML已应用"}) } // resourceAPIPath builds the K8s API path for common resource types (shared by Get/Update/Delete) func resourceAPIPath(ns, resource, name string) (string, error) { switch resource { case "deployments", "deployment": return fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/%s", ns, name), nil case "statefulsets", "statefulset": return fmt.Sprintf("/apis/apps/v1/namespaces/%s/statefulsets/%s", ns, name), nil case "daemonsets", "daemonset": return fmt.Sprintf("/apis/apps/v1/namespaces/%s/daemonsets/%s", ns, name), nil case "replicasets", "replicaset": return fmt.Sprintf("/apis/apps/v1/namespaces/%s/replicasets/%s", ns, name), nil case "pods", "pod": return fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", ns, name), nil case "services", "service": return fmt.Sprintf("/api/v1/namespaces/%s/services/%s", ns, name), nil case "ingresses", "ingress": return fmt.Sprintf("/apis/networking.k8s.io/v1/namespaces/%s/ingresses/%s", ns, name), nil case "configmaps", "configmap": return fmt.Sprintf("/api/v1/namespaces/%s/configmaps/%s", ns, name), nil case "secrets", "secret": return fmt.Sprintf("/api/v1/namespaces/%s/secrets/%s", ns, name), nil case "pvcs", "persistentvolumeclaim": return fmt.Sprintf("/api/v1/namespaces/%s/persistentvolumeclaims/%s", ns, name), nil case "hpas", "hpa", "horizontalpodautoscaler": return fmt.Sprintf("/apis/autoscaling/v2/namespaces/%s/horizontalpodautoscalers/%s", ns, name), nil case "cronjobs", "cronjob": return fmt.Sprintf("/apis/batch/v1/namespaces/%s/cronjobs/%s", ns, name), nil case "serviceaccounts", "serviceaccount": return fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts/%s", ns, name), nil case "roles", "role": return fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/roles/%s", ns, name), nil case "rolebindings", "rolebinding": return fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/rolebindings/%s", ns, name), nil default: return "", fmt.Errorf("unsupported resource type: %s", resource) } } // DeleteResourceYAMLNS deletes a K8s resource (Service/Ingress/ConfigMap etc.) func (h *KubernetesHandler) DeleteResourceYAMLNS(c *gin.Context) { clusterID := h.resolveClusterCode(c.Param("clusterCode")) if clusterID == 0 { c.JSON(500, models.Response{Code: 500, Message: "cluster not found"}); return } client, _, err := h.getK8sClient(clusterID) if err != nil { c.JSON(500, models.Response{Code: 500, Message: err.Error()}); return } ns := c.Param("namespace") resource := c.Param("resource") name := c.Param("name") apiPath, err := resourceAPIPath(ns, resource, name) if err != nil { c.JSON(400, models.Response{Code: 400, Message: err.Error()}); return } if err := client.DeleteResource(apiPath); err != nil { c.JSON(500, models.Response{Code: 500, Message: "删除失败: " + err.Error()}); return } c.JSON(200, models.Response{Code: 200, Message: "资源已删除"}) } // getK8sClient builds a K8s API client from cluster's stored kubeconfig func (h *KubernetesHandler) ApplyYAML(c *gin.Context) { clusterID := h.resolveClusterCode(c.Param("clusterCode")) if clusterID == 0 { c.JSON(500, models.Response{Code: 500, Message: "cluster not found"}); return } client, _, err := h.getK8sClient(clusterID) if err != nil { c.JSON(500, models.Response{Code: 500, Message: err.Error()}); return } var req struct { YAML string `json:"yaml"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, models.Response{Code: 400, Message: "参数错误: " + err.Error()}); return } if req.YAML == "" { c.JSON(400, models.Response{Code: 400, Message: "YAML 内容不能为空"}); return } result, err := client.ApplyYAML(req.YAML) if err != nil { c.JSON(500, models.Response{Code: 500, Message: "Apply 失败: " + err.Error()}); return } c.JSON(200, models.Response{Code: 200, Message: "资源已应用", Data: result}) } // ============================================================================ // ReplicaSet 列表 / Deployment 回滚 / 历史版本 // Logan.Li // ============================================================================ // ListReplicaSetsNS 列出 namespace 下的 ReplicaSet