8.0 KiB
v1alpha1 API 合同
| 项目 | 内容 |
|---|---|
| 状态 | Review |
| API group | database.ddupan.top |
| version | v1alpha1 |
| 最后更新 | 2026-09-10 |
本文把已批准的系统规格映射为 CRD 字段合同。批准后,API types、生成 CRD、sample 和 测试必须与本文一致。当前代码仍是旧骨架,不能作为本页的事实来源。
通用约定
- PostgreSQL identifier 匹配
^[a-z][a-z0-9_]{0,62}$。 - 所有引用名称使用 Kubernetes DNS label/name 的相应校验。
- Tenant 的
spec.instanceRef与metadata.name长度合计不超过 241 个字符,确保派生的<instanceRef>-<metadata.name>-postgresql不超过 Kubernetes DNS subdomain 的 253 字符限制。 - 默认值由 CRD defaulting 提供;需要读取旧值的校验由 CEL 或 webhook 完成。
status由 controller 独占写入,禁止出现密码、Token、管理用户名或完整连接串。- 两个 Kind 都只承诺一个
ReadyCondition;调用方不得依赖内部协调阶段。
PostgreSQLInstance
cluster-scoped,short name 为 pginstance。
Spec
| JSON path | 类型 | 必填/默认 | 合同 |
|---|---|---|---|
spec.endpoint.host |
string | 必填 | PostgreSQL DNS 名;必须被服务端证书 DNS SAN 覆盖 |
spec.endpoint.hostaddr |
string | 必填 | 单个 IPv4/IPv6;必须被服务端证书 IP SAN 覆盖 |
spec.endpoint.port |
int32 | 5432 |
1–65535 |
spec.endpoint.database |
string | postgres |
管理连接 database;合法 PostgreSQL identifier |
spec.endpoint.sslMode |
enum | verify-full |
disable、require、verify-ca、verify-full |
spec.adminCredentialRef.path |
string | 必填 | 部署级 KV mount 内的 mount-relative path |
spec.adminCredentialRef.usernameKey |
string | username |
OpenBao record 中的键名 |
spec.adminCredentialRef.passwordKey |
string | password |
OpenBao record 中的键名 |
spec.allowedExtensions |
set[string] | 空集合 | 合法 extension 名称的 allowlist |
adminCredentialRef.path 不以 / 开头,不含空段、.、..,也不包含 KV v2 API 的
data/metadata 层。它只定位既有管理凭据;controller 不创建或修改该记录。
Instance endpoint、管理凭据引用和 allowlist 可以修改。修改后 controller 重新验证; 删除 allowlist 项目不会自动从已有 Tenant database 删除 extension。
Status
| JSON path | 类型 | 含义 |
|---|---|---|
status.observedGeneration |
int64 | 最近完成有结论协调的 generation |
status.phase |
enum | Pending、Validating、InitializingRegistry、Ready、Deleting |
status.postgresqlVersion |
string | 从 server 回读的版本,不用于客户端解析 |
status.conditions[] |
metav1.Condition |
至少包含唯一的 Ready |
print columns:Endpoint=.spec.endpoint.host、Phase、Ready、Age。
Instance Ready=True 要求管理凭据可读、TLS/认证成功、server metadata 可读、registry
可访问且权限预检成功。它不代表数据库已经备份或高可用。
PostgreSQLTenant
namespaced,short name 为 pgtenant。
Spec
| JSON path | 类型 | 必填/默认 | 合同 |
|---|---|---|---|
spec.instanceRef |
string | 必填 | cluster-scoped Instance 名称 |
spec.database |
string | metadata.name |
合法 PostgreSQL identifier |
spec.loginRole |
string | metadata.name |
database owner 兼应用 login |
spec.extensions |
set[string] | 空集合 | 必须属于 Instance allowlist;成功创建后只允许追加 |
spec.credential.secretName |
string | <instance>-<name>-postgresql |
合法的同 namespace ESO target Secret 名称 |
spec.deletionPolicy |
enum | Retain |
Retain 或 Delete |
Tenant 不声明 OpenBao mount 或 path。controller 使用部署级 mount/base path 和
namespace/name 推导稳定路径,并用 UID metadata 验证所有权。ExternalSecret 固定为
<instanceRef>-<metadata.name>-postgresql;目标 Secret 可以由用户指定,只需
满足 Kubernetes Secret 名称校验,不限制命名内容;省略时使用相同默认名。
instanceRef、database、loginRole 和 credential.secretName 在首次成功创建外部
状态后不可变。
extensions 只允许集合不变或追加;移除返回 ImmutableField,不会执行
DROP EXTENSION。deletionPolicy 在对象进入删除前可以修改;删除开始后以 finalizer
首次观察到的值为准,避免清理过程中改变授权范围。
Status
| JSON path | 类型 | 含义 |
|---|---|---|
status.observedGeneration |
int64 | 最近完成有结论协调的 generation |
status.phase |
enum | controller 状态机的权威 checkpoint |
status.databaseOID |
uint32 | 回读的 database OID,仅供诊断 |
status.credential.secretRef.name |
string | 同 namespace 目标 Secret 名称 |
status.credential.openBaoURL |
string | 完整 KV v2 API URL,不含认证信息 |
status.conditions[] |
metav1.Condition |
至少包含唯一的 Ready |
Secret reference 不重复 namespace,因为它必定与 Tenant 同 namespace。OpenBao URL 格式
为 <consumer-address>/v1/<mount>/data/<derived-path>;不得包含 Token、用户名、密码或
query credential。
Tenant phase 枚举为 Pending、Planned、CredentialCreated、RoleCreated、
DatabaseCreated、ExternalSecretCreated、CredentialProjected、Ready、Deleting。
它不包含 Failed 或 Retained;失败类型由 Condition Reason 表达。
print columns:Instance、Database、Phase、Secret、Ready、Age。完整 OpenBao URL 只在
YAML/JSON status 中输出。
两个 Kind 的 status.phase 都是 controller 状态机的权威 checkpoint。controller 用它
选择下一候选动作,但必须在动作前后核对外部事实,不能仅凭 phase 跳过幂等检查。status
丢失或领先于实际状态时必须保守重建/纠正。自动化就绪判断仍应读取 Ready Condition;
phase 用于进度展示、恢复和排障。
Condition
每种类型最多一个 Condition;更新必须保留正确的 lastTransitionTime 语义。
| Reason | Kind | 可重试性 |
|---|---|---|
Reconciling |
两者 | 正常进行中 |
Ready |
两者 | 已收敛 |
InvalidSpec |
两者 | 修改 spec 前不会恢复 |
ImmutableField |
Tenant | 恢复原值或重新迁移 |
DependencyUnavailable |
两者 | 自动重试 |
AuthenticationFailed |
Instance | 修复凭据/TLS 后重试 |
InsufficientPrivileges |
Instance | 修复管理 role 后重试 |
InstanceNotReady |
Tenant | Instance 恢复后重试 |
Conflict |
Tenant | 人工解除名称/所有权冲突 |
ProvisioningFailed |
Tenant | 按错误类别退避重试 |
CredentialProjectionFailed |
Tenant | ESO/Secret 恢复后重试 |
Ready=True 必须使用 Reason Ready。处理中为 Unknown/Reconciling;已知未满足合同为
False。Condition message 可以包含资源名和错误类别,禁止包含凭据值或完整 Secret。
删除语义
Retain不需要等待外部依赖;删除 CR 后外部记录保留原 UID 并标记 unmanaged。Delete添加 finalizer,严格按规格的所有权验证和清理顺序执行;失败保持 finalizer。- controller 不为
PostgreSQLInstance级联删除 Tenant 或外部资源;存在引用时 Instance 删除应被 finalizer 阻止,直到 Tenant 被删除或管理员使用运维逃生流程。
示例
apiVersion: database.ddupan.top/v1alpha1
kind: PostgreSQLInstance
metadata:
name: shared
spec:
endpoint:
host: postgresql.home.arpa
hostaddr: 192.0.2.10
port: 5432
database: postgres
sslMode: verify-full
adminCredentialRef:
path: infrastructure/postgresql/shared/admin
allowedExtensions: [pg_trgm]
---
apiVersion: database.ddupan.top/v1alpha1
kind: PostgreSQLTenant
metadata:
name: netbox
namespace: netbox
spec:
instanceRef: shared
database: netbox
loginRole: netbox
extensions: [pg_trgm]
credential:
secretName: shared-netbox-database-credentials
deletionPolicy: Retain