fix: dashboard metrics 慢查询优化(index+UNIX_TIMESTAMP分组) + 批量升级版本号修复
This commit is contained in:
1 parent
c515f6ed03
commit
20f64e3851
5 files changed
+222
-19
No files matched your search
@@ -1806,6 +1806,32 @@ func (h *KubernetesHandler) resolveClusterCode(code string) int {
|
||||
return int(cluster.ID)
|
||||
}
|
||||
|
||||
// formatAge converts a K8s creationTimestamp (RFC3339) to a human-readable relative age
|
||||
func formatAge(creationTimestamp string) string {
|
||||
if creationTimestamp == "" {
|
||||
return "-"
|
||||
}
|
||||
t, err := time.Parse(time.RFC3339, creationTimestamp)
|
||||
if err != nil {
|
||||
return creationTimestamp
|
||||
}
|
||||
d := time.Since(t)
|
||||
switch {
|
||||
case d < time.Minute:
|
||||
return fmt.Sprintf("%ds", int(d.Seconds()))
|
||||
case d < time.Hour:
|
||||
return fmt.Sprintf("%dm", int(d.Minutes()))
|
||||
case d < 24*time.Hour:
|
||||
return fmt.Sprintf("%dh", int(d.Hours()))
|
||||
case d < 30*24*time.Hour:
|
||||
return fmt.Sprintf("%dd", int(d.Hours()/24))
|
||||
case d < 365*24*time.Hour:
|
||||
return fmt.Sprintf("%dM", int(d.Hours()/(24*30)))
|
||||
default:
|
||||
return fmt.Sprintf("%dy", int(d.Hours()/(24*365)))
|
||||
}
|
||||
}
|
||||
|
||||
// getK8sClientByCode resolves cluster by code, falls back to ID
|
||||
func (h *KubernetesHandler) getK8sClientByCode(c *gin.Context) (*k8sclient.Client, *models.KubernetesCluster, error) {
|
||||
clusterID, _ := strconv.Atoi(c.Param("id"))
|
||||
@@ -2571,6 +2597,12 @@ func (h *KubernetesHandler) GetResourceYAMLNS(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
@@ -2627,6 +2659,12 @@ func (h *KubernetesHandler) UpdateResourceYAMLNS(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
@@ -2663,6 +2701,12 @@ func resourceAPIPath(ns, resource, name string) (string, error) {
|
||||
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)
|
||||
}
|
||||
@@ -3744,3 +3788,151 @@ func (h *KubernetesHandler) GetMultiClusterComparison(c *gin.Context) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RBAC — ServiceAccount / Role / RoleBinding (code-based)
|
||||
// ============================================================================
|
||||
|
||||
// ListServiceAccountsNS lists ServiceAccounts in a namespace
|
||||
func (h *KubernetesHandler) ListServiceAccountsNS(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
|
||||
}
|
||||
sas, err := client.ListServiceAccounts(c.Param("namespace"))
|
||||
if err != nil {
|
||||
c.JSON(500, models.Response{Code: 500, Message: "K8s API error: " + err.Error()})
|
||||
return
|
||||
}
|
||||
type SASummary struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
SecretCount int `json:"secret_count"`
|
||||
ImagePullSecrets []string `json:"image_pull_secrets"`
|
||||
Age string `json:"age"`
|
||||
}
|
||||
result := make([]SASummary, 0, len(sas))
|
||||
for _, sa := range sas {
|
||||
ipsNames := make([]string, 0, len(sa.ImagePullSecrets))
|
||||
for _, ips := range sa.ImagePullSecrets {
|
||||
ipsNames = append(ipsNames, ips.Name)
|
||||
}
|
||||
result = append(result, SASummary{
|
||||
Namespace: sa.Metadata.Namespace,
|
||||
Name: sa.Metadata.Name,
|
||||
SecretCount: len(sa.Secrets),
|
||||
ImagePullSecrets: ipsNames,
|
||||
Age: formatAge(sa.Metadata.CreationTimestamp),
|
||||
})
|
||||
}
|
||||
c.JSON(200, models.Response{Code: 200, Data: result})
|
||||
}
|
||||
|
||||
// ListRolesNS lists Roles in a namespace
|
||||
func (h *KubernetesHandler) ListRolesNS(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
|
||||
}
|
||||
roles, err := client.ListRoles(c.Param("namespace"))
|
||||
if err != nil {
|
||||
c.JSON(500, models.Response{Code: 500, Message: "K8s API error: " + err.Error()})
|
||||
return
|
||||
}
|
||||
type RoleSummary struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
RuleCount int `json:"rule_count"`
|
||||
Verbs []string `json:"verbs"`
|
||||
Resources []string `json:"resources"`
|
||||
Age string `json:"age"`
|
||||
}
|
||||
result := make([]RoleSummary, 0, len(roles.Items))
|
||||
for _, r := range roles.Items {
|
||||
allVerbs := []string{}
|
||||
allResources := []string{}
|
||||
seen := map[string]bool{}
|
||||
for _, rule := range r.Rules {
|
||||
for _, v := range rule.Verbs {
|
||||
if !seen[v] {
|
||||
allVerbs = append(allVerbs, v)
|
||||
seen[v] = true
|
||||
}
|
||||
}
|
||||
for _, res := range rule.Resources {
|
||||
if !seen[res] {
|
||||
allResources = append(allResources, res)
|
||||
seen[res] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
result = append(result, RoleSummary{
|
||||
Namespace: r.Metadata.Namespace,
|
||||
Name: r.Metadata.Name,
|
||||
RuleCount: len(r.Rules),
|
||||
Verbs: allVerbs,
|
||||
Resources: allResources,
|
||||
Age: formatAge(r.Metadata.CreationTimestamp),
|
||||
})
|
||||
}
|
||||
c.JSON(200, models.Response{Code: 200, Data: result})
|
||||
}
|
||||
|
||||
// ListRoleBindingsNS lists RoleBindings in a namespace
|
||||
func (h *KubernetesHandler) ListRoleBindingsNS(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
|
||||
}
|
||||
rbs, err := client.ListRoleBindings(c.Param("namespace"))
|
||||
if err != nil {
|
||||
c.JSON(500, models.Response{Code: 500, Message: "K8s API error: " + err.Error()})
|
||||
return
|
||||
}
|
||||
type RBSummary struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
RoleRefKind string `json:"role_ref_kind"`
|
||||
RoleRefName string `json:"role_ref_name"`
|
||||
Subjects []string `json:"subjects"`
|
||||
Age string `json:"age"`
|
||||
}
|
||||
result := make([]RBSummary, 0, len(rbs.Items))
|
||||
for _, rb := range rbs.Items {
|
||||
subjStrs := make([]string, 0, len(rb.Subjects))
|
||||
for _, s := range rb.Subjects {
|
||||
if s.Namespace != "" && s.Namespace != rb.Metadata.Namespace {
|
||||
subjStrs = append(subjStrs, fmt.Sprintf("%s/%s (%s)", s.Kind, s.Name, s.Namespace))
|
||||
} else {
|
||||
subjStrs = append(subjStrs, fmt.Sprintf("%s/%s", s.Kind, s.Name))
|
||||
}
|
||||
}
|
||||
result = append(result, RBSummary{
|
||||
Namespace: rb.Metadata.Namespace,
|
||||
Name: rb.Metadata.Name,
|
||||
RoleRefKind: rb.RoleRef.Kind,
|
||||
RoleRefName: rb.RoleRef.Name,
|
||||
Subjects: subjStrs,
|
||||
Age: formatAge(rb.Metadata.CreationTimestamp),
|
||||
})
|
||||
}
|
||||
c.JSON(200, models.Response{Code: 200, Data: result})
|
||||
}
|
||||
Reference in new issue
Block a user