docs: 按实例实际能力判定扩展支持 #10

Merged
panxiao81 merged 1 commits from docs/instance-extension-contract into main 2026-09-14 14:25:54 +00:00
7 changed files with 43 additions and 20 deletions
+5 -5
View File
@@ -40,15 +40,16 @@ cluster-scoped,short name 为 `pginstance`。
| `spec.adminCredentialRef.name` | string | 必填 | controller namespace 内的管理 Secret 名称 | | `spec.adminCredentialRef.name` | string | 必填 | controller namespace 内的管理 Secret 名称 |
| `spec.adminCredentialRef.usernameKey` | string | `username` | Secret data 中的键名 | | `spec.adminCredentialRef.usernameKey` | string | `username` | Secret data 中的键名 |
| `spec.adminCredentialRef.passwordKey` | string | `password` | Secret data 中的键名 | | `spec.adminCredentialRef.passwordKey` | string | `password` | Secret data 中的键名 |
| `spec.allowedExtensions` | set[string] | 空集合 | 合法 extension 名称的 allowlist |
`adminCredentialRef` 不接受 namespace 或 Bao path。管理 Secret 固定在 controller `adminCredentialRef` 不接受 namespace 或 Bao path。管理 Secret 固定在 controller
namespace,名称须合法,两个字段须存在且非空。管理员维护 ExternalSecret,由 ESO namespace,名称须合法,两个字段须存在且非空。管理员维护 ExternalSecret,由 ESO
同步;controller 只读管理 Secret,不创建或修改它。此为 2026-09-13 批准的修订, 同步;controller 只读管理 Secret,不创建或修改它。此为 2026-09-13 批准的修订,
现有 API types、生成 CRD 和 samples 尚未更新。 现有 API types、生成 CRD 和 samples 尚未更新。
Instance endpoint、管理凭据引用和 allowlist 可以修改。修改后 controller 重新验证; Instance endpoint、管理凭据引用可以修改。修改后 controller 重新验证。
删除 allowlist 项目不会自动从已有 Tenant database 删除 extension。 2026-09-14 修订:v1alpha1 不实现 allowedExtensions;现有 API types、生成 CRD 和
samples 中的字段待后续移除,不作为一个可配置但被忽略的策略保留。
扩展请求按目标 PostgreSQL 实际可安装列表判断,可用列表由应用层查询。
endpoint 由管理员负责,不校验变更前后是否同一物理服务器/registry,只重验新配置 endpoint 由管理员负责,不校验变更前后是否同一物理服务器/registry,只重验新配置
的连接与管理能力。新 UID 按新 Instance 处理,不授权接管旧 UID 的 Tenant 资源。 的连接与管理能力。新 UID 按新 Instance 处理,不授权接管旧 UID 的 Tenant 资源。
@@ -81,7 +82,7 @@ namespaced,short name 为 `pgtenant`。
| `spec.instanceRef` | string | 必填 | cluster-scoped Instance 名称 | | `spec.instanceRef` | string | 必填 | cluster-scoped Instance 名称 |
| `spec.database` | string | `metadata.name` | 合法 PostgreSQL identifier | | `spec.database` | string | `metadata.name` | 合法 PostgreSQL identifier |
| `spec.loginRole` | string | `metadata.name` | database owner 兼应用 login | | `spec.loginRole` | string | `metadata.name` | database owner 兼应用 login |
| `spec.extensions` | set[string] | 空集合 | 必须属于 Instance allowlist;成功创建后只允许追加 | | `spec.extensions` | set[string] | 空集合 | 必须属于目标实例实际可安装的扩展列表;成功创建后只允许追加 |
| `spec.credential.secretName` | string | `<instance>-<name>-postgresql` | 合法的同 namespace ESO target Secret 名称 | | `spec.credential.secretName` | string | `<instance>-<name>-postgresql` | 合法的同 namespace ESO target Secret 名称 |
| `spec.deletionPolicy` | enum | `Retain` | `Retain` 或 `Delete` | | `spec.deletionPolicy` | enum | `Retain` | `Retain` 或 `Delete` |
@@ -172,7 +173,6 @@ spec:
sslMode: verify-full sslMode: verify-full
adminCredentialRef: adminCredentialRef:
name: shared-postgresql-admin name: shared-postgresql-admin
allowedExtensions: [pg_trgm]
--- ---
apiVersion: database.ddupan.top/v1alpha1 apiVersion: database.ddupan.top/v1alpha1
kind: PostgreSQLTenant kind: PostgreSQLTenant
+2 -2
View File
@@ -35,8 +35,8 @@ controller 不运行 PostgreSQL/OpenBao,不管理 VM、存储、备份或 Open
## 资源模型 ## 资源模型
`PostgreSQLInstance` 是 cluster-scoped,由平台管理员创建,描述外部 PostgreSQL 的 `PostgreSQLInstance` 是 cluster-scoped,由平台管理员创建,描述外部 PostgreSQL 的
DNS host、IP host address、端口、管理 database、TLS 模式、管理 Secret 引用和 DNS host、IP host address、端口、管理 database、TLS 模式和管理 Secret 引用。
extension allowlist。 实际可安装扩展由应用层查询后交给领域对象判定,v1alpha1 不实现管理员 allowlist。
管理连接使用管理员维护的 ExternalSecret 经 ESO 同步到 controller namespace 的 管理连接使用管理员维护的 ExternalSecret 经 ESO 同步到 controller namespace 的
Secret;Instance 只选择 Secret 名称与字段,controller 只读,不直接从 Bao 获取 Secret;Instance 只选择 Secret 名称与字段,controller 只读,不直接从 Bao 获取
+1 -1
View File
@@ -69,7 +69,7 @@ base path 必须是合法 mount-relative path,不以 `/` 开头且不包含空
- 创建/修改受管 login role; - 创建/修改受管 login role;
- 创建 database 并指定 owner; - 创建 database 并指定 owner;
- 撤销 `PUBLIC` CONNECT、授予租户 role CONNECT; - 撤销 `PUBLIC` CONNECT、授予租户 role CONNECT;
- 连接租户 database 并创建 allowlist extension; - 连接租户 database 并创建实例实际支持、租户申请的 extension;
- 创建和维护 controller 专属 registry schema/table; - 创建和维护 controller 专属 registry schema/table;
- `Delete` 时禁止连接、终止目标 database session、删除已验证归属的 database/role。 - `Delete` 时禁止连接、终止目标 database session、删除已验证归属的 database/role。
+20 -4
View File
@@ -34,7 +34,7 @@ registry,不增加安装身份连续性检查;只使旧观察失效,按新
| revision | 正整数,期望配置版本 | metadata.generation | 本轮不可变;不是物理服务器版本 | | revision | 正整数,期望配置版本 | metadata.generation | 本轮不可变;不是物理服务器版本 |
| definition.endpoint | Endpoint:host、hostaddr、port、managementDatabase、tlsMode | CR spec | 本轮不可变;新配置重验 | | definition.endpoint | Endpoint:host、hostaddr、port、managementDatabase、tlsMode | CR spec | 本轮不可变;新配置重验 |
| definition.adminCredential | CredentialReference:name、usernameKey、passwordKey | CR spec | 只引用 controller namespace 的管理 Secret,不存明文 | | definition.adminCredential | CredentialReference:name、usernameKey、passwordKey | CR spec | 只引用 controller namespace 的管理 Secret,不存明文 |
| definition.allowedExtensions | 去重的 ExtensionName 集合 | CR spec | 本轮不可变;不因 allowlist 缩小卸载扩展 | | availableExtensions | 可选的实际可安装扩展集合 | 应用层从目标 PostgreSQL 查询;本轮观察,不新增 status 字段 | 未观察与已观察的空集合不同;目标变化后旧结果失效 |
| checkpoint | Pending/Validating/InitializingRegistry/Ready/Deleting | CR status.phase | 只能由领域动作变更,应用层负责持久化 | | checkpoint | Pending/Validating/InitializingRegistry/Ready/Deleting | CR status.phase | 只能由领域动作变更,应用层负责持久化 |
| observedRevision | 最近完成有结论协调的版本 | CR status.observedGeneration | 成功或已知失败时更新,单纯记录意图不更新 | | observedRevision | 最近完成有结论协调的版本 | CR status.observedGeneration | 成功或已知失败时更新,单纯记录意图不更新 |
| readiness | Unknown/Ready/NotReady,加安全失败类别和操作说明 | 由 status Ready Condition 重建,结果再映射回 Condition | 方法更新;不是第二套持久化状态 | | readiness | Unknown/Ready/NotReady,加安全失败类别和操作说明 | 由 status Ready Condition 重建,结果再映射回 Condition | 方法更新;不是第二套持久化状态 |
@@ -58,6 +58,21 @@ SHOW server_version”。具体权限探测矩阵需在 PostgreSQL 适配器规
不属于 Instance 的字段:Tenant 清单、客户端、连接池、token TTL、CA 文件句柄、 不属于 Instance 的字段:Tenant 清单、客户端、连接池、token TTL、CA 文件句柄、
Kubernetes resourceVersion。resourceVersion 留在应用层作为乐观并发保存的前提。 Kubernetes resourceVersion。resourceVersion 留在应用层作为乐观并发保存的前提。
### 扩展支持判定(2026-09-14 已确认方向)
v1alpha1 按目标 PostgreSQL 实际可安装的扩展列表判断请求,不实现管理员 allowlist。
allowlist 仅保留为后续可选策略,不接受一个看似生效、实际被忽略的策略字段;现有
CRD 的 allowedExtensions 应在对应 API 改动中移除,本次只修订文档。
应用层查询实际可用扩展并提供与本轮目标绑定的观察;Instance 只做集合判断,不
访问数据库。不沿用之前提议的字符正则,不自动改大小写或名称;SQL 适配器仍须
安全引用 identifier。可用列表不是已安装列表,也不保证权限或其他安装前提满足。
未观察/查询失败不得当作空集合或不支持;不得用旧目标的列表授权新目标的操作。
非空请求须属于已观察的可用集合,返回不支持的名称;空请求无需扩展支持判定,
但不绕过 Instance 的其他就绪要求。安装后仍需回读,不能以集合匹配代替安装验证。
列表变化不触发自动卸载;已有扩展的漂移处理留到 Tenant 用例细化。
## 3. 设计签名 ## 3. 设计签名
```text ```text
@@ -70,7 +85,8 @@ Instance.PlanRegistryPreparation(observation: CapabilityObservation)
-> AlreadyUsable | PreparationAllowed | PreparationDenied -> AlreadyUsable | PreparationAllowed | PreparationDenied
Instance.AssessRegistryResult(result: RegistryPreparationResult) -> Outcome Instance.AssessRegistryResult(result: RegistryPreparationResult) -> Outcome
Instance.AssessReadiness(observation: CapabilityObservation) -> Outcome Instance.AssessReadiness(observation: CapabilityObservation) -> Outcome
Instance.CheckExtensions(requested: ExtensionSet) -> Accepted | ExtensionsDenied Instance.CheckExtensions(requested: ExtensionSet)
-> Accepted | ExtensionsUnsupported | ExtensionSupportUnobserved
Instance.RequireProvisioningReady() -> Accepted | InstanceNotReady Instance.RequireProvisioningReady() -> Accepted | InstanceNotReady
Instance.BeginDeletion() -> Outcome Instance.BeginDeletion() -> Outcome
Instance.Snapshot() -> InstanceSnapshot Instance.Snapshot() -> InstanceSnapshot
@@ -106,7 +122,7 @@ evidence 为空。若 observedRevision 与 revision 不一致,旧 Ready 不得
| PlanRegistryPreparation | 未删除;InitializingRegistry;本轮前置观察 | 根据管理能力及 registry 现状决定无需写入、允许准备或禁止准备;返回决策,不执行迁移、不标 Ready | 访问失败、不兼容或证据不足时禁止写入,NotReady;保持阶段,更新 observedRevision | | PlanRegistryPreparation | 未删除;InitializingRegistry;本轮前置观察 | 根据管理能力及 registry 现状决定无需写入、允许准备或禁止准备;返回决策,不执行迁移、不标 Ready | 访问失败、不兼容或证据不足时禁止写入,NotReady;保持阶段,更新 observedRevision |
| AssessRegistryResult | 未删除;InitializingRegistry;准备结果或无需写入时的完整回读 | 按全部就绪条件判断回读结果;全满足才 Ready,并更新 observedRevision/version/evidence | 操作失败或回读不满足时保持 InitializingRegistry、NotReady;不得提前 Ready | | AssessRegistryResult | 未删除;InitializingRegistry;准备结果或无需写入时的完整回读 | 按全部就绪条件判断回读结果;全满足才 Ready,并更新 observedRevision/version/evidence | 操作失败或回读不满足时保持 InitializingRegistry、NotReady;不得提前 Ready |
| AssessReadiness | 未删除;Ready;本轮观察 | 配置版本不一致时仅 BeginValidation;否则根据全部观察判断是否仍满足就绪条件 | 访问失败转 Validating/NotReady;registry 缺失或需迁移时转 InitializingRegistry,保存后下一轮修复 | | AssessReadiness | 未删除;Ready;本轮观察 | 配置版本不一致时仅 BeginValidation;否则根据全部观察判断是否仍满足就绪条件 | 访问失败转 Validating/NotReady;registry 缺失或需迁移时转 InitializingRegistry,保存后下一轮修复 |
| CheckExtensions | 一组规范化 extension 名称 | 检查请求是否为当前 allowlist 子集,返回不允许的名称;无 IO、无状态修改 | ExtensionsDenied;不卸载已存在 extension | | CheckExtensions | 请求集合;本轮实际可用扩展观察 | 判断请求是否为实际可用集合的子集,返回不支持的名称;无 IO、无状态修改 | ExtensionsUnsupported 或 ExtensionSupportUnobserved;不卸载已存在扩展 |
| RequireProvisioningReady | 供 Tenant 用例使用 | 要求未删除、Ready、observedRevision 匹配,并有本次调用链的新鲜完整 evidence | 不满足即 InstanceNotReady;持久化 Ready 本身不构成授权 | | RequireProvisioningReady | 供 Tenant 用例使用 | 要求未删除、Ready、observedRevision 匹配,并有本次调用链的新鲜完整 evidence | 不满足即 InstanceNotReady;持久化 Ready 本身不构成授权 |
| BeginDeletion | deleting=true | 转 Deleting,清除供应能力,Unknown;不执行任何数据库或凭据删除 | 引用检查/finalizer 处理失败不得恢复成可供应 | | BeginDeletion | deleting=true | 转 Deleting,清除供应能力,Unknown;不执行任何数据库或凭据删除 | 引用检查/finalizer 处理失败不得恢复成可供应 |
| Snapshot | 任意合法对象状态 | 返回可安全持久化的结果值 | 不触发 IO,也不改变状态 | | Snapshot | 任意合法对象状态 | 返回可安全持久化的结果值 | 不触发 IO,也不改变状态 |
@@ -196,7 +212,7 @@ Ready --registry 需修复/保存--> InitializingRegistry
- 首版不为 Instance 删除增加跨对象锁或准入控制。并发创建的 Tenant CR 不被 - 首版不为 Instance 删除增加跨对象锁或准入控制。并发创建的 Tenant CR 不被
finalizer 拦截,但遇到删除中/不存在的 Instance 不得开始供应;不承诺取消 finalizer 拦截,但遇到删除中/不存在的 Instance 不得开始供应;不承诺取消
已在途的外部操作,也不声称引用查询与移除 finalizer 是跨对象原子事务。 已在途的外部操作,也不声称引用查询与移除 finalizer 是跨对象原子事务。
- CheckExtensions 失败不能产生任何外部写入;修改 allowlist 不会自行卸载扩展。 - CheckExtensions 失败不能授权扩展安装;可用列表变化不会自行卸载已有扩展。
- Snapshot、错误、日志和领域对象格式化不输出明文凭据或 token。 - Snapshot、错误、日志和领域对象格式化不输出明文凭据或 token。
- 领域测试只提供观察值,无需数据库、网络、context 或 IO mock;相同状态和输入 - 领域测试只提供观察值,无需数据库、网络、context 或 IO mock;相同状态和输入
得到相同决策。缺少检查项、目标不匹配和旧配置结果不得产生 Ready。 得到相同决策。缺少检查项、目标不匹配和旧配置结果不得产生 Ready。
+2 -2
View File
@@ -29,12 +29,12 @@ database OID 是诊断观察值,不充当本系统的租户身份。
### Instance:实例能力与供应策略 ### Instance:实例能力与供应策略
Instance 是候选聚合根,持有自身身份、endpoint、管理凭据引用、extension allowlist, Instance 是候选聚合根,持有自身身份、endpoint、管理凭据引用、实际可用扩展观察,
以及用于判断当前能力的观察结果。它不持有所有 Tenant 对象的集合。 以及用于判断当前能力的观察结果。它不持有所有 Tenant 对象的集合。
其行为包括: 其行为包括:
- 判断租户申请是否符合本实例的 extension 策略。 - 判断租户申请的 extension 是否在本实例实际可安装列表中;v1alpha1 暂不实现 allowlist。
- 根据管理连接、服务器信息、registry 和权限检查结果判断是否具备供应能力。 - 根据管理连接、服务器信息、registry 和权限检查结果判断是否具备供应能力。
- 判断配置变化使哪些能力观察过期,禁止以旧 generation 的 Ready 证明新配置可用。 - 判断配置变化使哪些能力观察过期,禁止以旧 generation 的 Ready 证明新配置可用。
- 在 registry 初始化完成并回读验证后,接受新的就绪结果。 - 在 registry 初始化完成并回读验证后,接受新的就绪结果。
+2 -2
View File
@@ -33,7 +33,7 @@ pg_dump --schema-only --no-owner --no-privileges \
``` ```
检查不受 v1alpha1 管理的对象:额外 roles、跨库依赖、FDW、large objects、订阅、显式 检查不受 v1alpha1 管理的对象:额外 roles、跨库依赖、FDW、large objects、订阅、显式
tablespace、owner/grant 和不在 allowlist 的 extension。无法映射为单 database + 单 login tablespace、owner/grant 和目标实例不支持的 extension。无法映射为单 database + 单 login
owner 的环境必须先人工简化,不能让 controller 猜测。 owner 的环境必须先人工简化,不能让 controller 猜测。
### 2. 创建一致性 dump ### 2. 创建一致性 dump
@@ -84,7 +84,7 @@ pg_restore --exit-on-error --no-owner --no-privileges \
``` ```
extension 应由 Tenant spec 创建。若 dump 仍包含 extension 定义,预演必须确认 restore extension 应由 Tenant spec 创建。若 dump 仍包含 extension 定义,预演必须确认 restore
行为幂等;不在 allowlist 的 extension 必须在迁移前解决。 行为幂等;目标实例不支持的 extension 必须在迁移前解决。
### 6. 验证并切换 ### 6. 验证并切换
+11 -4
View File
@@ -38,7 +38,7 @@ v1alpha1 必须实现以下目标:
1. 注册一个已经存在的外部 PostgreSQL 实例并报告连接状态。 1. 注册一个已经存在的外部 PostgreSQL 实例并报告连接状态。
2. 为一个应用租户创建独立 database 和一个同时作为 database owner 的 login role。 2. 为一个应用租户创建独立 database 和一个同时作为 database owner 的 login role。
3. 根据实例 allowlist 安装租户申请的 PostgreSQL extension。 3. 根据实例实际可安装扩展列表检查并安装租户申请的 PostgreSQL extension。
4. 首次生成高强度长期密码,并只把凭据明文写入 OpenBao KV v2。 4. 首次生成高强度长期密码,并只把凭据明文写入 OpenBao KV v2。
5. 为 Kubernetes 应用创建 ExternalSecret,由 ESO 将凭据投射到同 namespace Secret。 5. 为 Kubernetes 应用创建 ExternalSecret,由 ESO 将凭据投射到同 namespace Secret。
6. 同时输出 OpenBao API URL,使 Kubernetes 外的应用可以直接读取凭据。 6. 同时输出 OpenBao API URL,使 Kubernetes 外的应用可以直接读取凭据。
@@ -93,8 +93,11 @@ PostgreSQL 管理 role。应用或 GitOps 流程在获得 namespace RBAC 后管
- PostgreSQL host、port 和管理连接使用的 database; - PostgreSQL host、port 和管理连接使用的 database;
- PostgreSQL host address,供无法解析 DNS 的消费者使用; - PostgreSQL host address,供无法解析 DNS 的消费者使用;
- TLS mode; - TLS mode;
- controller namespace 中 PostgreSQL 管理 Secret 的名称和字段名; - controller namespace 中 PostgreSQL 管理 Secret 的名称和字段名。
- 租户允许申请的 extension 集合。
可安装的 extension 集合由应用层从目标 PostgreSQL 查询,不由管理员在 Instance
中声明。v1alpha1 不实现 allowlist;该概念保留为后续可选策略。实际可用不代表安装
权限及前置条件已满足,安装仍需执行并回读;查询失败不得被解释为扩展不支持。
实例 Ready 不代表 PostgreSQL 数据有备份或高可用,只表示 controller 当前可以安全 实例 Ready 不代表 PostgreSQL 数据有备份或高可用,只表示 controller 当前可以安全
建立管理连接、读取 server metadata、访问 controller registry 并使用所需管理能力。 建立管理连接、读取 server metadata、访问 controller registry 并使用所需管理能力。
@@ -470,7 +473,8 @@ v1alpha1 至少必须提供:
6. controller 在每个外部写入步骤后中断,重启后都能继续并得到相同最终状态。 6. controller 在每个外部写入步骤后中断,重启后都能继续并得到相同最终状态。
7. 预先存在且不属于当前 Tenant UID 的 database、role 或 OpenBao path 导致 7. 预先存在且不属于当前 Tenant UID 的 database、role 或 OpenBao path 导致
Conflict,且不修改已有资源。 Conflict,且不修改已有资源。
8. 未在 allowlist 的 extension 在任何外部写入前被拒绝。 8. 目标实例实际不支持的 extension 在供应外部写入前被拒绝;扩展列表查询失败时
按依赖故障处理,不报告为不支持。安装结果仍须回读验证。
9. status 被清空后可以从两个外部事实来源重建。 9. status 被清空后可以从两个外部事实来源重建。
10. 删除 Retain Tenant 后外部资源仍存在且不再受管;重新创建同名 Tenant 报告 10. 删除 Retain Tenant 后外部资源仍存在且不再受管;重新创建同名 Tenant 报告
Conflict。 Conflict。
@@ -523,6 +527,9 @@ v1alpha1 至少必须提供:
## 17. 批准状态 ## 17. 批准状态
2026-09-14 确认 extension 判定修订:v1alpha1 使用实例实际可安装列表,不实现管理员
allowlist;后续可按需引入策略。现有 allowedExtensions 字段尚待 API 实现移除。
2026-09-13 已确认管理连接修订:Instance 引用 controller namespace 内的管理 Secret, 2026-09-13 已确认管理连接修订:Instance 引用 controller namespace 内的管理 Secret,
管理员维护 ExternalSecret,由 ESO 同步;controller 不再从 Bao 直接读取管理凭据。 管理员维护 ExternalSecret,由 ESO 同步;controller 不再从 Bao 直接读取管理凭据。
此项是已批准行为,现有 API types 与实现尚待后续修改。 此项是已批准行为,现有 API types 与实现尚待后续修改。