- 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
292 lines
8.7 KiB
Go
292 lines
8.7 KiB
Go
package output
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
helmMock "github.com/ocdp/cluster-service/internal/adapter/output/helm/mock"
|
||
helmReal "github.com/ocdp/cluster-service/internal/adapter/output/helm/real"
|
||
"github.com/ocdp/cluster-service/internal/adapter/output/k8s"
|
||
ociMock "github.com/ocdp/cluster-service/internal/adapter/output/oci/mock"
|
||
ociReal "github.com/ocdp/cluster-service/internal/adapter/output/oci/real"
|
||
"github.com/ocdp/cluster-service/internal/adapter/output/persistence/mock"
|
||
"github.com/ocdp/cluster-service/internal/adapter/output/persistence/postgres"
|
||
"github.com/ocdp/cluster-service/internal/domain/repository"
|
||
"github.com/ocdp/cluster-service/internal/pkg/crypto"
|
||
)
|
||
|
||
// AdapterMode 适配器模式
|
||
type AdapterMode string
|
||
|
||
const (
|
||
ModeMock AdapterMode = "mock" // Mock 模式(内存存储,用于开发调试)
|
||
// 默认模式:连接真实 PostgreSQL 和服务(任何非 "mock" 的值都是默认模式)
|
||
)
|
||
|
||
// AdapterFactory 适配器工厂
|
||
// 用于创建所有 Output Adapters,支持 Mock 和真实实现切换
|
||
type AdapterFactory struct {
|
||
mode AdapterMode
|
||
encryptor crypto.Encryptor // 加密器(用于敏感数据加密)
|
||
|
||
// 数据库连接字符串(非 Mock 模式需要)
|
||
dbConnString string
|
||
|
||
// 数据库连接(非 Mock 模式)
|
||
db *postgres.DB
|
||
}
|
||
|
||
// NewAdapterFactory 创建适配器工厂
|
||
func NewAdapterFactory(mode AdapterMode, encryptor crypto.Encryptor, dbConnString string) *AdapterFactory {
|
||
return &AdapterFactory{
|
||
mode: mode,
|
||
encryptor: encryptor,
|
||
dbConnString: dbConnString,
|
||
}
|
||
}
|
||
|
||
// CreateUserRepository 创建用户仓储
|
||
func (f *AdapterFactory) CreateUserRepository() (repository.UserRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewUserRepositoryMock(), nil
|
||
}
|
||
|
||
// 默认:真实实现(PostgreSQL)
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewUserRepository(f.db), nil
|
||
}
|
||
|
||
// CreateClusterRepository 创建集群仓储
|
||
func (f *AdapterFactory) CreateClusterRepository() (repository.ClusterRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewClusterRepositoryMock(f.encryptor), nil
|
||
}
|
||
|
||
// 默认:真实实现(PostgreSQL)
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewClusterRepository(f.db, f.encryptor), nil
|
||
}
|
||
|
||
// CreateRegistryRepository 创建 Registry 仓储
|
||
func (f *AdapterFactory) CreateRegistryRepository() (repository.RegistryRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewRegistryRepositoryMock(f.encryptor), nil
|
||
}
|
||
|
||
// 默认:真实实现(PostgreSQL)
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewRegistryRepository(f.db, f.encryptor), nil
|
||
}
|
||
|
||
// CreateInstanceRepository 创建实例仓储
|
||
func (f *AdapterFactory) CreateInstanceRepository() (repository.InstanceRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewInstanceRepositoryMock(), nil
|
||
}
|
||
|
||
// 默认:真实实现(PostgreSQL)
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewInstanceRepository(f.db), nil
|
||
}
|
||
|
||
func (f *AdapterFactory) CreateWorkspaceRepository() (repository.WorkspaceRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewWorkspaceRepositoryMock(), nil
|
||
}
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewWorkspaceRepository(f.db), nil
|
||
}
|
||
|
||
func (f *AdapterFactory) CreateWorkspaceClusterBindingRepository() (repository.WorkspaceClusterBindingRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewWorkspaceClusterBindingRepositoryMock(), nil
|
||
}
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewWorkspaceClusterBindingRepository(f.db), nil
|
||
}
|
||
|
||
func (f *AdapterFactory) CreateAuditLogRepository() (repository.AuditLogRepository, error) {
|
||
if f.mode == ModeMock {
|
||
return mock.NewAuditLogRepositoryMock(), nil
|
||
}
|
||
if err := f.ensureDBConnection(); err != nil {
|
||
return nil, err
|
||
}
|
||
return postgres.NewAuditLogRepository(f.db), nil
|
||
}
|
||
|
||
// CreateOCIClient 创建 OCI 客户端
|
||
func (f *AdapterFactory) CreateOCIClient() (repository.OCIClient, error) {
|
||
if f.mode == ModeMock {
|
||
return ociMock.NewOCIClientMock(), nil
|
||
}
|
||
|
||
// 默认:真实实现(ORAS SDK)
|
||
return ociReal.NewOCIClient(), nil
|
||
}
|
||
|
||
// CreateHelmClient 创建 Helm 客户端
|
||
func (f *AdapterFactory) CreateHelmClient() (repository.HelmClient, error) {
|
||
if f.mode == ModeMock {
|
||
return helmMock.NewHelmClientMock(), nil
|
||
}
|
||
|
||
// 默认:真实实现(Helm SDK)
|
||
return helmReal.NewHelmClient(), nil
|
||
}
|
||
|
||
// CreateMetricsClient 创建 Metrics 客户端
|
||
func (f *AdapterFactory) CreateMetricsClient(clusterRepo repository.ClusterRepository) repository.MetricsClient {
|
||
// Metrics client 总是使用真实的 Kubernetes API
|
||
return k8s.NewMetricsClient(clusterRepo)
|
||
}
|
||
|
||
// CreateEntryClient 创建实例入口查询客户端
|
||
func (f *AdapterFactory) CreateEntryClient() repository.InstanceEntryClient {
|
||
return k8s.NewEntryClient()
|
||
}
|
||
|
||
func (f *AdapterFactory) CreateDiagnosticsClient() repository.InstanceDiagnosticsClient {
|
||
if f.mode == ModeMock {
|
||
return k8s.NewMockDiagnosticsClient()
|
||
}
|
||
return k8s.NewDiagnosticsClient()
|
||
}
|
||
|
||
func (f *AdapterFactory) CreateTenantKubeClient() repository.TenantKubeClient {
|
||
if f.mode == ModeMock {
|
||
return k8s.NewMockTenantClient()
|
||
}
|
||
return k8s.NewTenantClient()
|
||
}
|
||
|
||
// CreateAllRepositories 一次性创建所有 Repositories
|
||
func (f *AdapterFactory) CreateAllRepositories() (*Repositories, error) {
|
||
userRepo, err := f.CreateUserRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create user repository: %w", err)
|
||
}
|
||
|
||
clusterRepo, err := f.CreateClusterRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create cluster repository: %w", err)
|
||
}
|
||
|
||
registryRepo, err := f.CreateRegistryRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create registry repository: %w", err)
|
||
}
|
||
|
||
instanceRepo, err := f.CreateInstanceRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create instance repository: %w", err)
|
||
}
|
||
|
||
workspaceRepo, err := f.CreateWorkspaceRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create workspace repository: %w", err)
|
||
}
|
||
|
||
bindingRepo, err := f.CreateWorkspaceClusterBindingRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create workspace cluster binding repository: %w", err)
|
||
}
|
||
|
||
auditRepo, err := f.CreateAuditLogRepository()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create audit log repository: %w", err)
|
||
}
|
||
|
||
ociClient, err := f.CreateOCIClient()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create OCI client: %w", err)
|
||
}
|
||
|
||
helmClient, err := f.CreateHelmClient()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to create Helm client: %w", err)
|
||
}
|
||
|
||
// 创建 Metrics client(依赖 clusterRepo)
|
||
metricsClient := f.CreateMetricsClient(clusterRepo)
|
||
entryClient := f.CreateEntryClient()
|
||
diagnosticsClient := f.CreateDiagnosticsClient()
|
||
tenantClient := f.CreateTenantKubeClient()
|
||
|
||
return &Repositories{
|
||
UserRepo: userRepo,
|
||
WorkspaceRepo: workspaceRepo,
|
||
BindingRepo: bindingRepo,
|
||
AuditRepo: auditRepo,
|
||
ClusterRepo: clusterRepo,
|
||
RegistryRepo: registryRepo,
|
||
InstanceRepo: instanceRepo,
|
||
OCIClient: ociClient,
|
||
HelmClient: helmClient,
|
||
MetricsClient: metricsClient,
|
||
EntryClient: entryClient,
|
||
DiagnosticsClient: diagnosticsClient,
|
||
TenantKubeClient: tenantClient,
|
||
}, nil
|
||
}
|
||
|
||
// Repositories 所有仓储的集合
|
||
type Repositories struct {
|
||
UserRepo repository.UserRepository
|
||
WorkspaceRepo repository.WorkspaceRepository
|
||
BindingRepo repository.WorkspaceClusterBindingRepository
|
||
AuditRepo repository.AuditLogRepository
|
||
ClusterRepo repository.ClusterRepository
|
||
RegistryRepo repository.RegistryRepository
|
||
InstanceRepo repository.InstanceRepository
|
||
OCIClient repository.OCIClient
|
||
HelmClient repository.HelmClient
|
||
MetricsClient repository.MetricsClient
|
||
EntryClient repository.InstanceEntryClient
|
||
DiagnosticsClient repository.InstanceDiagnosticsClient
|
||
TenantKubeClient repository.TenantKubeClient
|
||
}
|
||
|
||
// ensureDBConnection 确保数据库连接已建立
|
||
func (f *AdapterFactory) ensureDBConnection() error {
|
||
if f.db != nil {
|
||
return nil
|
||
}
|
||
|
||
if f.dbConnString == "" {
|
||
return fmt.Errorf("database connection string is required (set DATABASE_URL environment variable)")
|
||
}
|
||
|
||
db, err := postgres.NewDB(f.dbConnString)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to connect to database: %w", err)
|
||
}
|
||
|
||
// 初始化数据库 schema
|
||
if err := db.InitSchema(); err != nil {
|
||
return fmt.Errorf("failed to initialize database schema: %w", err)
|
||
}
|
||
|
||
f.db = db
|
||
return nil
|
||
}
|
||
|
||
// Close 关闭工厂资源
|
||
func (f *AdapterFactory) Close() error {
|
||
if f.db != nil {
|
||
return f.db.Close()
|
||
}
|
||
return nil
|
||
}
|