fix: update README.md to mark RDS as Available

This commit is contained in:
Julian Sommer
2026-08-03 15:46:23 +10:00
parent 5e17169661
commit 53960e0bc4
2 changed files with 139 additions and 3 deletions
+3 -3
View File
@@ -99,10 +99,10 @@ on-premise, or on a partner Neocloud.
| **EKS** | Kubernetes | Available |
| **ECR** | Container registry | Available |
| **ECS** | Container service | Available |
| **RDS** | Databases | Q3 2026 |
| **RDS** | Databases | Available |
| **Bedrock** | AI Deployment | Q3 2026 |
Roadmap items (Q3 2026) ship under the same AWS API surface. Code written for AWS
today keeps working the moment they land.
Roadmap items ship under the same AWS API surface. Code written for AWS today keeps working the moment they land.
[Track what's shipped in the release notes](https://github.com/mulgadc/spinifex/releases).
## Core Components
+136
View File
@@ -902,6 +902,142 @@ and `aws_acm_certificate_validation` still blocks correctly by polling until
---
## RDS (PostgreSQL)
Each DB instance is one dedicated system-owned VM running the engine directly, launched from the `spinifex-rds-postgres` AMI, tagged `spinifex:managed-by=rds` and therefore hidden from the customer's EC2 API. The engine is reached over a customer-account ENI injected into a subnet of the DB subnet group, so **the endpoint is private — reachable from inside the VPC only**. `Endpoint.Address` is `{db-instance-identifier}.{account-id}.{region}.rds.{base-domain}` where northstar is configured, and the endpoint ENI's private IP where it is not; the IP is stable across VM replacement either way. Default port 5432.
- **Engine:** `postgres` 18 only. `EngineVersion` is accepted as `18` or `18.x` and rejected otherwise; there is no in-place version upgrade.
- **Instance classes:** `db.t3.micro`, `db.t3.small`, `db.t3.medium`, `db.t3.large`, `db.m5.large`, `db.m5.xlarge` — a naming facade over the platform's EC2 sizing table. Any other class is rejected at create.
- **Storage:** gp3 only, 20–65536 GiB, always encrypted with the cluster key. Grow-only, and a grow is **stop/start with downtime** — the volume cannot be resized while attached.
- **TLS:** offered, not enforced. The engine serves a per-instance certificate signed by the cluster CA (the same `ca.pem` baked into AMIs), with the ENI IP and the DNS name both in the SAN set, so `sslmode=verify-full` works by name or by address. Clients opt in; a deployment with no cluster CA starts the engine without TLS.
- **Backups:** daily COW snapshots of the data volume inside `PreferredBackupWindow`. Retention defaults to 7 days and is capped at 7; `0` disables automated backups. Point-in-time recovery is not implemented.
- **Availability:** single-AZ. An instance whose host is lost is reported `failed` with a reason in `StatusInfos`; recovery is operator-driven. Engine crashes are restarted in-guest and VM crashes on a live host are restarted by `ec2-health-restart`.
Statuses: `creating`, `available`, `modifying`, `backing-up`, `rebooting`, `stopping`, `stopped`, `starting`, `deleting`, `failed`. (`recovering` is defined in the state machine but unreachable until auto-recovery lands.)
### RDS — DB Instances
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `create-db-instance` | `--db-instance-identifier`, `--engine` (postgres), `--engine-version` (18), `--db-instance-class`, `--allocated-storage` (20–65536 GiB), `--storage-type` (gp3), `--master-username`, `--master-user-password`, `--db-name`, `--port` (1150–65535), `--db-subnet-group-name` (unnamed → the account's default VPC subnet), `--vpc-security-group-ids`, `--db-parameter-group-name`, `--backup-retention-period` (0–7, default 7), `--preferred-backup-window`, `--preferred-maintenance-window` (unnamed → assigned from the identifier), `--deletion-protection`, `--tags`, `--storage-encrypted` (true only) | `--auto-minor-version-upgrade`, `--copy-tags-to-snapshot`, Performance Insights / Enhanced Monitoring flags (all accepted, no-op); see "Rejected Parameters" for those that fail loudly | **DONE** |
| `describe-db-instances` | `--db-instance-identifier` | `--filters`, `--max-records`, `--marker` (parsed, not applied) | **DONE** |
| `modify-db-instance` | `--db-instance-identifier`, `--master-user-password`, `--allocated-storage` (grow only, stop/start), `--db-instance-class` (VM replace), `--db-parameter-group-name`, `--vpc-security-group-ids`, `--deletion-protection`, `--backup-retention-period`, `--preferred-backup-window`, `--preferred-maintenance-window`, `--apply-immediately` | `--new-db-instance-identifier`, `--engine-version`, `--db-port-number`, `--db-subnet-group-name`, `--max-allocated-storage`, `--ca-certificate-identifier` (all rejected, not ignored) | **DONE** |
| `delete-db-instance` | `--db-instance-identifier`, `--skip-final-snapshot`, `--final-db-snapshot-identifier` (exactly one is required) | `--delete-automated-backups` (no-op — automated snapshots are always purged with the instance) | **DONE** — `DeletionProtection` blocks the call; a final snapshot **retains** the data volume until that snapshot is deleted |
| `reboot-db-instance` | `--db-instance-identifier` | `--force-failover` (rejected — single-AZ) | **DONE** — applies parameters marked `pending-reboot` |
| `stop-db-instance` | `--db-instance-identifier` | `--db-snapshot-identifier` (rejected) | **DONE** — the data volume and endpoint ENI are retained |
| `start-db-instance` | `--db-instance-identifier` | — | **DONE** — fresh VM, same data volume, same ENI and IP |
### RDS — Snapshots & Restore
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `create-db-snapshot` | `--db-snapshot-identifier`, `--db-instance-identifier`, `--tags` | — | **DONE** — the agent quiesces the engine first; a failed quiesce falls back to a crash-consistent snapshot and writes a `DescribeEvents` warning |
| `describe-db-snapshots` | `--db-snapshot-identifier`, `--db-instance-identifier`, `--snapshot-type` (manual, automated) | `--filters`, `--dbi-resource-id`, `--include-shared`, `--include-public` (rejected), `--max-records`, `--marker` | **DONE** |
| `delete-db-snapshot` | `--db-snapshot-identifier` | — | **DONE** — refused while a restored volume still references the snapshot; the last snapshot released reclaims a retained data volume |
| `restore-db-instance-from-db-snapshot` | `--db-instance-identifier`, `--db-snapshot-identifier`, `--db-instance-class`, `--allocated-storage` (≥ the snapshot's), `--storage-type` (gp3), `--port`, `--db-subnet-group-name`, `--vpc-security-group-ids`, `--db-parameter-group-name`, `--deletion-protection`, `--tags`, `--engine` (must match the snapshot) | `--db-name` (rejected when it differs from the snapshot's), plus the create rejections | **DONE** — unnamed fields are inherited from the snapshot; the master password comes from the restored datadir |
`copy-db-snapshot` and cross-account snapshot sharing are not implemented (`InvalidAction`).
### RDS — Automated Backups
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `describe-db-instance-automated-backups` | `--db-instance-identifier` | `--filters`, `--dbi-resource-id`, `--db-instance-automated-backups-arn` (rejected) | **DONE** — one entry per instance with backups enabled; the individual snapshots are listed by `describe-db-snapshots --snapshot-type automated` |
`RestoreWindow` and `LatestRestorableTime` are deliberately absent: backups are discrete daily snapshots, and reporting a window would imply recovery to any instant inside it.
### RDS — Subnet Groups
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `create-db-subnet-group` | `--db-subnet-group-name`, `--db-subnet-group-description`, `--subnet-ids`, `--tags` | — | **DONE** (any subnet count — every subnet reports the single `spinifexz1` zone) |
| `describe-db-subnet-groups` | `--db-subnet-group-name` | `--filters`, `--max-records`, `--marker` | **DONE** |
| `delete-db-subnet-group` | `--db-subnet-group-name` | — | **DONE** — refused while any DB instance still names the group, including one that is deleting |
| `modify-db-subnet-group` | — | all | **NOT STARTED** (`InvalidAction`) |
### RDS — Parameter Groups
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `create-db-parameter-group` | `--db-parameter-group-name`, `--db-parameter-group-family` (postgres18), `--description`, `--tags` | — | **DONE** — the `default.postgres18` prefix is reserved |
| `describe-db-parameter-groups` | `--db-parameter-group-name` | `--filters`, `--max-records`, `--marker` | **DONE** — the implicit default group is always listed |
| `modify-db-parameter-group` | `--db-parameter-group-name`, `--parameters` (ParameterName, ParameterValue, ApplyMethod) | — | **DONE** — the whole batch is validated before anything is written; `immediate` on a static parameter is rejected, as AWS does |
| `describe-db-parameters` | `--db-parameter-group-name`, `--source` (user, engine-default) | `--filters`, `--max-records`, `--marker` | **DONE** — 51-parameter PostgreSQL 18 catalog; memory defaults are computed per instance class and reported as literals |
| `delete-db-parameter-group` | `--db-parameter-group-name` | — | **DONE** — refused for a default group and while any instance references it |
| `reset-db-parameter-group` | — | all | **NOT STARTED** (`InvalidAction`) |
Dynamic parameters apply live when the instance's `ApplyImmediately` modify carries the group; static ones are marked `pending-reboot` and applied by `reboot-db-instance`.
### RDS — Tags
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `add-tags-to-resource` | `--resource-name` (db, snapshot, subgrp, pg ARNs), `--tags` | — | **DONE** |
| `remove-tags-from-resource` | `--resource-name`, `--tag-keys` | — | **DONE** |
| `list-tags-for-resource` | `--resource-name` | `--filters` | **DONE** — `describe-db-instances` reports the same tags in `TagList` |
ARNs are AWS-exact: `arn:aws:rds:{region}:{account}:db:{id}`, `:snapshot:{id}`, `:subgrp:{name}`, `:pg:{name}`.
### RDS — Events
| Command | Implemented Flags | Missing Flags | Status |
|---------|-------------------|---------------|--------|
| `describe-events` | `--source-type` (db-instance, db-snapshot), `--source-identifier`, `--event-categories`, `--duration`, `--start-time`, `--end-time`, `--max-records` | `--marker` | **DONE** — 100-event ring per resource, 14-day retention, one-hour default window |
The event ring is the only channel some facts have: a backup that could not be quiesced, a modify applied at the maintenance window rather than immediately, a skipped or failed automated backup, and the reason a failed instance failed.
### RDS — Internal Agent Actions
`RegisterDBInstance`, `SubmitDBStateChange`, `PollDBCommands` and `GetDBBootstrapConfig` are served on the same endpoint but are **not customer API**. They are callable only by a DB VM's own IMDS instance-role credentials, bound to that instance's identity, and are refused to every customer principal by class before any policy is evaluated. They are listed here so an operator reading a gateway log recognises them; nothing outside the guest should call them.
### RDS — Rejected Parameters
Policy: a parameter whose omission would create a false safety, security or availability guarantee is rejected with `InvalidParameterValue` rather than silently dropped. Parameters that are merely inert — `AutoMinorVersionUpgrade`, `CopyTagsToSnapshot`, `DeleteAutomatedBackups`, Performance Insights and Enhanced Monitoring fields — are accepted as no-ops.
| Parameter | Why it is rejected |
|-----------|--------------------|
| `MultiAZ=true` | Single-AZ platform; a standby would not exist |
| `PubliclyAccessible=true` | The endpoint is a private VPC address |
| `StorageEncrypted=false` | Unencrypted storage is not offered |
| `EnableIAMDatabaseAuthentication` | IAM database authentication is not implemented |
| `Iops`, `StorageThroughput`, `StorageType` ≠ `gp3` | Provisioned performance classes are not implemented |
| `KmsKeyId`, `TdeCredentialArn` | Storage is encrypted with the cluster key, not a customer-managed one |
| `AvailabilityZone` | The platform exposes a single zone |
| `DBSecurityGroups` | EC2-Classic security groups — use `VpcSecurityGroupIds` |
| `DBClusterIdentifier`, `DBClusterSnapshotIdentifier` | Clustered engines are not offered |
| `EnableCloudwatchLogsExports` | Log export is not implemented |
| `EngineVersion` other than 18, `Engine` on modify | No in-place engine or version change |
| `NewDBInstanceIdentifier` | The identifier is the DNS label and the KV key |
| `DBPortNumber`, `DBSubnetGroupName` on modify | Both would move the endpoint |
| `MaxAllocatedStorage` | Storage autoscaling is not implemented |
| `ManageMasterUserPassword`, `RotateMasterUserPassword` | Secrets Manager integration is not offered |
| `CACertificateIdentifier` | The serving certificate is minted from the cluster CA |
| `Domain`, `DomainFqdn` | Active Directory domain join is not offered |
| `OptionGroupName` | Option groups are not offered |
| `CustomIamInstanceProfile` | The DB VM's instance profile is platform-owned |
| `EnableCustomerOwnedIp` | An Outposts feature |
| `ForceFailover` (reboot) | No standby to fail over to |
| `DBSnapshotIdentifier` (stop) | Snapshot-on-stop is not implemented |
### RDS — Not Yet Implemented
Recognised actions below return `OperationNotSupported`, so a client sees "not offered" rather than a typo'd action name. Everything else in the `rds` namespace returns `InvalidAction`.
| Feature | Actions | Priority | Status |
|---------|---------|----------|--------|
| MySQL engine | — (second AMI preset + `Engine` implementation) | High | **NOT STARTED** |
| Point-in-time recovery (WAL archiving to predastore) | `restore-db-instance-to-point-in-time` | High | **NOT STARTED** |
| Auto-recovery from node loss (failure is detected, not repaired) | — | High | **NOT STARTED** |
| Read replicas | `create-db-instance-read-replica`, `promote-read-replica` | Medium | **NOT STARTED** |
| Aurora / DB clusters | `create-db-cluster`, `modify-db-cluster`, `delete-db-cluster`, `describe-db-clusters`, `failover-db-cluster` | Low | **NOT STARTED** |
| Option groups | `create-option-group`, `modify-option-group`, `delete-option-group`, `describe-option-groups` | Low | **NOT STARTED** |
| Engine-version / orderable-option data sources | `describe-db-engine-versions`, `describe-orderable-db-instance-options` | Low | **NOT STARTED** (`InvalidAction`) — not required by `aws_db_instance`; pin the class and version literally in Terraform |
| Multi-AZ standby, online (no-downtime) storage grow, storage autoscaling, IAM database auth, Performance Insights, Enhanced Monitoring, log exports, per-tenant private DNS zones, enforced TLS | — | — | **NOT STARTED** |
IAM: `AmazonRDSFullAccess` and `AmazonRDSReadOnlyAccess` are available as managed policies. They grant `rds:` verb prefixes rather than `rds:*`, because `rds:*` would also appear to grant the internal agent actions the gateway reserves for a DB VM's own role.
---
## CloudWatch (Basic Monitoring)
| Command | Implemented Flags | Missing Flags | Status |