Files
postgresql-tenant-operator/docs/api-reference.md
T
panxiao81 80f6d5232b
Tests / Run on Ubuntu (push) Failing after 2m11s
E2E Tests / Run on Ubuntu (push) Failing after 2m46s
Lint / Run on Ubuntu (push) Failing after 6m52s
docs: 明确凭据路径与 Secret 命名
2026-09-10 05:35:40 +00:00

175 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 都只承诺一个 `Ready` Condition;调用方不得依赖内部协调阶段。
## 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 被删除或管理员使用运维逃生流程。
## 示例
```yaml
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
```