feat: 接通 PostgreSQL 角色与数据库创建闭环
This commit is contained in:
@@ -17,10 +17,11 @@ import (
|
||||
)
|
||||
|
||||
type databaseOptions struct {
|
||||
secretNamespace string
|
||||
rootCert string
|
||||
credentialMount string
|
||||
credentialPrefix string
|
||||
secretNamespace string
|
||||
rootCert string
|
||||
credentialMount string
|
||||
credentialPrefix string
|
||||
provisionResources bool
|
||||
}
|
||||
|
||||
func (o *databaseOptions) bindFlags(flags *flag.FlagSet) {
|
||||
@@ -29,6 +30,7 @@ func (o *databaseOptions) bindFlags(flags *flag.FlagSet) {
|
||||
flags.StringVar(&o.rootCert, "database-root-cert", "", "PostgreSQL 管理连接信任的公开 CA bundle 路径")
|
||||
flags.StringVar(&o.credentialMount, "database-credential-mount", "", "应用凭据 KV v2 mount;为空时不启用凭据准备")
|
||||
flags.StringVar(&o.credentialPrefix, "database-credential-prefix", "applications", "应用凭据路径前缀;已有固定位置不随配置变化迁移")
|
||||
flags.BoolVar(&o.provisionResources, "database-provision-resources", false, "启用 PostgreSQL 角色和数据库创建;需要 Instance 管理凭据及 OpenBao 凭据准备")
|
||||
}
|
||||
|
||||
func (o databaseOptions) configureManager(options *ctrl.Options) {
|
||||
@@ -40,25 +42,30 @@ func (o databaseOptions) configureManager(options *ctrl.Options) {
|
||||
// registerDatabaseControllers 封装 Database 的内部装配,并返回在 manager 停止后执行的清理。
|
||||
func registerDatabaseControllers(ctx context.Context, manager ctrl.Manager, options databaseOptions, baoClient *bao.Client) (func(), error) {
|
||||
closeDatabaseConnections := func() {}
|
||||
var backend *application.InstanceService
|
||||
if options.secretNamespace != "" {
|
||||
service, err := setupInstanceObservation(manager, options.secretNamespace, options.rootCert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set up Instance observation: %w", err)
|
||||
}
|
||||
closeDatabaseConnections = service.Close
|
||||
backend = service
|
||||
}
|
||||
if err := wireBindingController(manager.GetClient(), manager.GetAPIReader()).SetupWithManager(ctx, manager); err != nil {
|
||||
closeDatabaseConnections()
|
||||
return nil, fmt.Errorf("set up Database binding controller: %w", err)
|
||||
}
|
||||
if err := setupCredentialPreparation(manager, options, baoClient); err != nil {
|
||||
if err := registerDatabaseSupplyController(manager, options, baoClient, backend); err != nil {
|
||||
closeDatabaseConnections()
|
||||
return nil, fmt.Errorf("set up Database credential preparation: %w", err)
|
||||
return nil, fmt.Errorf("register Database supply controller: %w", err)
|
||||
}
|
||||
return closeDatabaseConnections, nil
|
||||
}
|
||||
|
||||
func setupCredentialPreparation(manager ctrl.Manager, options databaseOptions, baoClient *bao.Client) error {
|
||||
func registerDatabaseSupplyController(manager ctrl.Manager, options databaseOptions, baoClient *bao.Client, backend *application.InstanceService) error {
|
||||
if options.provisionResources && (backend == nil || options.credentialMount == "") {
|
||||
return fmt.Errorf("database resource provisioning requires management Secret namespace and credential mount")
|
||||
}
|
||||
if options.credentialMount == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -69,6 +76,9 @@ func setupCredentialPreparation(manager ctrl.Manager, options databaseOptions, b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if options.provisionResources {
|
||||
return wireProvisioningController(manager.GetClient(), manager.GetAPIReader(), store, backend).SetupWithManager(manager)
|
||||
}
|
||||
return wireCredentialController(manager.GetClient(), manager.GetAPIReader(), store).SetupWithManager(manager)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,3 +26,13 @@ func wireInstanceController(writer client.Client, reader client.Reader, observer
|
||||
service := &application.InstanceReconciliation{Resources: resources, Observer: observer}
|
||||
return databasecontroller.NewInstanceReconciler(writer, service, resources, namespace)
|
||||
}
|
||||
|
||||
func wireProvisioningController(writer client.Client, reader client.Reader, store application.CredentialStore, backend application.ProvisioningBackend) *databasecontroller.ProvisioningReconciler {
|
||||
resources := &kubernetes.ProvisioningResources{Client: writer, Reader: reader}
|
||||
credentialResources := &kubernetes.CredentialResources{Client: writer, Reader: reader}
|
||||
service := &application.DatabaseReconciliation{
|
||||
Credentials: &application.CredentialPreparation{Resources: credentialResources, Store: store},
|
||||
Provisioning: &application.DatabaseProvisioning{Resources: resources, Credentials: store, Backend: backend},
|
||||
}
|
||||
return databasecontroller.NewProvisioningReconciler(writer, service)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ func TestDatabaseExplicitWiring(t *testing.T) {
|
||||
if !ok || resources.Reader != directReader || resources.Client != writer || reconciler.Service.Observer != observer || reconciler.Presenter != resources {
|
||||
t.Fatal("Instance 的服务或读取边界未按依赖图注入")
|
||||
}
|
||||
provisioner := wireProvisioningController(writer, directReader, nil, observer)
|
||||
provisioningResources, ok := provisioner.Service.Provisioning.Resources.(*kubernetes.ProvisioningResources)
|
||||
if !ok || provisioningResources.Client != writer || provisioningResources.Reader != directReader || provisioner.Service.Provisioning.Backend != observer {
|
||||
t.Fatal("资源供应必须复用已有 Instance 管理服务和 API 客户端")
|
||||
}
|
||||
}
|
||||
|
||||
// 防止领域重新依赖用例/存储,也防止 controller 再次私自构造具体 adapter。
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestBootstrapWithRealAPIServer(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
options.database.credentialMount = "secret"
|
||||
if err := setupCredentialPreparation(manager, options.database, fixtureClient); err != nil {
|
||||
if err := registerDatabaseSupplyController(manager, options.database, fixtureClient, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
done := make(chan error, 1)
|
||||
|
||||
@@ -63,12 +63,15 @@ func TestCredentialPreparationOptions(t *testing.T) {
|
||||
if options.database.credentialMount != "applications-kv" || options.database.credentialPrefix != "database" {
|
||||
t.Fatal("凭据准备参数未传入领域装配")
|
||||
}
|
||||
if err := setupCredentialPreparation(nil, options.database, nil); err == nil {
|
||||
if err := registerDatabaseSupplyController(nil, options.database, nil, nil); err == nil {
|
||||
t.Fatal("启用凭据准备必须有显式配置的认证 client")
|
||||
}
|
||||
if err := setupCredentialPreparation(nil, databaseOptions{}, nil); err != nil {
|
||||
if err := registerDatabaseSupplyController(nil, databaseOptions{}, nil, nil); err != nil {
|
||||
t.Fatal("默认停用凭据准备不应要求后端")
|
||||
}
|
||||
if err := registerDatabaseSupplyController(nil, databaseOptions{provisionResources: true}, nil, nil); err == nil {
|
||||
t.Fatal("启用资源供应必须有管理连接及凭据准备")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerFlagOverrides(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user