feat: 接通 PostgreSQL 角色与数据库创建闭环
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"git.ddupan.top/panxiao81/ayatori/internal/database/domain/credential"
|
||||
"git.ddupan.top/panxiao81/ayatori/internal/database/domain/instance"
|
||||
"git.ddupan.top/panxiao81/ayatori/internal/database/domain/provisioning"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrResourceConflict = errors.New("PostgreSQL resource conflict; manual resolution required")
|
||||
ErrResourceUncertain = errors.New("PostgreSQL creation outcome uncertain; manual resolution required")
|
||||
ErrResourceUnavailable = errors.New("PostgreSQL operation unavailable before creation")
|
||||
)
|
||||
|
||||
// ResourceDatabase 是供应实际需要的后端能力,不暴露 SQL 或任意回调给用例。
|
||||
type ResourceDatabase interface {
|
||||
InspectResources(context.Context, string, string) (provisioning.Observation, error)
|
||||
CreateLoginRole(context.Context, credential.ApplicationCredential) (uint32, error)
|
||||
CreateOwnedDatabase(context.Context, string, string, uint32) (uint32, error)
|
||||
ConfigureAccess(context.Context, string, string, provisioning.State) error
|
||||
}
|
||||
|
||||
type ProvisioningBackend interface {
|
||||
InspectResources(context.Context, instance.ObservationTarget, string, string) (provisioning.Observation, error)
|
||||
CreateLoginRole(context.Context, instance.ObservationTarget, credential.ApplicationCredential) (uint32, error)
|
||||
CreateOwnedDatabase(context.Context, instance.ObservationTarget, string, string, uint32) (uint32, error)
|
||||
ConfigureAccess(context.Context, instance.ObservationTarget, string, string, provisioning.State) error
|
||||
}
|
||||
|
||||
func (s *InstanceService) InspectResources(ctx context.Context, target instance.ObservationTarget, name, role string) (provisioning.Observation, error) {
|
||||
var result provisioning.Observation
|
||||
err := s.withManagementConnection(ctx, target, false, func(database Database) (err error) {
|
||||
result, err = database.InspectResources(ctx, name, role)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return provisioning.Observation{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *InstanceService) CreateLoginRole(ctx context.Context, target instance.ObservationTarget, value credential.ApplicationCredential) (uint32, error) {
|
||||
var oid uint32
|
||||
err := s.withManagementConnection(ctx, target, true, func(database Database) (err error) {
|
||||
oid, err = database.CreateLoginRole(ctx, value)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return oid, nil
|
||||
}
|
||||
|
||||
func (s *InstanceService) CreateOwnedDatabase(ctx context.Context, target instance.ObservationTarget, name, role string, roleOID uint32) (uint32, error) {
|
||||
var oid uint32
|
||||
err := s.withManagementConnection(ctx, target, true, func(database Database) (err error) {
|
||||
oid, err = database.CreateOwnedDatabase(ctx, name, role, roleOID)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return oid, nil
|
||||
}
|
||||
|
||||
func (s *InstanceService) ConfigureAccess(ctx context.Context, target instance.ObservationTarget, name, role string, state provisioning.State) error {
|
||||
return s.withManagementConnection(ctx, target, false, func(database Database) error {
|
||||
return database.ConfigureAccess(ctx, name, role, state)
|
||||
})
|
||||
}
|
||||
|
||||
// 所有供应操作与观察共用同一个连接登记和 Secret 刷新边界。
|
||||
// 创建后凭据回读失败不能冒充明确未执行;用例必须保留不确定诊断。
|
||||
func (s *InstanceService) withManagementConnection(ctx context.Context, target instance.ObservationTarget, creating bool, operation func(Database) error) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
current, err := s.connection(ctx, target)
|
||||
if err != nil {
|
||||
return ErrResourceUnavailable
|
||||
}
|
||||
metadata, err := current.database.InspectManagement(ctx)
|
||||
checks := metadata.Management
|
||||
if err != nil || checks.Connection != instance.CheckPassed || checks.Roles != instance.CheckPassed || checks.Databases != instance.CheckPassed || checks.Grants != instance.CheckPassed {
|
||||
return ErrResourceUnavailable
|
||||
}
|
||||
if err := s.checkCredentials(ctx, target, current.credentials); err != nil {
|
||||
return ErrResourceUnavailable
|
||||
}
|
||||
if err := operation(current.database); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.checkCredentials(ctx, target, current.credentials); err != nil {
|
||||
if creating {
|
||||
return ErrResourceUncertain
|
||||
}
|
||||
return ErrResourceUnavailable
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user