package services import ( "context" "fmt" "regexp" "sort" "time" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "opsxloki/models" ) type ClusterDeploymentOption struct { Name string `json:"name"` Replicas int `json:"replicas"` ReadyReplicas int `json:"readyReplicas"` } var kubernetesNamespacePattern = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`) func ListClusterNamespaces(ctx context.Context, cluster *models.Cluster) ([]string, error) { client, err := clusterClient(cluster, 10*time.Second) if err != nil { return nil, err } items, err := client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { if apierrors.IsForbidden(err) { // 命名空间级 Token 无权枚举命名空间:返回空列表,由用户手填 return []string{}, nil } return nil, fmt.Errorf("读取集群命名空间失败: %w", err) } result := make([]string, 0, len(items.Items)) for _, item := range items.Items { result = append(result, item.Name) } sort.Strings(result) return result, nil } func ListClusterDeployments(ctx context.Context, cluster *models.Cluster, namespace string) ([]ClusterDeploymentOption, error) { if len(namespace) > 63 || !kubernetesNamespacePattern.MatchString(namespace) { return nil, fmt.Errorf("命名空间名称无效") } client, err := clusterClient(cluster, 10*time.Second) if err != nil { return nil, err } items, err := client.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("读取集群 Deployment 失败: %w", err) } result := make([]ClusterDeploymentOption, 0, len(items.Items)) for _, item := range items.Items { replicas := int32(1) if item.Spec.Replicas != nil { replicas = *item.Spec.Replicas } result = append(result, ClusterDeploymentOption{ Name: item.Name, Replicas: int(replicas), ReadyReplicas: int(item.Status.ReadyReplicas), }) } sort.Slice(result, func(i, j int) bool { return result[i].Name < result[j].Name }) return result, nil }