67 lines
2.0 KiB
Go
67 lines
2.0 KiB
Go
// Package provisioning 保存 PostgreSQL 资源供应的确认与恢复规则,不依赖后端或 API。
|
|
package provisioning
|
|
|
|
import "fmt"
|
|
|
|
type Phase string
|
|
|
|
const (
|
|
Pending Phase = "Pending"
|
|
CreatingRole Phase = "CreatingRole"
|
|
CreatingDatabase Phase = "CreatingDatabase"
|
|
Available Phase = "Available"
|
|
Conflict Phase = "Conflict"
|
|
Unavailable Phase = "Unavailable"
|
|
Stopped Phase = "Stopped"
|
|
)
|
|
|
|
// OID 是已成功创建并回读的对象身份,不是从名称推导出的管理授权。
|
|
// 仅保存在 CR;不建立 PostgreSQL registry,也不承诺备份还原后的自动认领。
|
|
type State struct {
|
|
RoleOID uint32
|
|
DatabaseOID uint32
|
|
Phase Phase
|
|
Message string
|
|
}
|
|
|
|
type Observation struct {
|
|
RoleOID uint32
|
|
DatabaseOID uint32
|
|
RoleSafe bool
|
|
OwnerOID uint32
|
|
PublicConnect bool
|
|
AllowConnections bool
|
|
}
|
|
|
|
func (s State) WithPhase(phase Phase, message string) State {
|
|
s.Phase, s.Message = phase, message
|
|
return s
|
|
}
|
|
|
|
func (s State) Resume() (State, bool) {
|
|
if s.Phase == Conflict {
|
|
return s, false
|
|
}
|
|
if (s.Phase == CreatingRole && s.RoleOID == 0) || (s.Phase == CreatingDatabase && s.DatabaseOID == 0) {
|
|
return s.WithPhase(Conflict, "外部创建未留下成功确认;请核对目标角色和数据库,不自动认领或重复创建"), false
|
|
}
|
|
return s, true
|
|
}
|
|
|
|
// Check 既阻止未知同名对象,也拒绝已确认对象消失、被重建或权限漂移。
|
|
func (s State) Check(o Observation) error {
|
|
if s.RoleOID != o.RoleOID {
|
|
return fmt.Errorf("角色身份不匹配:记录 OID=%d,观察 OID=%d", s.RoleOID, o.RoleOID)
|
|
}
|
|
if s.DatabaseOID != o.DatabaseOID {
|
|
return fmt.Errorf("数据库身份不匹配:记录 OID=%d,观察 OID=%d", s.DatabaseOID, o.DatabaseOID)
|
|
}
|
|
if o.RoleOID != 0 && !o.RoleSafe {
|
|
return fmt.Errorf("已确认角色的登录属性、特权或成员关系发生变化")
|
|
}
|
|
if o.DatabaseOID != 0 && o.OwnerOID != s.RoleOID {
|
|
return fmt.Errorf("数据库 owner 与已确认角色不匹配")
|
|
}
|
|
return nil
|
|
}
|