103 lines
4.1 KiB
Go
103 lines
4.1 KiB
Go
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
|
|
}
|