260 lines
10 KiB
Go
260 lines
10 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"ezops/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func (h *KubernetesHandler) GetResourceYAMLNS(c *gin.Context) {
|
|
clusterID := h.resolveClusterCode(c.Param("clusterCode"))
|
|
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
|
|
}
|
|
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, model.Response{Code: 400, Message: "unsupported resource type: " + resource})
|
|
return
|
|
}
|
|
yamlBytes, err := client.GetResourceYAML(apiPath)
|
|
if err != nil {
|
|
c.JSON(500, model.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, 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
|
|
}
|
|
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, model.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, model.Response{Code: 400, Message: "unsupported resource type: " + resource})
|
|
return
|
|
}
|
|
if err := client.UpdateResourceYAML(apiPath, yamlData); err != nil {
|
|
c.JSON(500, model.Response{Code: 500, Message: "更新失败: " + err.Error()})
|
|
return
|
|
}
|
|
c.JSON(200, model.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, 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
|
|
}
|
|
ns := c.Param("namespace")
|
|
resource := c.Param("resource")
|
|
name := c.Param("name")
|
|
apiPath, err := resourceAPIPath(ns, resource, name)
|
|
if err != nil {
|
|
c.JSON(400, model.Response{Code: 400, Message: err.Error()})
|
|
return
|
|
}
|
|
if err := client.DeleteResource(apiPath); err != nil {
|
|
c.JSON(500, model.Response{Code: 500, Message: "删除失败: " + err.Error()})
|
|
return
|
|
}
|
|
c.JSON(200, model.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, 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
|
|
}
|
|
|
|
var req struct {
|
|
YAML string `json:"yaml"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(400, model.Response{Code: 400, Message: "参数错误: " + err.Error()})
|
|
return
|
|
}
|
|
if req.YAML == "" {
|
|
c.JSON(400, model.Response{Code: 400, Message: "YAML 内容不能为空"})
|
|
return
|
|
}
|
|
|
|
result, err := client.ApplyYAML(req.YAML)
|
|
if err != nil {
|
|
c.JSON(500, model.Response{Code: 500, Message: "Apply 失败: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, model.Response{Code: 200, Message: "资源已应用", Data: result})
|
|
}
|
|
|
|
// ============================================================================
|
|
// ReplicaSet 列表 / Deployment 回滚 / 历史版本 // Logan.Li
|
|
// ============================================================================
|
|
|
|
// ListReplicaSetsNS 列出 namespace 下的 ReplicaSet
|