- Add Workspace domain (entity, repository, service, handler, DTO) - Add multi-tenant K8s client with tenant binding and quota management - Add K8s diagnostics client (instance diagnostics) - Add authorization middleware (authz package) - Restructure frontend to feature-based architecture (features/) - Add User Management page in configuration - Add AccessDenied page and route guards - Refactor shared components (form inputs, layout, UI) - Update Tailwind config for new design system - Add comprehensive documentation (docs/, tasks/, plans) - Improve cluster service with better kubeconfig handling - Add tests for crypto, config, helm client, tenant binding
139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"github.com/google/uuid"
|
|
"github.com/ocdp/cluster-service/internal/domain/entity"
|
|
"github.com/ocdp/cluster-service/internal/domain/repository"
|
|
"github.com/ocdp/cluster-service/internal/pkg/authz"
|
|
)
|
|
|
|
// 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 {
|
|
principal, err := authz.RequirePrincipal(ctx)
|
|
if err != nil {
|
|
return entity.ErrUnauthorized
|
|
}
|
|
// 生成 ID
|
|
cluster.ID = uuid.New().String()
|
|
cluster.OwnerID = principal.UserID
|
|
cluster.WorkspaceID = principal.WorkspaceID
|
|
if principal.IsAdmin() && cluster.WorkspaceID == "" {
|
|
cluster.WorkspaceID = entity.DefaultWorkspaceID
|
|
}
|
|
if !principal.IsAdmin() && cluster.Visibility == authz.VisibilityGlobalShared {
|
|
return entity.ErrForbidden
|
|
}
|
|
cluster.Visibility = authz.NormalizeVisibility(principal.Role, cluster.Visibility)
|
|
|
|
// 验证
|
|
if err := cluster.Validate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 检查是否已存在
|
|
clusters, _ := s.clusterRepo.List(ctx)
|
|
for _, existingCluster := range clusters {
|
|
if existingCluster.Name == cluster.Name && existingCluster.WorkspaceID == cluster.WorkspaceID && existingCluster.OwnerID == cluster.OwnerID {
|
|
return entity.ErrClusterExists
|
|
}
|
|
}
|
|
|
|
return s.clusterRepo.Create(ctx, cluster)
|
|
}
|
|
|
|
// GetCluster 获取集群
|
|
func (s *ClusterService) GetCluster(ctx context.Context, id string) (*entity.Cluster, error) {
|
|
principal, err := authz.RequirePrincipal(ctx)
|
|
if err != nil {
|
|
return nil, entity.ErrUnauthorized
|
|
}
|
|
cluster, err := s.clusterRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !authz.CanReadResource(principal, cluster.WorkspaceID, cluster.OwnerID, cluster.Visibility) {
|
|
return nil, entity.ErrClusterNotFound
|
|
}
|
|
return cluster, nil
|
|
}
|
|
|
|
// UpdateCluster 更新集群
|
|
func (s *ClusterService) UpdateCluster(ctx context.Context, cluster *entity.Cluster) error {
|
|
principal, err := authz.RequirePrincipal(ctx)
|
|
if err != nil {
|
|
return entity.ErrUnauthorized
|
|
}
|
|
// 检查是否存在
|
|
existing, err := s.clusterRepo.GetByID(ctx, cluster.ID)
|
|
if err != nil {
|
|
return entity.ErrClusterNotFound
|
|
}
|
|
if !authz.CanWriteResource(principal, existing.WorkspaceID, existing.OwnerID, existing.Visibility) {
|
|
return entity.ErrForbidden
|
|
}
|
|
cluster.WorkspaceID = existing.WorkspaceID
|
|
cluster.OwnerID = existing.OwnerID
|
|
if principal.IsAdmin() {
|
|
cluster.Visibility = authz.NormalizeVisibility(principal.Role, cluster.Visibility)
|
|
} else {
|
|
cluster.Visibility = existing.Visibility
|
|
}
|
|
|
|
// 验证
|
|
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 {
|
|
principal, err := authz.RequirePrincipal(ctx)
|
|
if err != nil {
|
|
return entity.ErrUnauthorized
|
|
}
|
|
// 检查是否存在
|
|
cluster, err := s.clusterRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return entity.ErrClusterNotFound
|
|
}
|
|
if !authz.CanWriteResource(principal, cluster.WorkspaceID, cluster.OwnerID, cluster.Visibility) {
|
|
return entity.ErrForbidden
|
|
}
|
|
|
|
return s.clusterRepo.Delete(ctx, id)
|
|
}
|
|
|
|
// ListClusters 列出所有集群
|
|
func (s *ClusterService) ListClusters(ctx context.Context) ([]*entity.Cluster, error) {
|
|
principal, err := authz.RequirePrincipal(ctx)
|
|
if err != nil {
|
|
return nil, entity.ErrUnauthorized
|
|
}
|
|
clusters, err := s.clusterRepo.List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
visible := make([]*entity.Cluster, 0, len(clusters))
|
|
for _, cluster := range clusters {
|
|
if authz.CanReadResource(principal, cluster.WorkspaceID, cluster.OwnerID, cluster.Visibility) {
|
|
visible = append(visible, cluster)
|
|
}
|
|
}
|
|
return visible, nil
|
|
}
|