package service import ( "context" "encoding/base64" "fmt" "os" "github.com/google/uuid" "github.com/ocdp/cluster-service/internal/domain/entity" "github.com/ocdp/cluster-service/internal/domain/repository" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) // ClusterService 集群管理领域服务 type ClusterService struct { clusterRepo repository.ClusterRepository } // NewClusterService 创建集群服务 func NewClusterService(clusterRepo repository.ClusterRepository) *ClusterService { return &ClusterService{ clusterRepo: clusterRepo, } } // CreateCluster 创建新集群 func (s *ClusterService) CreateCluster(ctx context.Context, cluster *entity.Cluster) error { // 生成 ID cluster.ID = uuid.New().String() // 验证 if err := cluster.Validate(); err != nil { return err } // 检查是否已存在 existingCluster, _ := s.clusterRepo.GetByName(ctx, cluster.Name) if existingCluster != nil { return entity.ErrClusterExists } return s.clusterRepo.Create(ctx, cluster) } // GetCluster 获取集群 func (s *ClusterService) GetCluster(ctx context.Context, id string) (*entity.Cluster, error) { return s.clusterRepo.GetByID(ctx, id) } // UpdateCluster 更新集群 func (s *ClusterService) UpdateCluster(ctx context.Context, cluster *entity.Cluster) error { // 检查是否存在 _, err := s.clusterRepo.GetByID(ctx, cluster.ID) if err != nil { return entity.ErrClusterNotFound } // 验证 if err := cluster.Validate(); err != nil { return err } return s.clusterRepo.Update(ctx, cluster) } // DeleteCluster 删除集群 func (s *ClusterService) DeleteCluster(ctx context.Context, id string) error { // 检查是否存在 _, err := s.clusterRepo.GetByID(ctx, id) if err != nil { return entity.ErrClusterNotFound } return s.clusterRepo.Delete(ctx, id) } // ListClusters 列出所有集群 func (s *ClusterService) ListClusters(ctx context.Context) ([]*entity.Cluster, error) { return s.clusterRepo.List(ctx) } // ListByWorkspace 列出指定 workspace 的集群(包括共享集群) func (s *ClusterService) ListByWorkspace(ctx context.Context, workspaceID string) ([]*entity.Cluster, error) { return s.clusterRepo.GetByWorkspace(ctx, workspaceID) } // GetSharedClusters 获取所有共享集群 func (s *ClusterService) GetSharedClusters(ctx context.Context) ([]*entity.Cluster, error) { return s.clusterRepo.GetShared(ctx) } // TestConnection 测试集群连接是否可用 func (s *ClusterService) TestConnection(ctx context.Context, cluster *entity.Cluster) error { // Mock 模式直接返回成功 if os.Getenv("ADAPTER_MODE") == "mock" { return nil } // 尝试创建 k8s client config, err := s.createRestConfig(cluster) if err != nil { return fmt.Errorf("failed to create k8s config: %w", err) } // 设置超时 config.Timeout = 30 * 1000000000 // 30秒 (nanoseconds) clientset, err := kubernetes.NewForConfig(config) if err != nil { return fmt.Errorf("failed to create k8s client: %w", err) } // 测试连接 - 获取 version 信息 version, err := clientset.ServerVersion() if err != nil { return fmt.Errorf("failed to connect to cluster: %w", err) } if version == nil { return fmt.Errorf("cluster returned nil version") } return nil } // createRestConfig 从 cluster 实体创建 k8s REST 配置 func (s *ClusterService) createRestConfig(cluster *entity.Cluster) (*rest.Config, error) { // 优先使用 kubeconfig 格式(如果 CAData 包含完整的 kubeconfig 内容) if len(cluster.CAData) > 100 && (cluster.CAData[:11] == "apiVersion:" || cluster.CAData[:5] == "kind:") { return clientcmd.RESTConfigFromKubeConfig([]byte(cluster.CAData)) } // 使用证书或 token 认证 config := &rest.Config{ Host: cluster.Host, } if cluster.CertData != "" && cluster.KeyData != "" { // 尝试解码 base64 编码的证书,如果失败则尝试原始 PEM var caData, certData, keyData []byte var decodeErr error // 先尝试 base64 解码 caData, decodeErr = base64.StdEncoding.DecodeString(cluster.CAData) if decodeErr != nil { // base64 解码失败,可能是原始 PEM caData = []byte(cluster.CAData) } certData, decodeErr = base64.StdEncoding.DecodeString(cluster.CertData) if decodeErr != nil { certData = []byte(cluster.CertData) } keyData, decodeErr = base64.StdEncoding.DecodeString(cluster.KeyData) if decodeErr != nil { keyData = []byte(cluster.KeyData) } config.TLSClientConfig = rest.TLSClientConfig{ CAData: caData, CertData: certData, KeyData: keyData, Insecure: false, } } else if cluster.Token != "" { config.BearerToken = cluster.Token } else { // 尝试使用本地 kubeconfig kubeconfig := os.Getenv("KUBECONFIG") if kubeconfig == "" { kubeconfig = ".kube/config" } // 尝试从文件加载 kubeconfig if _, err := os.Stat(kubeconfig); err != nil { return nil, fmt.Errorf("no valid credentials found for cluster %s (no cert/key/token, and kubeconfig file not found: %s)", cluster.Name, kubeconfig) } return clientcmd.BuildConfigFromFlags("", kubeconfig) } return config, nil }